Skip to content

[codex] Add remote provider compatibility map#2642

Open
juliusmarminge wants to merge 12 commits into
mainfrom
t3code/290bc166
Open

[codex] Add remote provider compatibility map#2642
juliusmarminge wants to merge 12 commits into
mainfrom
t3code/290bc166

Conversation

@juliusmarminge
Copy link
Copy Markdown
Member

@juliusmarminge juliusmarminge commented May 11, 2026

What changed

  • Added provider compatibility advisory contract fields, including supported/broken/graceful status, severity, recommended range, and recommended version.
  • Added a bundled fallback compatibility document and a checked-in provider-compatibility.v1.json intended for GitHub raw hosting.
  • Added best-effort remote compatibility map enrichment with timeout, TTL cache, schema validation, and bundled fallback behavior.
  • Wired compatibility enrichment into provider snapshot background enrichment for Codex, Claude, Cursor, and OpenCode.
  • Surfaced provider compatibility warnings in the provider settings UI with a warning/error popover.

Why

Released T3 Code builds need compatibility policy updates without requiring users to update the app. This lets maintainers update a static GitHub-hosted map to mark provider harness versions as supported, graceful, unsupported, or broken for specific T3 Code release ranges.

Validation

  • bun fmt
  • bun lint (passes with existing unrelated warnings)
  • bun typecheck
  • bunx vitest run packages/contracts/src/server.test.ts apps/server/src/provider/providerSnapshot.test.ts apps/server/src/provider/providerCompatibility.test.ts apps/web/src/components/settings/providerStatus.test.ts

Note

Add remote provider compatibility map with advisory enrichment and UI update actions for Codex

  • Introduces provider-compatibility.v1.json as a versioned compatibility policy document covering Codex, Claude, OpenCode, and Cursor drivers, with semver ranges and recommended versions gated by T3 Code version.
  • Adds a new ProviderCompatibility service (ProviderCompatibility.ts) that loads bundled policies or fetches a remote document, enriches provider snapshots with compatibilityAdvisory fields, and may override provider status and message.
  • All four provider drivers (Codex, Claude, OpenCode, Cursor) now run targeted compatibility advisory enrichment in their snapshot pipelines and require ProviderCompatibilityService in their Effect environments.
  • Extends the server contracts with compatibilityAdvisory on ServerProvider and optional targetVersion on update input; the maintenance runner now executes a version-pinned install command when targetVersion is supplied.
  • Surfaces compatibility advisories in the chat composer as a banner with an inline "Install" popover action (ProviderUpdateActionPopover.tsx) and in the provider settings card; replaces the removed ProviderStatusBanner component.
  • Adds providerAdvisory helpers to @t3tools/client-runtime for deriving update commands, tracking in-progress updates, and building targeted update request payloads.
  • Adds a mirror script that copies the JSON into the marketing app's public directory for hosting, and wires it into the marketing build command.

Macroscope summarized f446519.


Note

Medium Risk
Adds new compatibility advisory plumbing and makes multiple provider drivers depend on ProviderCompatibilityService, affecting provider snapshot enrichment and update execution paths. Risk is mitigated by bundled fallback and new/updated tests, but failures could surface as provider status errors or missing advisories.

Overview
Introduces a provider compatibility map (provider-compatibility.v1.json) that is bundled into the server and optionally fetched at runtime (with timeout + TTL cache) to classify provider harness versions as supported / graceful / unsupported / broken for a given T3 Code version.

Wires compatibility advisory enrichment into provider snapshot generation and refresh for codex, claudeAgent, opencode, and cursor, and extends the ServerProvider contract with a compatibilityAdvisory plus targetVersion support on ServerProviderUpdateInput to run targeted installs.

Updates the web UI to surface compatibility warnings and install actions via a reusable ProviderUpdateActionPopover (including chat composer banner integration, disabling send when the selected provider is errored), and adds a marketing build step (scripts/mirror-provider-compatibility-map.ts + apps/marketing/vercel.ts) to publish the map as a static asset.

Reviewed by Cursor Bugbot for commit f446519. Bugbot is set up for automated code reviews on this repo. Configure here.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 11, 2026

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

Run ID: 7770f3f5-8143-4617-9a13-d24b738353d7

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
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch t3code/290bc166

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

@github-actions github-actions Bot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:XL 500-999 changed lines (additions + deletions). labels May 11, 2026
@juliusmarminge juliusmarminge marked this pull request as ready for review May 13, 2026 09:52
@macroscopeapp
Copy link
Copy Markdown
Contributor

macroscopeapp Bot commented May 13, 2026

Approvability

Verdict: Needs human review

This PR introduces a new provider compatibility checking feature with remote document fetching, UI banners, and targeted update capabilities. The scope includes new services, driver integrations, and significant UI changes that warrant human review.

You can customize Macroscope's approvability policy. Learn more.

Copy link
Copy Markdown
Contributor

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Autofix Details

Bugbot Autofix prepared a fix for the issue found in the latest run.

  • ✅ Fixed: Compatibility message overrides more-severe probe error message
    • Moved status computation before message computation and gated compatibilityMessage on status !== snapshot.status, so the advisory message only overrides the snapshot message when the advisory actually changed the status, preserving more-severe probe error messages.

Create PR

Or push these changes by commenting:

@cursor push 14123261fe
Preview (14123261fe)
diff --git a/apps/server/src/provider/providerCompatibility.ts b/apps/server/src/provider/providerCompatibility.ts
--- a/apps/server/src/provider/providerCompatibility.ts
+++ b/apps/server/src/provider/providerCompatibility.ts
@@ -293,10 +293,6 @@
     return snapshotWithoutAdvisory as Snapshot;
   }
 
-  const compatibilityMessage =
-    compatibilityAdvisory.severity !== "info"
-      ? (compatibilityAdvisory.message ?? undefined)
-      : undefined;
   const status =
     snapshot.enabled && compatibilityAdvisory.severity === "error"
       ? "error"
@@ -305,6 +301,10 @@
           snapshot.status === "ready"
         ? "warning"
         : snapshot.status;
+  const compatibilityMessage =
+    status !== snapshot.status && compatibilityAdvisory.severity !== "info"
+      ? (compatibilityAdvisory.message ?? undefined)
+      : undefined;
 
   return {
     ...snapshot,

You can send follow-ups to the cloud agent here.

Comment thread apps/server/src/provider/ProviderCompatibility.ts
Copy link
Copy Markdown
Contributor

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

There are 2 total unresolved issues (including 1 from previous review).

Autofix Details

Bugbot Autofix prepared a fix for the issue found in the latest run.

  • ✅ Fixed: Remote advisory cannot demote status set by bundled advisory
    • Modified applyCompatibilityAdvisory to compute base status/message by reversing the previous advisory's promotion before applying the new one, so a less-severe remote advisory can correctly demote status elevated by a bundled advisory.

Create PR

Or push these changes by commenting:

@cursor push a788468c7f
Preview (a788468c7f)
diff --git a/apps/server/src/provider/Drivers/ClaudeDriver.ts b/apps/server/src/provider/Drivers/ClaudeDriver.ts
--- a/apps/server/src/provider/Drivers/ClaudeDriver.ts
+++ b/apps/server/src/provider/Drivers/ClaudeDriver.ts
@@ -47,6 +47,7 @@
   normalizeCommandPath,
   resolveProviderMaintenanceCapabilitiesEffect,
 } from "../providerMaintenance.ts";
