History

fix: preserve pre-advisory provider status during compatibility advisory removal

When applyCompatibilityAdvisory overwrites a provider's status/message with compatibility information, the original values (e.g. from a genuine probe failure) were permanently lost. When a subsequent advisory removal or replacement occurred, removeExistingCompatibilityAdvisory would reset status to 'ready' instead of the original pre-advisory status. Fix by recording preAdvisoryStatus and preAdvisoryMessage in the advisory object when it is applied. removeExistingCompatibilityAdvisory now restores these saved values instead of unconditionally resetting to 'ready'/'disabled'. For old advisories without these fields, the fallback behavior is unchanged.

Cursor Agent committed 6 months ago

Commit fad51d11 parent 5f9dc673 files · +64 −2Browse files

Unified · Split

3 files
  1. apps/server/src/provider/providerCompatibility.test.ts +46 −0
  2. apps/server/src/provider/providerCompatibility.ts +16 −2
  3. packages/contracts/src/server.ts +2 −0
apps/server/src/provider/providerCompatibility.test.ts+46−0
@@ -327,6 +327,52 @@ describe("provider compatibility", () => { · 326 unmodified lines
327 });327 });
328 });328 });
329 329
330 it.effect("preserves genuine probe errors when the remote map relaxes a bundled advisory", () => {
331 const bundledMessage =
332 "This provider harness version 0.130.0 is known to be incompatible with this T3 Code release. Use 0.129.0.";
333 const probeErrorMessage = "CLI health-check failed: process exited with code 1";
334 const remoteDocument = {
335 version: 1,
336 policies: [
337 {
338 t3CodeRange: ">=0.0.0",
339 driver: "codex",
340 recommendedRange: ">=0.130.0",
341 recommendedVersion: "0.130.0",
342 ranges: [{ status: "supported", range: ">=0.130.0" }],
343 },
344 ],
345 };
346
347 return Effect.gen(function* () {
348 const enriched = yield* enrichProviderSnapshotWithCompatibilityAdvisory({
349 ...baseProvider,
350 status: "error",
351 message: bundledMessage,
352 compatibilityAdvisory: {
353 status: "broken",
354 severity: "error",
355 currentVersion: "0.130.0",
356 message: bundledMessage,
357 recommendedRange: "<0.130.0",
358 recommendedVersion: "0.129.0",
359 ranges: [{ status: "broken", range: ">=0.130.0" }],
360 preAdvisoryStatus: "error",
361 preAdvisoryMessage: probeErrorMessage,
362 },
363 }).pipe(
364 Effect.provideService(
365 HttpClient.HttpClient,
366 jsonHttpClient(() => ({ payload: remoteDocument })),
367 ),
368 );
369
370 expect(enriched.status).toBe("error");
371 expect(enriched.message).toBe(probeErrorMessage);
372 expect(enriched.compatibilityAdvisory).toMatchObject({ status: "supported" });
373 });
374 });
375
330 it.effect("falls back to the bundled map when the remote compatibility fetch fails", () =>376 it.effect("falls back to the bundled map when the remote compatibility fetch fails", () =>
331 Effect.gen(function* () {377 Effect.gen(function* () {
332 const enriched = yield* enrichProviderSnapshotWithCompatibilityAdvisory({378 const enriched = yield* enrichProviderSnapshotWithCompatibilityAdvisory({
apps/server/src/provider/providerCompatibility.ts+16−2
@@ -325,13 +325,19 @@ function applyCompatibilityAdvisory<Snapshot extends ProviderCompatibilitySnapsh · 324 unmodified lines
325 ? "warning"325 ? "warning"
326 : baseSnapshot.status;326 : baseSnapshot.status;
327 327
328 const advisoryWithPreState: ServerProviderCompatibilityAdvisory = {
329 ...compatibilityAdvisory,
330 preAdvisoryStatus: baseSnapshot.status,
331 ...(baseSnapshot.message ? { preAdvisoryMessage: baseSnapshot.message } : {}),
332 };
333
328 return {334 return {
329 ...baseSnapshot,335 ...baseSnapshot,
330 status,336 status,
331 ...(compatibilityMessage || baseSnapshot.message337 ...(compatibilityMessage || baseSnapshot.message
332 ? { message: compatibilityMessage ?? baseSnapshot.message }338 ? { message: compatibilityMessage ?? baseSnapshot.message }
333 : {}),339 : {}),
334 compatibilityAdvisory,340 compatibilityAdvisory: advisoryWithPreState,
335 } as Snapshot;341 } as Snapshot;
336}342}
337 343
@@ -349,11 +355,19 @@ function removeExistingCompatibilityAdvisory<Snapshot extends ProviderCompatibil · 11 unmodified lines
349 : undefined;355 : undefined;
350 if (compatibilityMessage && baseSnapshot.message === compatibilityMessage) {356 if (compatibilityMessage && baseSnapshot.message === compatibilityMessage) {
351 const { message: _message, ...snapshotWithoutCompatibilityMessage } = baseSnapshot;357 const { message: _message, ...snapshotWithoutCompatibilityMessage } = baseSnapshot;
358 const restoredStatus =
359 existingCompatibilityAdvisory.preAdvisoryStatus ?? (snapshot.enabled ? "ready" : "disabled");
352 return {360 return {
353 ...snapshotWithoutCompatibilityMessage,361 ...snapshotWithoutCompatibilityMessage,
354 status: snapshot.enabled ? "ready" : "disabled",362 status: restoredStatus,
363 ...(existingCompatibilityAdvisory.preAdvisoryMessage
364 ? { message: existingCompatibilityAdvisory.preAdvisoryMessage }
365 : {}),
355 } as Snapshot;366 } as Snapshot;
356 }367 }
368 if (existingCompatibilityAdvisory.preAdvisoryStatus !== undefined) {
369 return { ...baseSnapshot, status: existingCompatibilityAdvisory.preAdvisoryStatus } as Snapshot;
370 }
357 return baseSnapshot as Snapshot;371 return baseSnapshot as Snapshot;
358}372}
359 373
packages/contracts/src/server.ts+2−0
@@ -163,6 +163,8 @@ export const ServerProviderCompatibilityAdvisory = Schema.Struct({ · 162 unmodified lines
163 ranges: Schema.Array(ServerProviderCompatibilityRange).pipe(163 ranges: Schema.Array(ServerProviderCompatibilityRange).pipe(
164 Schema.withDecodingDefault(Effect.succeed([])),164 Schema.withDecodingDefault(Effect.succeed([])),
165 ),165 ),
166 preAdvisoryStatus: Schema.optionalKey(ServerProviderState),
167 preAdvisoryMessage: Schema.optionalKey(TrimmedNonEmptyString),
166});168});
167export type ServerProviderCompatibilityAdvisory = typeof ServerProviderCompatibilityAdvisory.Type;169export type ServerProviderCompatibilityAdvisory = typeof ServerProviderCompatibilityAdvisory.Type;
168 170

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t