From dcf4ce695c0ca5934fdd7dc631ffa8edf69b3981 Mon Sep 17 00:00:00 2001 From: Julius Marminge Date: Wed, 11 Nov 2026 11:15:54 +0000 Subject: [PATCH 1/2] test(provider-core): failing repro for #34295, auth failure not rechecked on settings apply AI-assisted; verified with the tests and tools listed above. --- .../src/server/managedProvider.test.ts | 60 +++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/packages/provider-core/src/server/managedProvider.test.ts b/packages/provider-core/src/server/managedProvider.test.ts index 5295232d3b14c..c9e5a6e01390d 100644 --- a/packages/provider-core/src/server/managedProvider.test.ts +++ b/packages/provider-core/src/server/managedProvider.test.ts @@ -472,6 +472,66 @@ describe("makeManagedServerProvider", () => { ).pipe(Effect.provide(layerAlwaysRunTest)), ); + it.effect( + "rechecks an auth failure when settings are applied, even if this provider's did not change", + () => + Effect.scoped( + Effect.gen(function* () { + const settingsChanges = yield* PubSub.unbounded< + TestSettings & { readonly revision: number } + >(); + const checkCalls = yield* Ref.make(0); + const seenRevisions: Array = []; + const releaseInitialCheck = yield* Deferred.make(); + const markerApplied = yield* Deferred.make(); + const unauthenticatedSnapshot: ServerProvider = { + ...refreshedSnapshot, + status: "error", + auth: { status: "unauthenticated" }, + message: "Codex CLI is not authenticated. Run `codex login` and try again.", + }; + const provider = yield* makeManagedServerProvider< + TestSettings & { readonly revision: number } + >({ + resolveMaintenance: () => Effect.succeed(maintenanceCapabilities), + getSettings: Effect.succeed({ enabled: true, revision: 0 }), + streamSettings: Stream.fromPubSub(settingsChanges), + haveSettingsChanged: (previous, next) => { + seenRevisions.push(next.revision); + if (next.revision === 2) Deferred.doneUnsafe(markerApplied, Effect.void); + return previous.enabled !== next.enabled; + }, + initialSnapshot: () => Effect.succeed(initialSnapshot), + // The credential is fixed (e.g. `codex login`) after the first, failed probe. + checkProvider: Deferred.await(releaseInitialCheck).pipe( + Effect.andThen(Ref.updateAndGet(checkCalls, (count) => count + 1)), + Effect.map((count) => (count === 1 ? unauthenticatedSnapshot : refreshedSnapshot)), + ), + refreshInterval: "1 hour", + }); + const initialProbe = yield* provider.streamChanges.pipe( + Stream.take(1), + Stream.runDrain, + Effect.forkChild, + ); + yield* Effect.yieldNow; + yield* Deferred.succeed(releaseInitialCheck, undefined); + yield* Fiber.join(initialProbe); + assert.deepStrictEqual(yield* provider.getSnapshot, unauthenticatedSnapshot); + + // A settings apply that does not touch this provider's settings, then a marker: + // the consumer is sequential, so the first apply has finished once the marker is seen. + yield* PubSub.publish(settingsChanges, { enabled: true, revision: 1 }); + yield* PubSub.publish(settingsChanges, { enabled: true, revision: 2 }); + yield* Deferred.await(markerApplied); + + assert.deepStrictEqual(seenRevisions, [1, 2]); + assert.isAtLeast(yield* Ref.get(checkCalls), 2); + assert.deepStrictEqual((yield* provider.getSnapshot).auth, refreshedSnapshot.auth); + }), + ).pipe(Effect.provide(layerAlwaysRunTest)), + ); + it.effect("streams supplemental snapshot updates after the base provider check completes", () => Effect.scoped( Effect.gen(function* () { From c2f18912842f727875af198ac366b3e3bc846f2f Mon Sep 17 00:00:00 2001 From: Julius Marminge Date: Wed, 11 Nov 2026 19:22:06 +0000 Subject: [PATCH 2/2] fix(provider-core): recheck a failed sign-in when settings are applied A provider whose last probe was unauthenticated kept that error after the credential was fixed, because an unrelated settings apply returned the cached snapshot. Treat any settings apply as a recheck while the snapshot is unauthenticated. Fixes pingdotgg/t3code#34295 AI-assisted; verified with the tests and tools listed above. --- packages/provider-core/src/server/managedProvider.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/packages/provider-core/src/server/managedProvider.ts b/packages/provider-core/src/server/managedProvider.ts index 39332d013e2ce..a766c95d62535 100644 --- a/packages/provider-core/src/server/managedProvider.ts +++ b/packages/provider-core/src/server/managedProvider.ts @@ -143,9 +143,15 @@ export const makeManagedServerProvider = Effect.fn("makeManagedServerProvider")( nextSettings: Settings, options?: { readonly forceRefresh?: boolean }, ) { - const forceRefresh = options?.forceRefresh === true; const previousSettings = yield* Ref.get(settingsRef); - if (!forceRefresh && !input.haveSettingsChanged(previousSettings, nextSettings)) { + const settingsChanged = + options?.forceRefresh === true || input.haveSettingsChanged(previousSettings, nextSettings); + // Nothing watches credential files, so any settings apply rechecks a failed sign-in. + const { snapshot: currentSnapshot } = yield* Ref.get(snapshotStateRef); + const forceRefresh = + options?.forceRefresh === true || + (currentSnapshot.enabled && currentSnapshot.auth.status === "unauthenticated"); + if (!forceRefresh && !settingsChanged) { yield* Ref.set(settingsRef, nextSettings); return yield* Ref.get(snapshotStateRef).pipe(Effect.map((state) => state.snapshot)); }