+import { enrichProviderSnapshotWithCompatibilityAdvisory } from "../providerCompatibility.ts";
 import { makeClaudeCapabilitiesCacheKey, makeClaudeContinuationGroupKey } from "./ClaudeHome.ts";
 const decodeClaudeSettings = Schema.decodeSync(ClaudeSettings);
 
@@ -171,6 +172,7 @@
         checkProvider,
         enrichSnapshot: ({ snapshot, publishSnapshot }) =>
           enrichProviderSnapshotWithVersionAdvisory(snapshot, maintenanceCapabilities).pipe(
+            Effect.flatMap(enrichProviderSnapshotWithCompatibilityAdvisory),
             Effect.provideService(HttpClient.HttpClient, httpClient),
             Effect.flatMap((enrichedSnapshot) => publishSnapshot(enrichedSnapshot)),
           ),

diff --git a/apps/server/src/provider/Drivers/CodexDriver.ts b/apps/server/src/provider/Drivers/CodexDriver.ts
--- a/apps/server/src/provider/Drivers/CodexDriver.ts
+++ b/apps/server/src/provider/Drivers/CodexDriver.ts
@@ -46,6 +46,7 @@
   makePackageManagedProviderMaintenanceResolver,
   resolveProviderMaintenanceCapabilitiesEffect,
 } from "../providerMaintenance.ts";
+import { enrichProviderSnapshotWithCompatibilityAdvisory } from "../providerCompatibility.ts";
 import {
   codexContinuationIdentity,
   materializeCodexShadowHome,
@@ -171,6 +172,7 @@
         checkProvider,
         enrichSnapshot: ({ snapshot, publishSnapshot }) =>
           enrichProviderSnapshotWithVersionAdvisory(snapshot, maintenanceCapabilities).pipe(
+            Effect.flatMap(enrichProviderSnapshotWithCompatibilityAdvisory),
             Effect.provideService(HttpClient.HttpClient, httpClient),
             Effect.flatMap((enrichedSnapshot) => publishSnapshot(enrichedSnapshot)),
           ),

diff --git a/apps/server/src/provider/Drivers/OpenCodeDriver.ts b/apps/server/src/provider/Drivers/OpenCodeDriver.ts
--- a/apps/server/src/provider/Drivers/OpenCodeDriver.ts
+++ b/apps/server/src/provider/Drivers/OpenCodeDriver.ts
@@ -46,6 +46,7 @@
   normalizeCommandPath,
   resolveProviderMaintenanceCapabilitiesEffect,
 } from "../providerMaintenance.ts";
+import { enrichProviderSnapshotWithCompatibilityAdvisory } from "../providerCompatibility.ts";
 const decodeOpenCodeSettings = Schema.decodeSync(OpenCodeSettings);
 
 const DRIVER_KIND = ProviderDriverKind.make("opencode");
@@ -150,6 +151,7 @@
         checkProvider,
         enrichSnapshot: ({ snapshot, publishSnapshot }) =>
           enrichProviderSnapshotWithVersionAdvisory(snapshot, maintenanceCapabilities).pipe(
+            Effect.flatMap(enrichProviderSnapshotWithCompatibilityAdvisory),
             Effect.provideService(HttpClient.HttpClient, httpClient),
             Effect.flatMap((enrichedSnapshot) => publishSnapshot(enrichedSnapshot)),
           ),

diff --git a/apps/server/src/provider/Layers/CursorProvider.ts b/apps/server/src/provider/Layers/CursorProvider.ts
--- a/apps/server/src/provider/Layers/CursorProvider.ts
+++ b/apps/server/src/provider/Layers/CursorProvider.ts
@@ -41,6 +41,7 @@
   enrichProviderSnapshotWithVersionAdvisory,
   type ProviderMaintenanceCapabilities,
 } from "../providerMaintenance.ts";
+import { enrichProviderSnapshotWithCompatibilityAdvisory } from "../providerCompatibility.ts";
 import { AcpSessionRuntime } from "../acp/AcpSessionRuntime.ts";
 
 const PROVIDER = ProviderDriverKind.make("cursor");
