From 1aa6c9288015c16a2fa8efa6410e857449c1a25e Mon Sep 17 00:00:00 2001 From: Sam Kelly Date: Sun, 25 Oct 2026 19:27:53 +1300 Subject: [PATCH 1/6] fix(server): skip provider CLI update when already on latest Re-probe before spawning so a stale behind_latest advisory does not run the installer when the binary already matches latest. Unknown version and an explicit pin still install. --- .../providerMaintenanceRunner.test.ts | 135 ++++++++++++++++-- .../src/provider/providerMaintenanceRunner.ts | 51 ++++++- 2 files changed, 169 insertions(+), 17 deletions(-) diff --git a/apps/server/src/provider/providerMaintenanceRunner.test.ts b/apps/server/src/provider/providerMaintenanceRunner.test.ts index 75c592b7811a1..02e036d71f21e 100644 --- a/apps/server/src/provider/providerMaintenanceRunner.test.ts +++ b/apps/server/src/provider/providerMaintenanceRunner.test.ts @@ -153,6 +153,12 @@ function layerMockSpawner( function makeRegistry( initialProviders: ServerProvider | ReadonlyArray = baseProvider, + options?: { + // Fixtures do not rewrite the binary. refreshInstance is the pre-spawn + // probe and must keep the fixture version so skip-if-current can still + // see "behind". refreshAfterUpdate is post-update verification. + readonly installedVersionAfterUpdate?: string; + }, ) { return Effect.gen(function* () { const providersRef = yield* Ref.make>( @@ -192,7 +198,17 @@ function makeRegistry( getProviders: Ref.get(providersRef), refresh: () => Ref.get(providersRef), refreshInstance: () => Ref.get(providersRef), - refreshAfterUpdate: () => Ref.get(providersRef), + refreshAfterUpdate: () => { + if (options?.installedVersionAfterUpdate === undefined) { + return Ref.get(providersRef); + } + return Ref.updateAndGet(providersRef, (providers) => + providers.map((provider) => ({ + ...provider, + version: options.installedVersionAfterUpdate, + })), + ); + }, refreshWorkspaceSnapshot: () => Ref.get(providersRef), getProviderMaintenanceCapabilitiesForInstance: (_instanceId, provider) => Effect.succeed(lifecycleFor(provider)), @@ -268,7 +284,92 @@ describe("providerMaintenanceRunner", () => { Effect.provide( Layer.mergeAll( layerNonWindowsPlatform, - layerLatestVersionHttpClient("0.0.0"), + layerLatestVersionHttpClient("1.0.0"), + layerMockSpawner((command, args) => { + calls.push({ command, args }); + return { stdout: "updated" }; + }), + ), + ), + ); + }); + + it.effect("skips the installer when a fresh probe already matches latest", () => { + const calls: Array<{ command: string; args: ReadonlyArray }> = []; + return Effect.gen(function* () { + const { registry, updateStatesRef } = yield* makeRegistry({ + ...baseProvider, + version: "2.1.310", + }); + const updater = yield* makeTestRunner({ + ...registry, + getProviderMaintenanceCapabilitiesForInstance: () => + Effect.succeed( + makeProviderMaintenanceCapabilities({ + provider: CODEX_DRIVER, + packageName: "@anthropic-ai/claude-code", + updateExecutable: "claude", + updateArgs: ["update"], + updateLockKey: "claudeAgent-native", + }), + ), + }); + + const result = yield* updater.updateProvider(CODEX_DRIVER); + assert.deepStrictEqual(calls, []); + assert.strictEqual(result.providers[0]?.updateState?.status, "succeeded"); + assert.strictEqual(result.providers[0]?.updateState?.message, "Already up to date."); + assert.deepStrictEqual( + (yield* Ref.get(updateStatesRef)).map((state) => [state.status, state.message]), + [ + ["queued", "Waiting for another provider update to finish."], + ["running", "Checking for the latest version"], + ["succeeded", "Already up to date."], + ], + ); + }).pipe( + Effect.provide( + Layer.mergeAll( + layerNonWindowsPlatform, + layerLatestVersionHttpClient("2.1.310"), + layerMockSpawner((command, args) => { + calls.push({ command, args }); + return { stdout: "updated" }; + }), + ), + ), + ); + }); + + it.effect("still runs the installer when the fresh probe is behind latest", () => { + const calls: Array<{ command: string; args: ReadonlyArray }> = []; + return Effect.gen(function* () { + const { registry } = yield* makeRegistry({ + ...baseProvider, + version: "2.1.309", + }); + const updater = yield* makeTestRunner({ + ...registry, + getProviderMaintenanceCapabilitiesForInstance: () => + Effect.succeed( + makeProviderMaintenanceCapabilities({ + provider: CODEX_DRIVER, + packageName: "@anthropic-ai/claude-code", + updateExecutable: "claude", + updateArgs: ["update"], + updateLockKey: "claudeAgent-native", + }), + ), + }); + + const result = yield* updater.updateProvider(CODEX_DRIVER); + assert.deepStrictEqual(calls, [{ command: "claude", args: ["update"] }]); + assert.strictEqual(result.providers[0]?.updateState?.status, "unchanged"); + }).pipe( + Effect.provide( + Layer.mergeAll( + layerNonWindowsPlatform, + layerLatestVersionHttpClient("2.1.310"), layerMockSpawner((command, args) => { calls.push({ command, args }); return { stdout: "updated" }; @@ -297,7 +398,7 @@ describe("providerMaintenanceRunner", () => { Effect.provide( Layer.mergeAll( layerNonWindowsPlatform, - layerLatestVersionHttpClient("0.0.0"), + layerLatestVersionHttpClient("1.0.0"), layerMockSpawner(() => ({ stdout: "updated" })), ), ), @@ -330,7 +431,7 @@ describe("providerMaintenanceRunner", () => { Effect.provide( Layer.mergeAll( layerNonWindowsPlatform, - layerLatestVersionHttpClient("0.0.0"), + layerLatestVersionHttpClient("1.0.0"), layerMockSpawner((_command, _args, options) => { seen.push(options.env); return { stdout: "updated" }; @@ -370,7 +471,7 @@ describe("providerMaintenanceRunner", () => { Effect.provide( Layer.mergeAll( layerNonWindowsPlatform, - layerLatestVersionHttpClient("0.0.0"), + layerLatestVersionHttpClient("1.0.0"), layerMockSpawner((command, args) => { calls.push({ command, args }); return { stdout: "updated" }; @@ -432,7 +533,7 @@ describe("providerMaintenanceRunner", () => { Effect.provide( Layer.mergeAll( layerNonWindowsPlatform, - layerLatestVersionHttpClient("0.0.0"), + layerLatestVersionHttpClient("1.0.0"), layerMockSpawner((command) => { calls.push(command); return { stdout: "updated" }; @@ -460,7 +561,7 @@ describe("providerMaintenanceRunner", () => { Effect.provide( Layer.mergeAll( layerNonWindowsPlatform, - layerLatestVersionHttpClient("0.0.0"), + layerLatestVersionHttpClient("1.0.0"), layerMockSpawner(() => ({ stdout: "checking install\n", stderr: "Error: Could not detect the Codex installation method.\n\n", @@ -486,7 +587,7 @@ describe("providerMaintenanceRunner", () => { Effect.provide( Layer.mergeAll( layerNonWindowsPlatform, - layerLatestVersionHttpClient("0.0.0"), + layerLatestVersionHttpClient("1.0.0"), layerMockSpawner(() => ({ code: 1 })), ), ), @@ -551,7 +652,7 @@ describe("providerMaintenanceRunner", () => { Effect.provide( Layer.mergeAll( layerNonWindowsPlatform, - layerLatestVersionHttpClient("0.0.0"), + layerLatestVersionHttpClient("1.0.0"), layerMockSpawner(() => { startedLatch.resolve(); return { @@ -570,7 +671,9 @@ describe("providerMaintenanceRunner", () => { "releases the running-provider marker when interrupted after queuing but before the lock run starts", () => Effect.gen(function* () { - const { registry } = yield* makeRegistry(baseProvider); + const { registry } = yield* makeRegistry(baseProvider, { + installedVersionAfterUpdate: "1.0.0", + }); let blockQueuedState = true; const queuedStateWrittenLatch: { resolve: () => void } = { resolve: () => {} }; const releaseQueuedStateLatch: { resolve: () => void } = { resolve: () => {} }; @@ -611,7 +714,7 @@ describe("providerMaintenanceRunner", () => { Effect.provide( Layer.mergeAll( layerNonWindowsPlatform, - layerLatestVersionHttpClient("0.0.0"), + layerLatestVersionHttpClient("1.0.0"), layerMockSpawner(() => ({ stdout: "updated" })), ), ), @@ -625,7 +728,9 @@ describe("providerMaintenanceRunner", () => { readonly shell: boolean | string | undefined; }> = []; return Effect.gen(function* () { - const { registry } = yield* makeRegistry(baseProvider); + const { registry } = yield* makeRegistry(baseProvider, { + installedVersionAfterUpdate: "1.0.0", + }); const runner = yield* makeTestRunner(registry); const result = yield* runner.updateProvider(CODEX_DRIVER); @@ -651,7 +756,7 @@ describe("providerMaintenanceRunner", () => { Layer.succeed(SpawnExecutableResolution, (command) => command === "npm" ? "C:\\fake\\npm\\npm.cmd" : undefined, ), - layerLatestVersionHttpClient("0.0.0"), + layerLatestVersionHttpClient("1.0.0"), Layer.succeed( ChildProcessSpawner.ChildProcessSpawner, ChildProcessSpawner.make((command) => { @@ -689,7 +794,7 @@ it.effect.each([ }, { mode: "unknown", - version: "1.14.20", + version: "1.14.19", installation: { installedVersion: null }, status: "unchanged", }, @@ -785,7 +890,7 @@ it.effect( "uses fresh installer versions for post-update verification and compatibility checks", () => { const calls: string[] = []; - let latest = "1.0.0"; + let latest = "2.0.0"; const manifest: ModelManifest.ModelManifestData = { version: 1, currentModels: {}, diff --git a/apps/server/src/provider/providerMaintenanceRunner.ts b/apps/server/src/provider/providerMaintenanceRunner.ts index 9dddf23579d8a..4c2205af9a198 100644 --- a/apps/server/src/provider/providerMaintenanceRunner.ts +++ b/apps/server/src/provider/providerMaintenanceRunner.ts @@ -269,6 +269,14 @@ function isOutdatedProvider(provider: ServerProvider | undefined): boolean { return provider?.versionAdvisory?.status === "behind_latest"; } +function isAlreadyOnLatest(provider: ServerProvider | undefined): boolean { + return ( + provider !== undefined && + isStillInstalled(provider) && + provider.versionAdvisory?.status === "current" + ); +} + function isStillInstalled(provider: ServerProvider): boolean { return provider.installed; } @@ -315,12 +323,13 @@ export const make = Effect.fn("ProviderMaintenanceRunner.make")(function* () { }), }); - const verifyRefreshedProvider = ( + const verifyFromRefresh = ( + refresh: Effect.Effect>, provider: ProviderDriverKind, maintenanceCapabilities: ProviderMaintenanceCapabilities, instanceId: ProviderInstanceId, ): Effect.Effect => - providerRegistry.refreshAfterUpdate(provider, instanceId).pipe( + refresh.pipe( Effect.flatMap((providers) => { const refreshedProviders = providers.filter( (candidate) => candidate.driver === provider && candidate.instanceId === instanceId, @@ -364,6 +373,18 @@ export const make = Effect.fn("ProviderMaintenanceRunner.make")(function* () { }), ); + const verifyRefreshedProvider = ( + provider: ProviderDriverKind, + maintenanceCapabilities: ProviderMaintenanceCapabilities, + instanceId: ProviderInstanceId, + ): Effect.Effect => + verifyFromRefresh( + providerRegistry.refreshAfterUpdate(provider, instanceId), + provider, + maintenanceCapabilities, + instanceId, + ); + const updateProvider: ProviderMaintenanceRunnerShape["updateProvider"] = Effect.fn( "ProviderMaintenanceRunner.updateProvider", )(function* (target) { @@ -481,6 +502,32 @@ export const make = Effect.fn("ProviderMaintenanceRunner.make")(function* () { }), ); } + + // Re-probe before spawning. A stale behind_latest advisory must + // not run the installer when the binary is already current. + // Unknown version stays on the installer path. + if (targetVersion === undefined) { + const precheck = yield* verifyFromRefresh( + providerRegistry.refreshInstance(instanceId), + provider, + fresh, + instanceId, + ); + const alreadyCurrent = + precheck.verifiedProviders.length > 0 && + precheck.verifiedProviders.every(isAlreadyOnLatest); + if (alreadyCurrent) { + return yield* finish( + makeUpdateState({ + status: "succeeded", + startedAt, + finishedAt: yield* nowIso, + message: "Already up to date.", + }), + ); + } + } + yield* setRunningMessage(`Running ${describeCommand(command)}`); const result = yield* runMaintenanceCommand(command, setRunningMessage); const finishedAt = yield* nowIso; From 746c80fa9175b20faf846c207eb5c7fce380d10c Mon Sep 17 00:00:00 2001 From: Sam Kelly Date: Sun, 25 Oct 2026 19:45:26 +1300 Subject: [PATCH 2/6] fix(server): skip current only after a fresh enriched probe The skip test now starts from a stale behind_latest snapshot and requires refreshInstance to rewrite the live version. Skip also requires this-run enrichment so a cached current advisory cannot hide a failed latest lookup. --- .../providerMaintenanceRunner.test.ts | 61 ++++++++++++++++++- .../src/provider/providerMaintenanceRunner.ts | 11 +++- 2 files changed, 69 insertions(+), 3 deletions(-) diff --git a/apps/server/src/provider/providerMaintenanceRunner.test.ts b/apps/server/src/provider/providerMaintenanceRunner.test.ts index 02e036d71f21e..2b4c2b250d8f2 100644 --- a/apps/server/src/provider/providerMaintenanceRunner.test.ts +++ b/apps/server/src/provider/providerMaintenanceRunner.test.ts @@ -296,13 +296,31 @@ describe("providerMaintenanceRunner", () => { it.effect("skips the installer when a fresh probe already matches latest", () => { const calls: Array<{ command: string; args: ReadonlyArray }> = []; + const refreshedInstanceIds: Array = []; return Effect.gen(function* () { - const { registry, updateStatesRef } = yield* makeRegistry({ + const { registry, updateStatesRef, providersRef } = yield* makeRegistry({ ...baseProvider, - version: "2.1.310", + version: "2.1.309", + versionAdvisory: { + status: "behind_latest", + currentVersion: "2.1.309", + latestVersion: "2.1.310", + updateCommand: "claude update", + canUpdate: true, + checkedAt: "2026-04-10T00:00:00.000Z", + message: "Update available.", + }, }); const updater = yield* makeTestRunner({ ...registry, + refreshInstance: (instanceId) => { + refreshedInstanceIds.push(instanceId); + return Ref.updateAndGet(providersRef, (providers) => + providers.map((provider) => + provider.instanceId === instanceId ? { ...provider, version: "2.1.310" } : provider, + ), + ); + }, getProviderMaintenanceCapabilitiesForInstance: () => Effect.succeed( makeProviderMaintenanceCapabilities({ @@ -316,6 +334,7 @@ describe("providerMaintenanceRunner", () => { }); const result = yield* updater.updateProvider(CODEX_DRIVER); + assert.deepStrictEqual(refreshedInstanceIds, [CODEX_INSTANCE_ID]); assert.deepStrictEqual(calls, []); assert.strictEqual(result.providers[0]?.updateState?.status, "succeeded"); assert.strictEqual(result.providers[0]?.updateState?.message, "Already up to date."); @@ -341,6 +360,44 @@ describe("providerMaintenanceRunner", () => { ); }); + it.effect("still runs the installer when the installed version is unknown", () => { + const calls: Array<{ command: string; args: ReadonlyArray }> = []; + return Effect.gen(function* () { + const { registry } = yield* makeRegistry({ + ...baseProvider, + version: null, + }); + const updater = yield* makeTestRunner({ + ...registry, + getProviderMaintenanceCapabilitiesForInstance: () => + Effect.succeed( + makeProviderMaintenanceCapabilities({ + provider: CODEX_DRIVER, + packageName: "@anthropic-ai/claude-code", + updateExecutable: "claude", + updateArgs: ["update"], + updateLockKey: "claudeAgent-native", + }), + ), + }); + + const result = yield* updater.updateProvider(CODEX_DRIVER); + assert.deepStrictEqual(calls, [{ command: "claude", args: ["update"] }]); + assert.strictEqual(result.providers[0]?.updateState?.status, "succeeded"); + }).pipe( + Effect.provide( + Layer.mergeAll( + layerNonWindowsPlatform, + layerLatestVersionHttpClient("2.1.310"), + layerMockSpawner((command, args) => { + calls.push({ command, args }); + return { stdout: "updated" }; + }), + ), + ), + ); + }); + it.effect("still runs the installer when the fresh probe is behind latest", () => { const calls: Array<{ command: string; args: ReadonlyArray }> = []; return Effect.gen(function* () { diff --git a/apps/server/src/provider/providerMaintenanceRunner.ts b/apps/server/src/provider/providerMaintenanceRunner.ts index 4c2205af9a198..8503715c5fb03 100644 --- a/apps/server/src/provider/providerMaintenanceRunner.ts +++ b/apps/server/src/provider/providerMaintenanceRunner.ts @@ -83,6 +83,7 @@ class ProviderMaintenanceCommandError extends Data.TaggedError("ProviderMaintena interface VerifiedProviderRefresh { readonly providers: ReadonlyArray; readonly verifiedProviders: ReadonlyArray; + readonly advisoryEnriched: boolean; } const nowIso = Effect.map(DateTime.now, DateTime.formatIso); @@ -328,6 +329,7 @@ export const make = Effect.fn("ProviderMaintenanceRunner.make")(function* () { provider: ProviderDriverKind, maintenanceCapabilities: ProviderMaintenanceCapabilities, instanceId: ProviderInstanceId, + phase: "pre-update" | "post-update", ): Effect.Effect => refresh.pipe( Effect.flatMap((providers) => { @@ -338,6 +340,7 @@ export const make = Effect.fn("ProviderMaintenanceRunner.make")(function* () { return Effect.succeed({ providers, verifiedProviders: [], + advisoryEnriched: true, }); } return Effect.forEach( @@ -357,15 +360,18 @@ export const make = Effect.fn("ProviderMaintenanceRunner.make")(function* () { Effect.map((verifiedProviders): VerifiedProviderRefresh => ({ providers, verifiedProviders, + advisoryEnriched: true, })), Effect.catchCause((cause) => - Effect.logWarning("Provider post-update version verification failed", { + Effect.logWarning("Provider version verification failed", { provider, + phase, cause: Cause.pretty(cause), }).pipe( Effect.as({ providers, verifiedProviders: refreshedProviders, + advisoryEnriched: false, }), ), ), @@ -383,6 +389,7 @@ export const make = Effect.fn("ProviderMaintenanceRunner.make")(function* () { provider, maintenanceCapabilities, instanceId, + "post-update", ); const updateProvider: ProviderMaintenanceRunnerShape["updateProvider"] = Effect.fn( @@ -512,8 +519,10 @@ export const make = Effect.fn("ProviderMaintenanceRunner.make")(function* () { provider, fresh, instanceId, + "pre-update", ); const alreadyCurrent = + precheck.advisoryEnriched && precheck.verifiedProviders.length > 0 && precheck.verifiedProviders.every(isAlreadyOnLatest); if (alreadyCurrent) { From dddb610fb5ee2b900bd356882e114d008e8f970b Mon Sep 17 00:00:00 2001 From: Sam Kelly Date: Sun, 25 Oct 2026 20:02:02 +1300 Subject: [PATCH 3/6] fix(server): skip current only after a live version probe refreshInstance can return the cached row on unknown ids or probe failure. Skip now requires checkedAt to change, so a cached current advisory cannot hide an unverified binary. --- .../providerMaintenanceRunner.test.ts | 46 ++++++++++++++++++- .../src/provider/providerMaintenanceRunner.ts | 11 ++++- 2 files changed, 54 insertions(+), 3 deletions(-) diff --git a/apps/server/src/provider/providerMaintenanceRunner.test.ts b/apps/server/src/provider/providerMaintenanceRunner.test.ts index 2b4c2b250d8f2..416281e87cce7 100644 --- a/apps/server/src/provider/providerMaintenanceRunner.test.ts +++ b/apps/server/src/provider/providerMaintenanceRunner.test.ts @@ -317,7 +317,13 @@ describe("providerMaintenanceRunner", () => { refreshedInstanceIds.push(instanceId); return Ref.updateAndGet(providersRef, (providers) => providers.map((provider) => - provider.instanceId === instanceId ? { ...provider, version: "2.1.310" } : provider, + provider.instanceId === instanceId + ? { + ...provider, + version: "2.1.310", + checkedAt: "2026-04-10T00:00:01.000Z", + } + : provider, ), ); }, @@ -360,6 +366,44 @@ describe("providerMaintenanceRunner", () => { ); }); + it.effect("still runs the installer when refreshInstance returns the cached snapshot", () => { + const calls: Array<{ command: string; args: ReadonlyArray }> = []; + return Effect.gen(function* () { + const { registry } = yield* makeRegistry({ + ...baseProvider, + version: "2.1.310", + }); + const updater = yield* makeTestRunner({ + ...registry, + getProviderMaintenanceCapabilitiesForInstance: () => + Effect.succeed( + makeProviderMaintenanceCapabilities({ + provider: CODEX_DRIVER, + packageName: "@anthropic-ai/claude-code", + updateExecutable: "claude", + updateArgs: ["update"], + updateLockKey: "claudeAgent-native", + }), + ), + }); + + const result = yield* updater.updateProvider(CODEX_DRIVER); + assert.deepStrictEqual(calls, [{ command: "claude", args: ["update"] }]); + assert.strictEqual(result.providers[0]?.updateState?.status, "succeeded"); + }).pipe( + Effect.provide( + Layer.mergeAll( + layerNonWindowsPlatform, + layerLatestVersionHttpClient("2.1.310"), + layerMockSpawner((command, args) => { + calls.push({ command, args }); + return { stdout: "updated" }; + }), + ), + ), + ); + }); + it.effect("still runs the installer when the installed version is unknown", () => { const calls: Array<{ command: string; args: ReadonlyArray }> = []; return Effect.gen(function* () { diff --git a/apps/server/src/provider/providerMaintenanceRunner.ts b/apps/server/src/provider/providerMaintenanceRunner.ts index 8503715c5fb03..4070d03dad071 100644 --- a/apps/server/src/provider/providerMaintenanceRunner.ts +++ b/apps/server/src/provider/providerMaintenanceRunner.ts @@ -514,6 +514,7 @@ export const make = Effect.fn("ProviderMaintenanceRunner.make")(function* () { // not run the installer when the binary is already current. // Unknown version stays on the installer path. if (targetVersion === undefined) { + const providersBeforeProbe = yield* providerRegistry.getProviders; const precheck = yield* verifyFromRefresh( providerRegistry.refreshInstance(instanceId), provider, @@ -521,10 +522,16 @@ export const make = Effect.fn("ProviderMaintenanceRunner.make")(function* () { instanceId, "pre-update", ); + const liveProbedProviders = precheck.verifiedProviders.filter((verified) => { + const prior = providersBeforeProbe.find( + (candidate) => candidate.instanceId === verified.instanceId, + ); + return prior !== undefined && prior.checkedAt !== verified.checkedAt; + }); const alreadyCurrent = precheck.advisoryEnriched && - precheck.verifiedProviders.length > 0 && - precheck.verifiedProviders.every(isAlreadyOnLatest); + liveProbedProviders.length > 0 && + liveProbedProviders.every(isAlreadyOnLatest); if (alreadyCurrent) { return yield* finish( makeUpdateState({ From 0ab8a1101b4fc31ea94547db5487d9322d215df2 Mon Sep 17 00:00:00 2001 From: Sam Kelly Date: Mon, 26 Oct 2026 08:13:32 +1300 Subject: [PATCH 4/6] test(server): skip current only after this-run enrichment A live restamped probe with a cached current advisory still runs the installer when the latest-version lookup fails on the precheck enrich. --- .../providerMaintenanceRunner.test.ts | 77 ++++++++++++++++++- 1 file changed, 76 insertions(+), 1 deletion(-) diff --git a/apps/server/src/provider/providerMaintenanceRunner.test.ts b/apps/server/src/provider/providerMaintenanceRunner.test.ts index 416281e87cce7..7bc08996ca8e4 100644 --- a/apps/server/src/provider/providerMaintenanceRunner.test.ts +++ b/apps/server/src/provider/providerMaintenanceRunner.test.ts @@ -235,6 +235,7 @@ const makeTestRunner = ( ranges: [], })), }, + latestVersions: Layer.Layer = ProviderLatestVersions.layer, ) => Effect.service(ProviderMaintenanceRunner.ProviderMaintenanceRunner).pipe( Effect.provide( @@ -248,13 +249,28 @@ const makeTestRunner = ( forceRefresh: Effect.succeed(manifest), refreshInBackground: Effect.void, }), - ProviderLatestVersions.layer, + latestVersions, ), ), ), ), ); +function layerFailingLatestLookupAfterFirst() { + let lookups = 0; + return Layer.succeed(ProviderLatestVersions.ProviderLatestVersions, { + cached: (_key, lookup) => + Effect.gen(function* () { + lookups += 1; + if (lookups > 1) { + return yield* Effect.fail(new Error("latest lookup failed")); + } + return yield* lookup; + }), + invalidate: () => Effect.void, + }); +} + describe("providerMaintenanceRunner", () => { it.effect("runs the allowlisted provider update command and records success", () => { const calls: Array<{ command: string; args: ReadonlyArray }> = []; @@ -404,6 +420,65 @@ describe("providerMaintenanceRunner", () => { ); }); + it.effect("still runs the installer when precheck advisory enrichment fails", () => { + const calls: Array<{ command: string; args: ReadonlyArray }> = []; + return Effect.gen(function* () { + const { registry, providersRef } = yield* makeRegistry({ + ...baseProvider, + version: "2.1.310", + versionAdvisory: { + status: "current", + currentVersion: "2.1.310", + latestVersion: "2.1.310", + updateCommand: "claude update", + canUpdate: true, + checkedAt: "2026-04-10T00:00:00.000Z", + message: null, + }, + }); + const updater = yield* makeTestRunner( + { + ...registry, + refreshInstance: (instanceId) => + Ref.updateAndGet(providersRef, (providers) => + providers.map((provider) => + provider.instanceId === instanceId + ? { ...provider, checkedAt: "2026-04-10T00:00:01.000Z" } + : provider, + ), + ), + getProviderMaintenanceCapabilitiesForInstance: () => + Effect.succeed( + makeProviderMaintenanceCapabilities({ + provider: CODEX_DRIVER, + packageName: "@anthropic-ai/claude-code", + updateExecutable: "claude", + updateArgs: ["update"], + updateLockKey: "claudeAgent-native", + }), + ), + }, + undefined, + layerFailingLatestLookupAfterFirst(), + ); + + const result = yield* updater.updateProvider(CODEX_DRIVER); + assert.deepStrictEqual(calls, [{ command: "claude", args: ["update"] }]); + assert.strictEqual(result.providers[0]?.updateState?.status, "succeeded"); + }).pipe( + Effect.provide( + Layer.mergeAll( + layerNonWindowsPlatform, + layerLatestVersionHttpClient("2.1.310"), + layerMockSpawner((command, args) => { + calls.push({ command, args }); + return { stdout: "updated" }; + }), + ), + ), + ); + }); + it.effect("still runs the installer when the installed version is unknown", () => { const calls: Array<{ command: string; args: ReadonlyArray }> = []; return Effect.gen(function* () { From 0b02eca83dfd12766192dfd7580283edb509c425 Mon Sep 17 00:00:00 2001 From: maria-rcks Date: Mon, 26 Oct 2026 12:29:43 +0000 Subject: [PATCH 5/6] fix(server): simplify fresh provider update precheck --- .../providerMaintenanceRunner.test.ts | 18 +++++-- .../src/provider/providerMaintenanceRunner.ts | 48 ++++++------------- 2 files changed, 28 insertions(+), 38 deletions(-) diff --git a/apps/server/src/provider/providerMaintenanceRunner.test.ts b/apps/server/src/provider/providerMaintenanceRunner.test.ts index 7bc08996ca8e4..ded42a89f977d 100644 --- a/apps/server/src/provider/providerMaintenanceRunner.test.ts +++ b/apps/server/src/provider/providerMaintenanceRunner.test.ts @@ -154,9 +154,6 @@ function layerMockSpawner( function makeRegistry( initialProviders: ServerProvider | ReadonlyArray = baseProvider, options?: { - // Fixtures do not rewrite the binary. refreshInstance is the pre-spawn - // probe and must keep the fixture version so skip-if-current can still - // see "behind". refreshAfterUpdate is post-update verification. readonly installedVersionAfterUpdate?: string; }, ) { @@ -482,12 +479,19 @@ describe("providerMaintenanceRunner", () => { it.effect("still runs the installer when the installed version is unknown", () => { const calls: Array<{ command: string; args: ReadonlyArray }> = []; return Effect.gen(function* () { - const { registry } = yield* makeRegistry({ + const { registry, providersRef } = yield* makeRegistry({ ...baseProvider, version: null, }); const updater = yield* makeTestRunner({ ...registry, + refreshInstance: () => + Ref.updateAndGet(providersRef, (providers) => + providers.map((provider) => ({ + ...provider, + checkedAt: "2026-04-10T00:00:01.000Z", + })), + ), getProviderMaintenanceCapabilitiesForInstance: () => Effect.succeed( makeProviderMaintenanceCapabilities({ @@ -968,6 +972,12 @@ it.effect.each([ installation: { installedVersion: "1.14.20" }, status: "succeeded", }, + { + mode: "targeted-current", + version: "1.14.20", + installation: { installedVersion: "1.14.20" }, + status: "succeeded", + }, { mode: "unknown", version: "1.14.19", diff --git a/apps/server/src/provider/providerMaintenanceRunner.ts b/apps/server/src/provider/providerMaintenanceRunner.ts index 4070d03dad071..4aafbcf109b06 100644 --- a/apps/server/src/provider/providerMaintenanceRunner.ts +++ b/apps/server/src/provider/providerMaintenanceRunner.ts @@ -270,14 +270,6 @@ function isOutdatedProvider(provider: ServerProvider | undefined): boolean { return provider?.versionAdvisory?.status === "behind_latest"; } -function isAlreadyOnLatest(provider: ServerProvider | undefined): boolean { - return ( - provider !== undefined && - isStillInstalled(provider) && - provider.versionAdvisory?.status === "current" - ); -} - function isStillInstalled(provider: ServerProvider): boolean { return provider.installed; } @@ -379,19 +371,6 @@ export const make = Effect.fn("ProviderMaintenanceRunner.make")(function* () { }), ); - const verifyRefreshedProvider = ( - provider: ProviderDriverKind, - maintenanceCapabilities: ProviderMaintenanceCapabilities, - instanceId: ProviderInstanceId, - ): Effect.Effect => - verifyFromRefresh( - providerRegistry.refreshAfterUpdate(provider, instanceId), - provider, - maintenanceCapabilities, - instanceId, - "post-update", - ); - const updateProvider: ProviderMaintenanceRunnerShape["updateProvider"] = Effect.fn( "ProviderMaintenanceRunner.updateProvider", )(function* (target) { @@ -510,11 +489,10 @@ export const make = Effect.fn("ProviderMaintenanceRunner.make")(function* () { ); } - // Re-probe before spawning. A stale behind_latest advisory must - // not run the installer when the binary is already current. - // Unknown version stays on the installer path. if (targetVersion === undefined) { - const providersBeforeProbe = yield* providerRegistry.getProviders; + const prior = (yield* providerRegistry.getProviders).find( + (candidate) => candidate.instanceId === instanceId, + ); const precheck = yield* verifyFromRefresh( providerRegistry.refreshInstance(instanceId), provider, @@ -522,16 +500,16 @@ export const make = Effect.fn("ProviderMaintenanceRunner.make")(function* () { instanceId, "pre-update", ); - const liveProbedProviders = precheck.verifiedProviders.filter((verified) => { - const prior = providersBeforeProbe.find( - (candidate) => candidate.instanceId === verified.instanceId, - ); - return prior !== undefined && prior.checkedAt !== verified.checkedAt; - }); const alreadyCurrent = precheck.advisoryEnriched && - liveProbedProviders.length > 0 && - liveProbedProviders.every(isAlreadyOnLatest); + prior !== undefined && + precheck.verifiedProviders.length > 0 && + precheck.verifiedProviders.every( + (verified) => + verified.checkedAt !== prior.checkedAt && + verified.installed && + verified.versionAdvisory?.status === "current", + ); if (alreadyCurrent) { return yield* finish( makeUpdateState({ @@ -575,10 +553,12 @@ export const make = Effect.fn("ProviderMaintenanceRunner.make")(function* () { yield* invalidateLatestProviderVersion(verified).pipe( Effect.provideService(ProviderLatestVersions.ProviderLatestVersions, latestVersions), ); - const { verifiedProviders } = yield* verifyRefreshedProvider( + const { verifiedProviders } = yield* verifyFromRefresh( + providerRegistry.refreshAfterUpdate(provider, instanceId), provider, verified, instanceId, + "post-update", ); // Cursor about probes can fail transiently; missing versions alone do not imply failure. const couldNotVerify = From 30ed8880d2336610a79e3987cc4e6760fd113f86 Mon Sep 17 00:00:00 2001 From: maria-rcks Date: Mon, 26 Oct 2026 12:35:02 +0000 Subject: [PATCH 6/6] fix(server): keep failed provider probes on the installer path --- .../providerMaintenanceRunner.test.ts | 112 ++++-------------- .../src/provider/providerMaintenanceRunner.ts | 1 + 2 files changed, 25 insertions(+), 88 deletions(-) diff --git a/apps/server/src/provider/providerMaintenanceRunner.test.ts b/apps/server/src/provider/providerMaintenanceRunner.test.ts index ded42a89f977d..a349ffa567cd0 100644 --- a/apps/server/src/provider/providerMaintenanceRunner.test.ts +++ b/apps/server/src/provider/providerMaintenanceRunner.test.ts @@ -196,13 +196,14 @@ function makeRegistry( refresh: () => Ref.get(providersRef), refreshInstance: () => Ref.get(providersRef), refreshAfterUpdate: () => { - if (options?.installedVersionAfterUpdate === undefined) { + const version = options?.installedVersionAfterUpdate; + if (version === undefined) { return Ref.get(providersRef); } return Ref.updateAndGet(providersRef, (providers) => providers.map((provider) => ({ ...provider, - version: options.installedVersionAfterUpdate, + version, })), ); }, @@ -260,7 +261,7 @@ function layerFailingLatestLookupAfterFirst() { Effect.gen(function* () { lookups += 1; if (lookups > 1) { - return yield* Effect.fail(new Error("latest lookup failed")); + return yield* Effect.die("latest lookup failed"); } return yield* lookup; }), @@ -307,7 +308,19 @@ describe("providerMaintenanceRunner", () => { ); }); - it.effect("skips the installer when a fresh probe already matches latest", () => { + it.effect.each([ + { name: "current", probe: {}, skip: true }, + { name: "failed", probe: { status: "error" }, skip: false }, + { name: "behind", probe: { version: "2.1.309" }, skip: false }, + { name: "unknown", probe: { version: null }, skip: false }, + { + name: "installed current", + probe: { version: "2.1.309", installedVersion: "2.1.310" }, + skip: true, + }, + { name: "installed behind", probe: { installedVersion: "2.1.309" }, skip: false }, + { name: "installed unknown", probe: { installedVersion: null }, skip: false }, + ] as const)("only skips a successful fresh current probe: $name", ({ probe, skip }) => { const calls: Array<{ command: string; args: ReadonlyArray }> = []; const refreshedInstanceIds: Array = []; return Effect.gen(function* () { @@ -334,6 +347,7 @@ describe("providerMaintenanceRunner", () => { ? { ...provider, version: "2.1.310", + ...probe, checkedAt: "2026-04-10T00:00:01.000Z", } : provider, @@ -354,7 +368,8 @@ describe("providerMaintenanceRunner", () => { const result = yield* updater.updateProvider(CODEX_DRIVER); assert.deepStrictEqual(refreshedInstanceIds, [CODEX_INSTANCE_ID]); - assert.deepStrictEqual(calls, []); + assert.deepStrictEqual(calls, skip ? [] : [{ command: "claude", args: ["update"] }]); + if (!skip) return; assert.strictEqual(result.providers[0]?.updateState?.status, "succeeded"); assert.strictEqual(result.providers[0]?.updateState?.message, "Already up to date."); assert.deepStrictEqual( @@ -476,89 +491,6 @@ describe("providerMaintenanceRunner", () => { ); }); - it.effect("still runs the installer when the installed version is unknown", () => { - const calls: Array<{ command: string; args: ReadonlyArray }> = []; - return Effect.gen(function* () { - const { registry, providersRef } = yield* makeRegistry({ - ...baseProvider, - version: null, - }); - const updater = yield* makeTestRunner({ - ...registry, - refreshInstance: () => - Ref.updateAndGet(providersRef, (providers) => - providers.map((provider) => ({ - ...provider, - checkedAt: "2026-04-10T00:00:01.000Z", - })), - ), - getProviderMaintenanceCapabilitiesForInstance: () => - Effect.succeed( - makeProviderMaintenanceCapabilities({ - provider: CODEX_DRIVER, - packageName: "@anthropic-ai/claude-code", - updateExecutable: "claude", - updateArgs: ["update"], - updateLockKey: "claudeAgent-native", - }), - ), - }); - - const result = yield* updater.updateProvider(CODEX_DRIVER); - assert.deepStrictEqual(calls, [{ command: "claude", args: ["update"] }]); - assert.strictEqual(result.providers[0]?.updateState?.status, "succeeded"); - }).pipe( - Effect.provide( - Layer.mergeAll( - layerNonWindowsPlatform, - layerLatestVersionHttpClient("2.1.310"), - layerMockSpawner((command, args) => { - calls.push({ command, args }); - return { stdout: "updated" }; - }), - ), - ), - ); - }); - - it.effect("still runs the installer when the fresh probe is behind latest", () => { - const calls: Array<{ command: string; args: ReadonlyArray }> = []; - return Effect.gen(function* () { - const { registry } = yield* makeRegistry({ - ...baseProvider, - version: "2.1.309", - }); - const updater = yield* makeTestRunner({ - ...registry, - getProviderMaintenanceCapabilitiesForInstance: () => - Effect.succeed( - makeProviderMaintenanceCapabilities({ - provider: CODEX_DRIVER, - packageName: "@anthropic-ai/claude-code", - updateExecutable: "claude", - updateArgs: ["update"], - updateLockKey: "claudeAgent-native", - }), - ), - }); - - const result = yield* updater.updateProvider(CODEX_DRIVER); - assert.deepStrictEqual(calls, [{ command: "claude", args: ["update"] }]); - assert.strictEqual(result.providers[0]?.updateState?.status, "unchanged"); - }).pipe( - Effect.provide( - Layer.mergeAll( - layerNonWindowsPlatform, - layerLatestVersionHttpClient("2.1.310"), - layerMockSpawner((command, args) => { - calls.push({ command, args }); - return { stdout: "updated" }; - }), - ), - ), - ); - }); - it.effect( "keeps a successful update when the binary is present but its version is unreadable", () => { @@ -1013,6 +945,10 @@ it.effect.each([ const updater = yield* makeTestRunner( { ...registry, + refreshInstance: () => + Ref.updateAndGet(providersRef, (providers) => + providers.map((provider) => ({ ...provider, checkedAt: "2026-04-10T00:00:01.000Z" })), + ), getProviderMaintenanceCapabilitiesForInstance: () => Effect.succeed(pinnedCapabilities), refreshAfterUpdate: () => Effect.gen(function* () { diff --git a/apps/server/src/provider/providerMaintenanceRunner.ts b/apps/server/src/provider/providerMaintenanceRunner.ts index 4aafbcf109b06..1450a837f75e5 100644 --- a/apps/server/src/provider/providerMaintenanceRunner.ts +++ b/apps/server/src/provider/providerMaintenanceRunner.ts @@ -508,6 +508,7 @@ export const make = Effect.fn("ProviderMaintenanceRunner.make")(function* () { (verified) => verified.checkedAt !== prior.checkedAt && verified.installed && + verified.status !== "error" && verified.versionAdvisory?.status === "current", ); if (alreadyCurrent) {