Skip to content

Commit a788468

Browse files
committed
fix: allow remote advisory to demote status set by bundled advisory
When applyCompatibilityAdvisory replaces an existing advisory, compute the base status/message by reversing the previous advisory's promotion. This enables a less-severe remote advisory (e.g. severity 'info' for 'supported') to correctly demote status that was elevated to 'error' by a bundled advisory—the core purpose of the remote override system.
1 parent 99151ed commit a788468

2 files changed

Lines changed: 67 additions & 8 deletions

File tree

apps/server/src/provider/providerCompatibility.test.ts

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import * as Effect from "effect/Effect";
44
import { HttpClient, HttpClientResponse } from "effect/unstable/http";
55

66
import {
7+
applyBundledProviderCompatibilityAdvisory,
78
clearProviderCompatibilityCacheForTests,
89
createProviderCompatibilityAdvisory,
910
enrichProviderSnapshotWithCompatibilityAdvisory,
@@ -121,4 +122,42 @@ describe("provider compatibility", () => {
121122
recommendedVersion: "0.129.0",
122123
});
123124
});
125+
126+
it("remote advisory demotes status set by bundled advisory", async () => {
127+
// The bundled document marks <0.129.0 as "broken", so 0.128.0 triggers
128+
// an error advisory on the initial snapshot.
129+
const snapshotAfterBundled = applyBundledProviderCompatibilityAdvisory({
130+
snapshot: { ...baseProvider, version: "0.128.0" },
131+
driver: codexDriver,
132+
currentVersion: "0.128.0",
133+
});
134+
expect(snapshotAfterBundled.status).toBe("error");
135+
expect(snapshotAfterBundled.compatibilityAdvisory?.severity).toBe("error");
136+
137+
// The remote document relaxes the policy: 0.128.0 is now "supported".
138+
const remoteDocument = {
139+
version: 1,
140+
policies: [
141+
{
142+
t3CodeRange: ">=0.0.0",
143+
driver: "codex",
144+
recommendedRange: ">=0.129.0",
145+
recommendedVersion: "0.129.0",
146+
ranges: [{ status: "supported", range: ">=0.128.0" }],
147+
},
148+
],
149+
};
150+
151+
const enriched = await Effect.runPromise(
152+
enrichProviderSnapshotWithCompatibilityAdvisory({
153+
...snapshotAfterBundled,
154+
instanceId: baseProvider.instanceId,
155+
driver: baseProvider.driver,
156+
}).pipe(Effect.provideService(HttpClient.HttpClient, jsonHttpClient(remoteDocument))),
157+
);
158+
159+
expect(enriched.status).toBe("ready");
160+
expect(enriched.compatibilityAdvisory?.severity).toBe("info");
161+
expect(enriched.message).toBeUndefined();
162+
});
124163
});

apps/server/src/provider/providerCompatibility.ts

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,24 @@ export const resolveRemoteProviderCompatibilityDocument = Effect.fn(
281281
return document;
282282
});
283283

284+
function baseStatusWithoutAdvisory<Snapshot extends ProviderCompatibilitySnapshot>(
285+
snapshot: Snapshot,
286+
): Snapshot["status"] {
287+
const prev = snapshot.compatibilityAdvisory;
288+
if (!prev || !snapshot.enabled) return snapshot.status;
289+
if (prev.severity === "error" && snapshot.status === "error") return "ready";
290+
if (prev.severity === "warning" && snapshot.status === "warning") return "ready";
291+
return snapshot.status;
292+
}
293+
294+
function baseMessageWithoutAdvisory<Snapshot extends ProviderCompatibilitySnapshot>(
295+
snapshot: Snapshot,
296+
): Snapshot["message"] {
297+
const prev = snapshot.compatibilityAdvisory;
298+
if (prev?.message != null && snapshot.message === prev.message) return undefined;
299+
return snapshot.message;
300+
}
301+
284302
function applyCompatibilityAdvisory<Snapshot extends ProviderCompatibilitySnapshot>(
285303
snapshot: Snapshot,
286304
compatibilityAdvisory: ServerProviderCompatibilityAdvisory | undefined,
@@ -290,25 +308,27 @@ function applyCompatibilityAdvisory<Snapshot extends ProviderCompatibilitySnapsh
290308
return snapshotWithoutAdvisory as Snapshot;
291309
}
292310

311+
const baseStatus = baseStatusWithoutAdvisory(snapshot);
312+
293313
const compatibilityMessage =
294314
compatibilityAdvisory.severity !== "info"
295315
? (compatibilityAdvisory.message ?? undefined)
296316
: undefined;
297317
const status =
298318
snapshot.enabled && compatibilityAdvisory.severity === "error"
299319
? "error"
300-
: snapshot.enabled &&
301-
compatibilityAdvisory.severity === "warning" &&
302-
snapshot.status === "ready"
320+
: snapshot.enabled && compatibilityAdvisory.severity === "warning" && baseStatus === "ready"
303321
? "warning"
304-
: snapshot.status;
322+
: baseStatus;
323+
324+
const baseMessage = baseMessageWithoutAdvisory(snapshot);
325+
const resolvedMessage = compatibilityMessage ?? baseMessage;
326+
const { message: _existingMessage, ...snapshotWithoutMessage } = snapshot;
305327

306328
return {
307-
...snapshot,
329+
...snapshotWithoutMessage,
308330
status,
309-
...(compatibilityMessage || snapshot.message
310-
? { message: compatibilityMessage ?? snapshot.message }
311-
: {}),
331+
...(resolvedMessage != null ? { message: resolvedMessage } : {}),
312332
compatibilityAdvisory,
313333
} as Snapshot;
314334
}

0 commit comments

Comments
 (0)