@@ -1242,6 +1243,7 @@
     snapshot,
     input.maintenanceCapabilities,
   ).pipe(
+    Effect.flatMap(enrichProviderSnapshotWithCompatibilityAdvisory),
     Effect.provideService(HttpClient.HttpClient, input.httpClient),
     Effect.flatMap((enrichedSnapshot) =>
       publishSnapshot(stampIdentity(enrichedSnapshot)).pipe(Effect.as(enrichedSnapshot)),

diff --git a/apps/server/src/provider/providerCompatibility.test.ts b/apps/server/src/provider/providerCompatibility.test.ts
new file mode 100644
--- /dev/null
+++ b/apps/server/src/provider/providerCompatibility.test.ts
@@ -1,0 +1,163 @@
+import { afterEach, describe, expect, it } from "vitest";
+import { ProviderDriverKind, ProviderInstanceId, type ServerProvider } from "@t3tools/contracts";
+import * as Effect from "effect/Effect";
+import { HttpClient, HttpClientResponse } from "effect/unstable/http";
+
+import {
+  applyBundledProviderCompatibilityAdvisory,
+  clearProviderCompatibilityCacheForTests,
+  createProviderCompatibilityAdvisory,
+  enrichProviderSnapshotWithCompatibilityAdvisory,
+  type ProviderCompatibilityDocument,
+} from "./providerCompatibility.ts";
+
+const codexDriver = ProviderDriverKind.make("codex");
+
+const baseProvider: ServerProvider = {
+  instanceId: ProviderInstanceId.make("codex"),
+  driver: codexDriver,
+  displayName: "Codex",
+  enabled: true,
+  installed: true,
+  version: "0.130.0",
+  status: "ready",
+  auth: { status: "authenticated" },
+  checkedAt: "2026-04-10T00:00:00.000Z",
+  models: [],
+  slashCommands: [],
+  skills: [],
+};
+
+function jsonHttpClient(payload: unknown, status = 200): HttpClient.HttpClient {
+  return HttpClient.make((request) =>
+    Effect.succeed(
+      HttpClientResponse.fromWeb(
+        request,
+        new Response(JSON.stringify(payload), {
+          status,
+          headers: { "content-type": "application/json" },
+        }),
+      ),
+    ),
+  );
+}
+
+afterEach(() => {
+  clearProviderCompatibilityCacheForTests();
+});
+
+describe("provider compatibility", () => {
+  it("selects policies by T3 Code version range", () => {
+    const document: ProviderCompatibilityDocument = {
+      version: 1,
+      policies: [
+        {
+          t3CodeRange: "<0.1.0",
+          driver: codexDriver,
+          recommendedRange: "<0.130.0",
+          recommendedVersion: "0.129.0",
+          ranges: [{ status: "broken", range: ">=0.130.0" }],
+        },
+        {
+          t3CodeRange: ">=0.1.0",
+          driver: codexDriver,
+          recommendedRange: ">=0.130.0",
+          recommendedVersion: "0.130.0",
+          ranges: [{ status: "supported", range: ">=0.130.0" }],
+        },
+      ],
+    };
+
+    expect(
+      createProviderCompatibilityAdvisory({
+        driver: codexDriver,
+        currentVersion: "0.130.0",
+        document,
+        t3CodeVersion: "0.0.22",
+      }),
+    ).toMatchObject({
+      status: "broken",
+      recommendedVersion: "0.129.0",
+    });
+  });
+
+  it("enriches snapshots from the remote compatibility map when available", async () => {
+    const remoteDocument = {
+      version: 1,
+      policies: [
+        {
+          t3CodeRange: ">=0.0.0",
+          driver: "codex",
+          recommendedRange: "<0.130.0",
+          recommendedVersion: "0.129.0",
+          ranges: [{ status: "broken", range: ">=0.130.0" }],
+        },
+      ],
+    };
+
+    const enriched = await Effect.runPromise(
+      enrichProviderSnapshotWithCompatibilityAdvisory(baseProvider).pipe(
+        Effect.provideService(HttpClient.HttpClient, jsonHttpClient(remoteDocument)),
+      ),
+    );
+
+    expect(enriched.status).toBe("error");
+    expect(enriched.compatibilityAdvisory).toMatchObject({
+      status: "broken",
+      recommendedVersion: "0.129.0",
+    });
+  });
+
+  it("falls back to the bundled map when the remote compatibility fetch fails", async () => {
+    const enriched = await Effect.runPromise(
+      enrichProviderSnapshotWithCompatibilityAdvisory({
+        ...baseProvider,
+        version: "0.128.0",
+      }).pipe(Effect.provideService(HttpClient.HttpClient, jsonHttpClient({}, 404))),
+    );
+
+    expect(enriched.status).toBe("error");
+    expect(enriched.compatibilityAdvisory).toMatchObject({
+      status: "broken",
+      recommendedVersion: "0.129.0",
+    });
+  });
+
+  it("remote advisory demotes status set by bundled advisory", async () => {
+    // The bundled document marks <0.129.0 as "broken", so 0.128.0 triggers
+    // an error advisory on the initial snapshot.
+    const snapshotAfterBundled = applyBundledProviderCompatibilityAdvisory({
+      snapshot: { ...baseProvider, version: "0.128.0" },
+      driver: codexDriver,
+      currentVersion: "0.128.0",
+    });
+    expect(snapshotAfterBundled.status).toBe("error");
+    expect(snapshotAfterBundled.compatibilityAdvisory?.severity).toBe("error");
+
+    // The remote document relaxes the policy: 0.128.0 is now "supported".
+    const remoteDocument = {
+      version: 1,
+      policies: [
+        {
+          t3CodeRange: ">=0.0.0",
+          driver: "codex",
+          recommendedRange: ">=0.129.0",
+          recommendedVersion: "0.129.0",
+          ranges: [{ status: "supported", range: ">=0.128.0" }],
+        },
+      ],
+    };
+
+    const enriched = await Effect.runPromise(
+      enrichProviderSnapshotWithCompatibilityAdvisory({
+        ...snapshotAfterBundled,
+        instanceId: baseProvider.instanceId,
+        driver: baseProvider.driver,
+      }).pipe(Effect.provideService(HttpClient.HttpClient, jsonHttpClient(remoteDocument))),
+    );
+
+    expect(enriched.status).toBe("ready");
+    expect(enriched.compatibilityAdvisory?.severity).toBe("info");
+    expect(enriched.message).toBeUndefined();
+  });
+});

diff --git a/apps/server/src/provider/providerCompatibility.ts b/apps/server/src/provider/providerCompatibility.ts
new file mode 100644
--- /dev/null
+++ b/apps/server/src/provider/providerCompatibility.ts
@@ -1,0 +1,371 @@
+import {
+  ProviderDriverKind,
+  TrimmedNonEmptyString,
+  type ServerProvider,
+  type ServerProviderCompatibilityAdvisory,
+  type ServerProviderCompatibilityRange,
+} from "@t3tools/contracts";
+import { satisfiesSemverRange } from "@t3tools/shared/semver";
+import * as DateTime from "effect/DateTime";
+import * as Effect from "effect/Effect";
+import * as Option from "effect/Option";
+import * as Schema from "effect/Schema";
+import { HttpClient, HttpClientRequest } from "effect/unstable/http";
+
+import bundledCompatibilityDocumentJson from "../../../../provider-compatibility.v1.json" with { type: "json" };
+import packageJson from "../../package.json" with { type: "json" };
+
+export interface ProviderCompatibilityPolicy {
+  readonly t3CodeRange: string;
+  readonly driver: ProviderDriverKind;
+  readonly recommendedRange: string | null;
+  readonly recommendedVersion?: string | null;
+  readonly ranges: ReadonlyArray<ServerProviderCompatibilityRange>;
+}
+
+export interface ProviderCompatibilityDocument {
+  readonly version: 1;
+  readonly policies: ReadonlyArray<ProviderCompatibilityPolicy>;
+}
+
+interface RemoteCompatibilityCacheEntry {
+  readonly expiresAt: number;
+  readonly document: ProviderCompatibilityDocument | null;
+}
+
+type ProviderCompatibilitySnapshot = Pick<ServerProvider, "enabled" | "status" | "message"> & {
+  readonly compatibilityAdvisory?: ServerProviderCompatibilityAdvisory | undefined;
+};
+
+const T3_CODE_VERSION = packageJson.version;
+const REMOTE_COMPATIBILITY_CACHE_TTL_MS = 15 * 60 * 1_000;
+const REMOTE_COMPATIBILITY_TIMEOUT_MS = 2_500;
+
+export const DEFAULT_PROVIDER_COMPATIBILITY_MAP_URL =
+  "https://raw.githubusercontent.com/pingdotgg/t3code/main/provider-compatibility.v1.json";
+
+const remoteCompatibilityCache = new Map<string, RemoteCompatibilityCacheEntry>();
+
+const RemoteCompatibilityRange = Schema.Struct({
+  status: Schema.Literals(["unknown", "supported", "graceful", "unsupported", "broken"]),
+  range: TrimmedNonEmptyString,
+  label: Schema.optional(TrimmedNonEmptyString),
+});
+
+const RemoteCompatibilityPolicy = Schema.Struct({
+  t3CodeRange: TrimmedNonEmptyString,
+  driver: TrimmedNonEmptyString,
+  recommendedRange: Schema.NullOr(TrimmedNonEmptyString),
+  recommendedVersion: Schema.optionalKey(Schema.NullOr(TrimmedNonEmptyString)),
+  ranges: Schema.Array(RemoteCompatibilityRange),
+});
+
+const RemoteCompatibilityDocument = Schema.Struct({
+  version: Schema.Literal(1),
+  policies: Schema.Array(RemoteCompatibilityPolicy),
+});
+
+function normalizeCompatibilityDocument(
+  document: typeof RemoteCompatibilityDocument.Type,
+): ProviderCompatibilityDocument {
+  return {
+    version: document.version,
+    policies: document.policies.map((policy) => ({
+      t3CodeRange: policy.t3CodeRange,
+      driver: ProviderDriverKind.make(policy.driver),
+      recommendedRange: policy.recommendedRange,
+      ...(policy.recommendedVersion !== undefined
+        ? { recommendedVersion: policy.recommendedVersion }
+        : {}),
+      ranges: policy.ranges.map((range) => ({
+        status: range.status,
+        range: range.range,
+        ...(range.label !== undefined ? { label: range.label } : {}),
+      })),
+    })),
+  };
+}
+
+const decodeRawCompatibilityDocument = Schema.decodeUnknownEffect(RemoteCompatibilityDocument);
+const decodeCompatibilityDocument = (input: unknown) =>
+  decodeRawCompatibilityDocument(input).pipe(Effect.map(normalizeCompatibilityDocument));
+
+/**
+ * Repo-root compatibility JSON bundled into the app. Used when the remote map
+ * cannot be fetched or has no matching policy for the current provider/build.
+ */
+const bundledProviderCompatibilityDocument: ProviderCompatibilityDocument =
+  normalizeCompatibilityDocument(
+    bundledCompatibilityDocumentJson as typeof RemoteCompatibilityDocument.Type,
+  );
+
+export function clearProviderCompatibilityCacheForTests(): void {
+  remoteCompatibilityCache.clear();
+}
+
+function remoteCompatibilityMapUrl(): string {
+  return (
+    process.env.T3_PROVIDER_COMPATIBILITY_MAP_URL?.trim() || DEFAULT_PROVIDER_COMPATIBILITY_MAP_URL
+  );
+}
+
+function policyMatches(input: {
+  readonly policy: ProviderCompatibilityPolicy;
+  readonly driver: ProviderDriverKind;
+  readonly t3CodeVersion: string;
+}): boolean {
+  return (
+    input.policy.driver === input.driver &&
+    satisfiesSemverRange(input.t3CodeVersion, input.policy.t3CodeRange)
+  );
+}
+
+function compatibilityPolicyForDriver(input: {
+  readonly document: ProviderCompatibilityDocument;
+  readonly driver: ProviderDriverKind;
+  readonly t3CodeVersion?: string;
+}): ProviderCompatibilityPolicy | null {
+  const t3CodeVersion = input.t3CodeVersion ?? T3_CODE_VERSION;
+  return (
+    input.document.policies.find((policy) =>
+      policyMatches({ policy, driver: input.driver, t3CodeVersion }),
+    ) ?? null
+  );
+}
+
+function severityForStatus(
+  status: ServerProviderCompatibilityAdvisory["status"],
+): ServerProviderCompatibilityAdvisory["severity"] {
+  switch (status) {
+    case "broken":
+      return "error";
+    case "unsupported":
+    case "graceful":
+      return "warning";
+    case "supported":
+    case "unknown":
+      return "info";
+  }
+}
+
+function messageForStatus(input: {
+  readonly status: ServerProviderCompatibilityAdvisory["status"];
+  readonly currentVersion: string | null;
+  readonly recommendedRange: string | null;
+  readonly recommendedVersion: string | null;
+}) {
+  const current = input.currentVersion ? ` ${input.currentVersion}` : "";
+  const recommendedTarget = input.recommendedVersion ?? input.recommendedRange;
+  const recommended = recommendedTarget ? ` Use ${recommendedTarget}.` : "";
+  switch (input.status) {
+    case "broken":
+      return `This provider harness version${current} is known to be incompatible with this T3 Code release.${recommended}`;
+    case "unsupported":
+      return `This provider harness version${current} is outside the compatibility range for this T3 Code release.${recommended}`;
+    case "graceful":
+      return `This provider harness version${current} should still work, but updating is recommended.${recommended}`;
+    case "unknown":
+      return `T3 Code could not determine whether this provider harness version is compatible.${recommended}`;
+    case "supported":
+      return null;
+  }
+}
+
+function createProviderCompatibilityAdvisoryFromDocument(input: {
+  readonly document: ProviderCompatibilityDocument;
+  readonly driver: ProviderDriverKind;
+  readonly currentVersion: string | null;
+  readonly t3CodeVersion?: string;
+}): ServerProviderCompatibilityAdvisory | undefined {
+  const policy = compatibilityPolicyForDriver({
+    document: input.document,
+    driver: input.driver,
+    ...(input.t3CodeVersion ? { t3CodeVersion: input.t3CodeVersion } : {}),
+  });
+  if (!policy) {
+    return undefined;
+  }
+
+  const currentVersion = input.currentVersion;
+  const matchedRange =
+    currentVersion === null
+      ? undefined
+      : policy.ranges.find((range) => satisfiesSemverRange(currentVersion, range.range));
+  const status = matchedRange?.status ?? (currentVersion === null ? "unknown" : "unsupported");
+  const recommendedVersion = policy.recommendedVersion ?? null;
+
+  return {
+    status,
+    severity: severityForStatus(status),
+    currentVersion: input.currentVersion,
+    message: messageForStatus({
+      status,
+      currentVersion: input.currentVersion,
+      recommendedRange: policy.recommendedRange,
+      recommendedVersion,
+    }),
+    recommendedRange: policy.recommendedRange,
+    recommendedVersion,
+    ranges: [...policy.ranges],
+  };
+}
+
+export function createProviderCompatibilityAdvisory(input: {
+  readonly driver: ProviderDriverKind;
+  readonly currentVersion: string | null;
+  readonly document?: ProviderCompatibilityDocument;
+  readonly t3CodeVersion?: string;
+}): ServerProviderCompatibilityAdvisory | undefined {
+  return createProviderCompatibilityAdvisoryFromDocument({
+    document: input.document ?? bundledProviderCompatibilityDocument,
+    driver: input.driver,
+    currentVersion: input.currentVersion,
+    ...(input.t3CodeVersion ? { t3CodeVersion: input.t3CodeVersion } : {}),
+  });
+}
+
+function fetchRemoteCompatibilityDocument(
+  url: string,
+): Effect.Effect<ProviderCompatibilityDocument | null, never, HttpClient.HttpClient> {
+  return Effect.gen(function* () {
+    const client = yield* HttpClient.HttpClient;
+    const response = yield* client
+      .execute(
+        HttpClientRequest.get(url).pipe(
+          HttpClientRequest.setHeader("accept", "application/json"),
+          HttpClientRequest.setHeader("user-agent", `t3code/${T3_CODE_VERSION}`),
+        ),
+      )
+      .pipe(Effect.timeoutOption(REMOTE_COMPATIBILITY_TIMEOUT_MS));
+
+    if (Option.isNone(response)) {
+      return null;
+    }
+
+    const httpResponse = response.value;
+    if (httpResponse.status < 200 || httpResponse.status >= 300) {
+      return null;
+    }
+
+    const payload = yield* httpResponse.json.pipe(
+      Effect.flatMap(decodeCompatibilityDocument),
+      Effect.catch(() => Effect.succeed(null)),
+    );
+    return payload;
+  }).pipe(
+    Effect.tapError((cause) =>
+      Effect.logWarning("provider compatibility map fetch failed", {
+        cause,
+        url,
+      }),
+    ),
+    Effect.catch(() => Effect.succeed(null)),
+  );
+}
+
+export const resolveRemoteProviderCompatibilityDocument = Effect.fn(
+  "resolveRemoteProviderCompatibilityDocument",
+)(function* () {
+  const url = remoteCompatibilityMapUrl();
+  const now = DateTime.toEpochMillis(yield* DateTime.now);
+  const cached = remoteCompatibilityCache.get(url);
+  if (cached && cached.expiresAt > now) {
+    return cached.document;
+  }
+
+  const document = yield* fetchRemoteCompatibilityDocument(url);
+  remoteCompatibilityCache.set(url, {
+    expiresAt: now + REMOTE_COMPATIBILITY_CACHE_TTL_MS,
+    document,
+  });
+  return document;
+});
+
+function baseStatusWithoutAdvisory<Snapshot extends ProviderCompatibilitySnapshot>(
+  snapshot: Snapshot,
+): Snapshot["status"] {
+  const prev = snapshot.compatibilityAdvisory;
+  if (!prev || !snapshot.enabled) return snapshot.status;
+  if (prev.severity === "error" && snapshot.status === "error") return "ready";
+  if (prev.severity === "warning" && snapshot.status === "warning") return "ready";
+  return snapshot.status;
+}
+
+function baseMessageWithoutAdvisory<Snapshot extends ProviderCompatibilitySnapshot>(
+  snapshot: Snapshot,
+): Snapshot["message"] {
+  const prev = snapshot.compatibilityAdvisory;
+  if (prev?.message != null && snapshot.message === prev.message) return undefined;
+  return snapshot.message;
+}
+
+function applyCompatibilityAdvisory<Snapshot extends ProviderCompatibilitySnapshot>(
+  snapshot: Snapshot,
+  compatibilityAdvisory: ServerProviderCompatibilityAdvisory | undefined,
+): Snapshot {
+  if (!compatibilityAdvisory) {
+    const { compatibilityAdvisory: _omit, ...snapshotWithoutAdvisory } = snapshot;
+    return snapshotWithoutAdvisory as Snapshot;
+  }
+
+  const baseStatus = baseStatusWithoutAdvisory(snapshot);
+
+  const compatibilityMessage =
+    compatibilityAdvisory.severity !== "info"
+      ? (compatibilityAdvisory.message ?? undefined)
+      : undefined;
+  const status =
+    snapshot.enabled && compatibilityAdvisory.severity === "error"
+      ? "error"
+      : snapshot.enabled && compatibilityAdvisory.severity === "warning" && baseStatus === "ready"
+        ? "warning"
+        : baseStatus;
+
+  const baseMessage = baseMessageWithoutAdvisory(snapshot);
+  const resolvedMessage = compatibilityMessage ?? baseMessage;
+  const { message: _existingMessage, ...snapshotWithoutMessage } = snapshot;
+
+  return {
+    ...snapshotWithoutMessage,
+    status,
+    ...(resolvedMessage != null ? { message: resolvedMessage } : {}),
+    compatibilityAdvisory,
+  } as Snapshot;
+}
+
+export function applyBundledProviderCompatibilityAdvisory<
+  Snapshot extends ProviderCompatibilitySnapshot,
+>(input: {
+  readonly snapshot: Snapshot;
+  readonly driver: ProviderDriverKind;
+  readonly currentVersion: string | null;
+}): Snapshot {
+  return applyCompatibilityAdvisory(
+    input.snapshot,
+    createProviderCompatibilityAdvisory({
+      driver: input.driver,
+      currentVersion: input.currentVersion,
+    }),
+  );
+}
+
+export const enrichProviderSnapshotWithCompatibilityAdvisory = Effect.fn(
+  "enrichProviderSnapshotWithCompatibilityAdvisory",
+)(function* (snapshot: ServerProvider) {
+  const remoteDocument = yield* resolveRemoteProviderCompatibilityDocument();
+  const remoteAdvisory = remoteDocument
+    ? createProviderCompatibilityAdvisory({
+        driver: snapshot.driver,
+        currentVersion: snapshot.version,
+        document: remoteDocument,
+      })
+    : undefined;
+  const advisory =
+    remoteAdvisory ??
+    createProviderCompatibilityAdvisory({
+      driver: snapshot.driver,
+      currentVersion: snapshot.version,
+    });
+
+  return applyCompatibilityAdvisory(snapshot, advisory);
+});

diff --git a/apps/server/src/provider/providerSnapshot.test.ts b/apps/server/src/provider/providerSnapshot.test.ts
--- a/apps/server/src/provider/providerSnapshot.test.ts
+++ b/apps/server/src/provider/providerSnapshot.test.ts
@@ -2,7 +2,7 @@
 import { ProviderDriverKind, type ModelCapabilities } from "@t3tools/contracts";
 import { createModelCapabilities } from "@t3tools/shared/model";
 
-import { providerModelsFromSettings } from "./providerSnapshot.ts";
+import { buildServerProvider, providerModelsFromSettings } from "./providerSnapshot.ts";
 
 const OPENCODE_CUSTOM_MODEL_CAPABILITIES: ModelCapabilities = createModelCapabilities({
   optionDescriptors: [
@@ -42,3 +42,56 @@
     ]);
   });
 });
+
+describe("buildServerProvider", () => {
+  it("marks known incompatible provider harness versions as errors", () => {
+    const provider = buildServerProvider({
+      driver: ProviderDriverKind.make("codex"),
+      presentation: { displayName: "Codex" },
+      enabled: true,
+      checkedAt: "2026-04-10T00:00:00.000Z",
+      models: [],
+      probe: {
+        installed: true,
+        version: "0.128.0",
+        status: "ready",
+        auth: { status: "authenticated" },
+      },
+    });
+
+    expect(provider.status).toBe("error");
+    expect(provider.compatibilityAdvisory).toMatchObject({
+      status: "broken",
+      severity: "error",
+      currentVersion: "0.128.0",
+      recommendedRange: ">=0.129.0",
+      recommendedVersion: "0.129.0",
+    });
+    expect(provider.message).toContain("known to be incompatible");
+  });
+
+  it("keeps known supported provider harness versions ready", () => {
+    const provider = buildServerProvider({
+      driver: ProviderDriverKind.make("codex"),
+      presentation: { displayName: "Codex" },
+      enabled: true,
+      checkedAt: "2026-04-10T00:00:00.000Z",
+      models: [],
+      probe: {
+        installed: true,
+        version: "0.129.0",
+        status: "ready",
+        auth: { status: "authenticated" },
+      },
+    });
+
+    expect(provider.status).toBe("ready");
+    expect(provider.compatibilityAdvisory).toMatchObject({
+      status: "supported",
+      severity: "info",
+      currentVersion: "0.129.0",
+      recommendedVersion: "0.129.0",
+    });
+    expect(provider.message).toBeUndefined();
+  });
+});

diff --git a/apps/server/src/provider/providerSnapshot.ts b/apps/server/src/provider/providerSnapshot.ts
--- a/apps/server/src/provider/providerSnapshot.ts
+++ b/apps/server/src/provider/providerSnapshot.ts
@@ -14,6 +14,7 @@
 import { ChildProcess, ChildProcessSpawner } from "effect/unstable/process";
 import { normalizeModelSlug } from "@t3tools/shared/model";
 import { isWindowsCommandNotFound } from "../processRunner.ts";
+import { applyBundledProviderCompatibilityAdvisory } from "./providerCompatibility.ts";
 import { createProviderVersionAdvisory } from "./providerMaintenance.ts";
 import { collectUint8StreamText } from "../stream/collectUint8StreamText.ts";
 
@@ -208,7 +209,7 @@
         checkedAt: input.checkedAt,
       })
     : undefined;
-  return {
+  const snapshot: ServerProviderDraft = {
     displayName: input.presentation.displayName,
     ...(input.presentation.badgeLabel ? { badgeLabel: input.presentation.badgeLabel } : {}),
     ...(typeof input.presentation.showInteractionModeToggle === "boolean"
@@ -226,6 +227,13 @@
     skills: [...(input.skills ?? [])],
     ...(versionAdvisory ? { versionAdvisory } : {}),
   };
+  return input.driver
+    ? applyBundledProviderCompatibilityAdvisory({
+        snapshot,
+        driver: input.driver,
+        currentVersion: input.probe.version,
+      })
+    : snapshot;
 }
 
 export const collectStreamAsString = <E>(

diff --git a/apps/web/src/components/settings/ProviderInstanceCard.tsx b/apps/web/src/components/settings/ProviderInstanceCard.tsx
--- a/apps/web/src/components/settings/ProviderInstanceCard.tsx
+++ b/apps/web/src/components/settings/ProviderInstanceCard.tsx
@@ -1,6 +1,7 @@
 "use client";
 
 import {
+  AlertTriangleIcon,
   ArrowUpCircleIcon,
   ChevronDownIcon,
   CopyIcon,
@@ -40,6 +41,7 @@
 import {
   getProviderVersionAdvisoryPresentation,
   PROVIDER_STATUS_STYLES,
+  getProviderCompatibilityAdvisoryPresentation,
   getProviderSummary,
   getProviderVersionLabel,
   type ProviderStatusKey,
@@ -481,6 +483,9 @@
     : null;
   const summary = rawSummary;
   const versionLabel = getProviderVersionLabel(liveProvider?.version);
+  const compatibilityAdvisory = getProviderCompatibilityAdvisoryPresentation(
+    liveProvider?.compatibilityAdvisory,
+  );
   const versionAdvisory = getProviderVersionAdvisoryPresentation(liveProvider?.versionAdvisory);
   const updateCommand = versionAdvisory?.updateCommand ?? null;
   const FallbackIconComponent = driverOption?.icon;
@@ -677,6 +682,45 @@
             <div className="flex min-w-0 flex-wrap items-center gap-2">
               {titleHeadNode}
               {versionCodeNode}
+              {compatibilityAdvisory ? (
+                <Popover>
+                  <PopoverTrigger
+                    render={
+                      <Button
+                        type="button"
+                        size="icon-xs"
+                        variant="ghost"
+                        className={cn(
+                          "size-5 rounded-sm p-0",
+                          compatibilityAdvisory.emphasis === "strong"
+                            ? "text-destructive hover:text-destructive"
+                            : "text-warning hover:text-warning",
+                        )}
+                        aria-label={`${compatibilityAdvisory.title} — view details`}
+                      >
+                        <AlertTriangleIcon className="size-3.5" />
+                      </Button>
+                    }
+                  />
+                  <PopoverPopup side="bottom" align="start" className="w-84">
+                    <div className="grid gap-1">
+                      <p className="text-[13px] font-semibold leading-tight text-foreground">
+                        {compatibilityAdvisory.title}
+                      </p>
+                      <p
+                        className={cn(
+                          "text-xs leading-snug",
+                          compatibilityAdvisory.emphasis === "strong"
+                            ? "text-destructive"
+                            : "text-warning",
+                        )}
+                      >
+                        {compatibilityAdvisory.detail}
+                      </p>
+                    </div>
... diff truncated: showing 800 of 1015 lines

You can send follow-ups to the cloud agent here.

Comment thread apps/server/src/provider/providerCompatibility.ts Outdated
@juliusmarminge juliusmarminge force-pushed the t3code/290bc166 branch 5 times, most recently from c14e731 to e3818c6 Compare May 13, 2026 12:34
Comment thread apps/server/src/provider/ProviderCompatibility.ts
@juliusmarminge juliusmarminge force-pushed the t3code/290bc166 branch 2 times, most recently from b6fa144 to 9502396 Compare May 13, 2026 12:45
Copy link
Copy Markdown
Contributor

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

There are 2 total unresolved issues (including 1 from previous review).

Autofix Details

Bugbot Autofix prepared a fix for the issue found in the latest run.

  • ✅ Fixed: Negative cache prevents retry after transient failures
    • Added a separate 60-second negative-cache TTL constant used when caching null (failed) fetch results, while keeping the 15-minute TTL for successful entries.

Create PR

Or push these changes by commenting:

@cursor push 3edc33ea14
Preview (3edc33ea14)
diff --git a/apps/server/src/provider/providerCompatibility.ts b/apps/server/src/provider/providerCompatibility.ts
--- a/apps/server/src/provider/providerCompatibility.ts
+++ b/apps/server/src/provider/providerCompatibility.ts
@@ -39,6 +39,7 @@
 
 const T3_CODE_VERSION = packageJson.version;
 const REMOTE_COMPATIBILITY_CACHE_TTL_MS = 15 * 60 * 1_000;
+const REMOTE_COMPATIBILITY_NEGATIVE_CACHE_TTL_MS = 60 * 1_000;
 const REMOTE_COMPATIBILITY_TIMEOUT_MS = 2_500;
 
 export const DEFAULT_PROVIDER_COMPATIBILITY_MAP_URL =
@@ -290,8 +291,11 @@
     }
 
     const document = yield* fetchRemoteCompatibilityDocument(url);
+    const ttl = document
+      ? REMOTE_COMPATIBILITY_CACHE_TTL_MS
+      : REMOTE_COMPATIBILITY_NEGATIVE_CACHE_TTL_MS;
     remoteCompatibilityCache.set(url, {
-      expiresAt: now + REMOTE_COMPATIBILITY_CACHE_TTL_MS,
+      expiresAt: now + ttl,
       document,
     });
     if (document) {

You can send follow-ups to the cloud agent here.

Comment thread apps/server/src/provider/providerCompatibility.ts Outdated
Copy link
Copy Markdown
Contributor

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

There are 3 total unresolved issues (including 2 from previous reviews).

Autofix Details

Bugbot Autofix prepared a fix for the issue found in the latest run.

  • ✅ Fixed: Status reset loses real provider errors during advisory removal
    • Added preAdvisoryStatus and preAdvisoryMessage fields to the advisory object so removeExistingCompatibilityAdvisory can restore the original provider status and message instead of unconditionally resetting to 'ready'.

Create PR

Or push these changes by commenting:

@cursor push fad51d16d8
Preview (fad51d16d8)
diff --git a/apps/server/src/provider/providerCompatibility.test.ts b/apps/server/src/provider/providerCompatibility.test.ts
--- a/apps/server/src/provider/providerCompatibility.test.ts
+++ b/apps/server/src/provider/providerCompatibility.test.ts
@@ -327,6 +327,52 @@
     });
   });
 
+  it.effect("preserves genuine probe errors when the remote map relaxes a bundled advisory", () => {
+    const bundledMessage =
+      "This provider harness version 0.130.0 is known to be incompatible with this T3 Code release. Use 0.129.0.";
+    const probeErrorMessage = "CLI health-check failed: process exited with code 1";
+    const remoteDocument = {
+      version: 1,
+      policies: [
+        {
+          t3CodeRange: ">=0.0.0",
+          driver: "codex",
+          recommendedRange: ">=0.130.0",
+          recommendedVersion: "0.130.0",
+          ranges: [{ status: "supported", range: ">=0.130.0" }],
+        },
+      ],
+    };
+
+    return Effect.gen(function* () {
+      const enriched = yield* enrichProviderSnapshotWithCompatibilityAdvisory({
+        ...baseProvider,
+        status: "error",
+        message: bundledMessage,
+        compatibilityAdvisory: {
+          status: "broken",
+          severity: "error",
+          currentVersion: "0.130.0",
+          message: bundledMessage,
+          recommendedRange: "<0.130.0",
+          recommendedVersion: "0.129.0",
+          ranges: [{ status: "broken", range: ">=0.130.0" }],
+          preAdvisoryStatus: "error",
+          preAdvisoryMessage: probeErrorMessage,
+        },
+      }).pipe(
+        Effect.provideService(
+          HttpClient.HttpClient,
+          jsonHttpClient(() => ({ payload: remoteDocument })),
+        ),
+      );
+
+      expect(enriched.status).toBe("error");
+      expect(enriched.message).toBe(probeErrorMessage);
+      expect(enriched.compatibilityAdvisory).toMatchObject({ status: "supported" });
+    });
+  });
+
   it.effect("falls back to the bundled map when the remote compatibility fetch fails", () =>
     Effect.gen(function* () {
       const enriched = yield* enrichProviderSnapshotWithCompatibilityAdvisory({

diff --git a/apps/server/src/provider/providerCompatibility.ts b/apps/server/src/provider/providerCompatibility.ts
--- a/apps/server/src/provider/providerCompatibility.ts
+++ b/apps/server/src/provider/providerCompatibility.ts
@@ -325,13 +325,19 @@
         ? "warning"
         : baseSnapshot.status;
 
+  const advisoryWithPreState: ServerProviderCompatibilityAdvisory = {
+    ...compatibilityAdvisory,
+    preAdvisoryStatus: baseSnapshot.status,
+    ...(baseSnapshot.message ? { preAdvisoryMessage: baseSnapshot.message } : {}),
+  };
+
   return {
     ...baseSnapshot,
     status,
     ...(compatibilityMessage || baseSnapshot.message
       ? { message: compatibilityMessage ?? baseSnapshot.message }
       : {}),
-    compatibilityAdvisory,
+    compatibilityAdvisory: advisoryWithPreState,
   } as Snapshot;
 }
 
@@ -349,11 +355,19 @@
       : undefined;
   if (compatibilityMessage && baseSnapshot.message === compatibilityMessage) {
     const { message: _message, ...snapshotWithoutCompatibilityMessage } = baseSnapshot;
+    const restoredStatus =
+      existingCompatibilityAdvisory.preAdvisoryStatus ?? (snapshot.enabled ? "ready" : "disabled");
     return {
       ...snapshotWithoutCompatibilityMessage,
-      status: snapshot.enabled ? "ready" : "disabled",
+      status: restoredStatus,
+      ...(existingCompatibilityAdvisory.preAdvisoryMessage
+        ? { message: existingCompatibilityAdvisory.preAdvisoryMessage }
+        : {}),
     } as Snapshot;
   }
+  if (existingCompatibilityAdvisory.preAdvisoryStatus !== undefined) {
+    return { ...baseSnapshot, status: existingCompatibilityAdvisory.preAdvisoryStatus } as Snapshot;
+  }
   return baseSnapshot as Snapshot;
 }
 

diff --git a/packages/contracts/src/server.ts b/packages/contracts/src/server.ts
--- a/packages/contracts/src/server.ts
+++ b/packages/contracts/src/server.ts
@@ -163,6 +163,8 @@
   ranges: Schema.Array(ServerProviderCompatibilityRange).pipe(
     Schema.withDecodingDefault(Effect.succeed([])),
   ),
+  preAdvisoryStatus: Schema.optionalKey(ServerProviderState),
+  preAdvisoryMessage: Schema.optionalKey(TrimmedNonEmptyString),
 });
 export type ServerProviderCompatibilityAdvisory = typeof ServerProviderCompatibilityAdvisory.Type;

You can send follow-ups to the cloud agent here.

Comment thread apps/server/src/provider/ProviderCompatibility.ts
Comment thread apps/server/src/provider/ProviderCompatibility.ts Outdated
@cursor
Copy link
Copy Markdown
Contributor

cursor Bot commented May 13, 2026

Bugbot Autofix prepared a fix for the issue found in the latest run.

  • ✅ Fixed: Exported function resolveRemoteProviderCompatibilityDocument is unused
    • Removed the unused exported function resolveRemoteProviderCompatibilityDocument which was dead code never imported or called anywhere in the codebase.

Create PR

Or push these changes by commenting:

@cursor push b0048f7a73
Preview (b0048f7a73)
diff --git a/apps/server/src/provider/ProviderCompatibility.ts b/apps/server/src/provider/ProviderCompatibility.ts
--- a/apps/server/src/provider/ProviderCompatibility.ts
+++ b/apps/server/src/provider/ProviderCompatibility.ts
@@ -233,13 +233,6 @@
     ),
 );
 
-export const resolveRemoteProviderCompatibilityDocument = Effect.fn(
-  "resolveRemoteProviderCompatibilityDocument",
-)(function* () {
-  const compatibility = yield* ProviderCompatibilityService;
-  return yield* compatibility.resolveRemoteDocument;
-});
-
 function applyCompatibilityAdvisory<Snapshot extends ProviderCompatibilitySnapshot>(
   snapshot: Snapshot,
   compatibilityAdvisory: ServerProviderCompatibilityAdvisory | undefined,

You can send follow-ups to the cloud agent here.

@github-actions github-actions Bot added size:XXL 1,000+ changed lines (additions + deletions). and removed size:XL 500-999 changed lines (additions + deletions). labels May 13, 2026
Comment thread apps/server/src/provider/ProviderCompatibility.ts Outdated
Copy link
Copy Markdown
Contributor

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

There are 4 total unresolved issues (including 3 from previous reviews).

Autofix Details

Bugbot Autofix prepared a fix for the issue found in the latest run.

  • ✅ Fixed: Test contradicts bundled compatibility map for codex version
    • Changed the test driver from 'codex' (which has a matching bundled policy marking <0.129.0 as broken) to 'unmapped-test-driver' (no policy in the bundled map), so the test correctly exercises the no-matching-policy code path.

Create PR

Or push these changes by commenting:

@cursor push e24a57b546
Preview (e24a57b546)
diff --git a/apps/server/src/provider/providerSnapshot.test.ts b/apps/server/src/provider/providerSnapshot.test.ts
--- a/apps/server/src/provider/providerSnapshot.test.ts
+++ b/apps/server/src/provider/providerSnapshot.test.ts
@@ -46,8 +46,8 @@
 describe("buildServerProvider", () => {
   it("leaves compatibility unset when no bundled policy matches this T3 Code version", () => {
     const provider = buildServerProvider({
-      driver: ProviderDriverKind.make("codex"),
-      presentation: { displayName: "Codex" },
+      driver: ProviderDriverKind.make("unmapped-test-driver"),
+      presentation: { displayName: "Test" },
       enabled: true,
       checkedAt: "2026-04-10T00:00:00.000Z",
       models: [],

You can send follow-ups to the cloud agent here.

Comment thread apps/server/src/provider/providerSnapshot.test.ts
Julius Marminge and others added 3 commits May 20, 2026 10:35
- move provider compatibility logic behind a shared service
- cache remote map fetches across snapshot refreshes
- update drivers and tests to provide the new layer
juliusmarminge and others added 2 commits May 20, 2026 10:35
- Generate provider-specific install commands from compatibility advisories
- Surface provider update actions in the chat UI and block sends when unavailable
- Skip stale advisories for disabled providers before version probing

Co-authored-by: codex <codex@users.noreply.github.com>
juliusmarminge and others added 2 commits May 20, 2026 10:52
- Move the Vercel-specific install and build commands into `apps/marketing/vercel.ts`
- Simplify the marketing package build script to `astro build`
Co-authored-by: codex <codex@users.noreply.github.com>
Copy link
Copy Markdown
Contributor

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Autofix Details

Bugbot Autofix prepared a fix for the issue found in the latest run.

  • ✅ Fixed: Remote map drops bundled policy
    • Modified createProviderCompatibilityAdvisory to fall back to the bundled document when the provided remote document yields no matching policy, preventing the enrichment path from stripping a valid bundled advisory.

Create PR

Or push these changes by commenting:

@cursor push 0883def1a8
Preview (0883def1a8)
diff --git a/apps/server/src/provider/ProviderCompatibility.ts b/apps/server/src/provider/ProviderCompatibility.ts
--- a/apps/server/src/provider/ProviderCompatibility.ts
+++ b/apps/server/src/provider/ProviderCompatibility.ts
@@ -204,12 +204,26 @@
   readonly maintenanceCapabilities?: ProviderMaintenanceCapabilities | undefined;
   readonly t3CodeVersion?: string;
 }): ServerProviderCompatibilityAdvisory | undefined {
-  return createProviderCompatibilityAdvisoryFromDocument({
-    document: input.document ?? bundledProviderCompatibilityDocument,
+  const shared = {
     driver: input.driver,
     currentVersion: input.currentVersion,
     maintenanceCapabilities: input.maintenanceCapabilities,
     ...(input.t3CodeVersion ? { t3CodeVersion: input.t3CodeVersion } : {}),
+  };
+
+  if (input.document) {
+    const advisory = createProviderCompatibilityAdvisoryFromDocument({
+      document: input.document,
+      ...shared,
+    });
+    if (advisory) {
+      return advisory;
+    }
+  }
+
+  return createProviderCompatibilityAdvisoryFromDocument({
+    document: bundledProviderCompatibilityDocument,
+    ...shared,
   });
 }

You can send follow-ups to the cloud agent here.

Comment thread apps/server/src/provider/ProviderCompatibility.ts
juliusmarminge and others added 3 commits May 20, 2026 11:11
Co-authored-by: codex <codex@users.noreply.github.com>
- Decode the source JSON inline before copying
- Switch logging to `Effect.logInfo` and remove unused console import
- Require t3Code 0.0.24+ for bundled drivers
- Pin OpenCode recommendation to 1.14.40 and flag newer harness versions
Copy link
Copy Markdown
Contributor

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

Bugbot Autofix prepared a fix for the issue found in the latest run.

  • ✅ Fixed: OpenCode policy misclassifies versions
    • Changed the >1.14.40 range status from 'supported' to 'broken' and narrowed the supported range from '>=1.14.19' to '>=1.14.19 <=1.14.40' so versions above 1.14.40 correctly receive broken advisories.

Create PR

Or push these changes by commenting:

@cursor push 973bdec065
Preview (973bdec065)
diff --git a/provider-compatibility.v1.json b/provider-compatibility.v1.json
--- a/provider-compatibility.v1.json
+++ b/provider-compatibility.v1.json
@@ -39,13 +39,13 @@
       "recommendedVersion": "1.14.40",
       "ranges": [
         {
-          "status": "supported",
+          "status": "broken",
           "range": ">1.14.40",
           "label": "Known incompatible OpenCode harness"
         },
         {
           "status": "supported",
-          "range": ">=1.14.19",
+          "range": ">=1.14.19 <=1.14.40",
           "label": "Known working OpenCode harness"
         },
         {

You can send follow-ups to the cloud agent here.

Reviewed by Cursor Bugbot for commit 97201a8. Configure here.

Comment thread provider-compatibility.v1.json
juliusmarminge and others added 2 commits May 20, 2026 13:25
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XXL 1,000+ changed lines (additions + deletions). vouch:trusted PR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant