fix(enterprise/coderd/license): remove AI Governance required banner (#28268)

## Summary

The AI Governance add-on is now included in all Premium licenses, so the
transitional license banner warning that AI Bridge / AI Gateway requires
the AI Governance add-on is no longer accurate. This removes that
warning throughout Coder.

The banner text this removes:

> The AI Governance add-on is required to use AI Gateway. Please reach
out to your account team or sales@coder.com to learn more.

## Changes

- Remove the AI Bridge soft warning block in `LicensesEntitlements`.
- Remove the now-unused `hasExplicitAIBridgeEntitlement` tracking that
only fed that warning.
- Remove `TestAIBridgeSoftWarning` and the vestigial warning
assertions/comments that pinned the removed behavior.

The license banner surfaces backend entitlement warnings verbatim, so
removing the warning here removes it from the dashboard banner. No
frontend changes are needed; the warning string existed only in the
backend.

## Testing

- `go build ./enterprise/coderd/license/...`
- `go vet ./enterprise/coderd/license/...`
- `go test ./enterprise/coderd/license/` (license entitlement + AI
Governance addon suites)

---

*This PR was created by Coder Agents on behalf of @jcjiang.*
This commit is contained in:
Jiachen Jiang
2026-08-19 08:50:17 -07:00
committed by GitHub
parent e003014ff8
commit 6107cfe918
2 changed files with 0 additions and 188 deletions
-26
View File
@@ -347,12 +347,6 @@ func LicensesEntitlements(
keys map[string]ed25519.PublicKey,
featureArguments FeatureArguments,
) (codersdk.Entitlements, error) {
// TODO: Remove this tracking once AI Bridge is enforced as an add-on license.
// Track if AI Bridge was explicitly granted via license Features (add-on)
// vs inherited from FeatureSet (Premium). Only explicit grants should
// suppress the soft warning for AI Bridge GA.
hasExplicitAIBridgeEntitlement := false
// Each valid license's FeatureUserLimit claim forms a candidate pairing of
// seat limit and counting mode: licenses carrying the AI Governance
// addon count workspace-capable users, others count all active users.
@@ -514,15 +508,6 @@ func LicensesEntitlements(
})
}
// TODO: Remove this tracking once AI Bridge is enforced as an add-on license.
// Track explicit AI Bridge entitlement (add-on license). This is checked
// at the license level since AI Bridge may come from the FeatureSet
// (Premium) rather than being explicitly listed in claims.Features.
// Only having the AI Governance addon should suppress the soft warning.
if slices.Contains(claims.Addons, codersdk.AddonAIGovernance) {
hasExplicitAIBridgeEntitlement = true
}
// Add all features from the feature set.
for _, featureName := range claims.FeatureSet.Features() {
if featureName.UsesLimit() || featureName.UsesUsagePeriod() {
@@ -924,17 +909,6 @@ func LicensesEntitlements(
default:
}
}
// TODO: Remove this soft warning block once AI Bridge is enforced as an add-on license.
// AI Bridge soft warning: Show warning when AI Bridge is enabled and
// entitled via Premium FeatureSet but not via explicit add-on license.
// This is a transitional warning as AI Bridge moves to GA and will
// require a separate add-on license in future versions.
aiBridgeFeature := entitlements.Features[codersdk.FeatureAIBridge]
if aiBridgeFeature.Enabled && aiBridgeFeature.Entitlement.Entitled() && !hasExplicitAIBridgeEntitlement {
entitlements.Warnings = append(entitlements.Warnings,
"The AI Governance add-on is required to use AI Gateway. Please reach out to your account team or sales@coder.com to learn more.")
}
}
// Wrap up by disabling all features that are not entitled.
-162
View File
@@ -57,9 +57,6 @@ func premiumRuntimeHoursFixture(t *testing.T) (*dbmock.MockStore, *coderdenttest
// warning cannot pollute the callers' warning assertions.
GraceAt: dbtime.Now().Add(time.Hour * 24 * 60).Truncate(time.Second),
ExpiresAt: dbtime.Now().Add(time.Hour * 24 * 90).Truncate(time.Second),
// The addon marks AI Bridge as explicitly entitled, suppressing
// the unrelated "AI Governance add-on is required to use AI
// Gateway" warning that Premium would otherwise produce.
}).UserLimit(100).AIGovernanceAddon(100).AgentRuntimeHours(100, ptr.Ref[int64](80), ptr.Ref[int64](120))
lic := database.License{
@@ -2301,163 +2298,6 @@ func TestLicenseEntitlements(t *testing.T) {
}
}
func TestAIBridgeSoftWarning(t *testing.T) {
t.Parallel()
aiBridgeEnabledEnablements := map[codersdk.FeatureName]bool{
codersdk.FeatureAIBridge: true,
}
aiBridgeDisabledEnablements := map[codersdk.FeatureName]bool{
codersdk.FeatureAIBridge: false,
}
aiBridgeWarningMessage := "The AI Governance add-on is required to use AI Gateway. Please reach out to your account team or sales@coder.com to learn more."
// A Premium license grants a managed agent limit and a grandfathered
// agent runtime allocation by default: a nil AgentRuntimeMsFn is a hard
// developer error and a nil ManagedAgentCountFn degrades into an
// entitlements error, so these subtests wire zero-usage closures.
zeroUsageArgs := license.FeatureArguments{
ManagedAgentCountFn: func(_ context.Context, _, _ time.Time) (int64, error) {
return 0, nil
},
AgentRuntimeMsFn: func(_ context.Context, _, _ time.Time) (int64, error) {
return 0, nil
},
}
t.Run("NoAddon_AIBridgeOff", func(t *testing.T) {
t.Parallel()
// License without addon and AI Bridge disabled should NOT show warning.
lo := (&coderdenttest.LicenseOptions{
AccountType: "salesforce",
AccountID: "test",
FeatureSet: codersdk.FeatureSetPremium,
}).Valid(time.Now())
generatedLicenses := []database.License{
{
ID: 1,
UploadedAt: time.Now().Add(time.Hour * -1),
JWT: lo.Generate(t),
Exp: lo.GraceAt,
UUID: uuid.New(),
},
}
entitlements, err := license.LicensesEntitlements(context.Background(), time.Now(), generatedLicenses, aiBridgeDisabledEnablements, coderdenttest.Keys, zeroUsageArgs)
require.NoError(t, err)
aiBridgeFeature := entitlements.Features[codersdk.FeatureAIBridge]
assert.False(t, aiBridgeFeature.Enabled)
require.NotContains(t, entitlements.Warnings, aiBridgeWarningMessage)
})
t.Run("NoAddon_AIBridgeOn", func(t *testing.T) {
t.Parallel()
// License without addon and AI Bridge enabled SHOULD show warning.
lo := (&coderdenttest.LicenseOptions{
AccountType: "salesforce",
AccountID: "test",
FeatureSet: codersdk.FeatureSetPremium,
}).Valid(time.Now())
generatedLicenses := []database.License{
{
ID: 1,
UploadedAt: time.Now().Add(time.Hour * -1),
JWT: lo.Generate(t),
Exp: lo.GraceAt,
UUID: uuid.New(),
},
}
entitlements, err := license.LicensesEntitlements(context.Background(), time.Now(), generatedLicenses, aiBridgeEnabledEnablements, coderdenttest.Keys, zeroUsageArgs)
require.NoError(t, err)
aiBridgeFeature := entitlements.Features[codersdk.FeatureAIBridge]
assert.True(t, aiBridgeFeature.Enabled)
assert.Equal(t, codersdk.EntitlementEntitled, aiBridgeFeature.Entitlement)
require.Contains(t, entitlements.Warnings, aiBridgeWarningMessage)
})
t.Run("Addon_AIBridgeOff", func(t *testing.T) {
t.Parallel()
// License with addon and AI Bridge disabled should NOT show warning.
lo := (&coderdenttest.LicenseOptions{
AccountType: "salesforce",
AccountID: "test",
FeatureSet: codersdk.FeatureSetPremium,
Addons: []codersdk.Addon{codersdk.AddonAIGovernance},
Features: license.Features{
codersdk.FeatureAIGovernanceUserLimit: 100,
},
}).Valid(time.Now())
generatedLicenses := []database.License{
{
ID: 1,
UploadedAt: time.Now().Add(time.Hour * -1),
JWT: lo.Generate(t),
Exp: lo.GraceAt,
UUID: uuid.New(),
},
}
entitlements, err := license.LicensesEntitlements(context.Background(), time.Now(), generatedLicenses, aiBridgeDisabledEnablements, coderdenttest.Keys, zeroUsageArgs)
require.NoError(t, err)
aiBridgeFeature := entitlements.Features[codersdk.FeatureAIBridge]
assert.False(t, aiBridgeFeature.Enabled)
require.NotContains(t, entitlements.Warnings, aiBridgeWarningMessage)
})
t.Run("Addon_AIBridgeOn", func(t *testing.T) {
t.Parallel()
// License with addon and AI Bridge enabled should NOT show warning.
lo := (&coderdenttest.LicenseOptions{
AccountType: "salesforce",
AccountID: "test",
FeatureSet: codersdk.FeatureSetPremium,
Addons: []codersdk.Addon{codersdk.AddonAIGovernance},
Features: license.Features{
codersdk.FeatureAIGovernanceUserLimit: 100,
},
}).Valid(time.Now())
generatedLicenses := []database.License{
{
ID: 1,
UploadedAt: time.Now().Add(time.Hour * -1),
JWT: lo.Generate(t),
Exp: lo.GraceAt,
UUID: uuid.New(),
},
}
entitlements, err := license.LicensesEntitlements(context.Background(), time.Now(), generatedLicenses, aiBridgeEnabledEnablements, coderdenttest.Keys, zeroUsageArgs)
require.NoError(t, err)
aiBridgeFeature := entitlements.Features[codersdk.FeatureAIBridge]
assert.True(t, aiBridgeFeature.Enabled)
assert.Equal(t, codersdk.EntitlementEntitled, aiBridgeFeature.Entitlement)
require.NotContains(t, entitlements.Warnings, aiBridgeWarningMessage)
})
t.Run("NoLicense_AIBridgeOn", func(t *testing.T) {
t.Parallel()
// No license with AI Bridge enabled should NOT show the soft warning
// (it will show the generic "not entitled" warning instead).
entitlements, err := license.LicensesEntitlements(context.Background(), time.Now(), []database.License{}, aiBridgeEnabledEnablements, coderdenttest.Keys, zeroUsageArgs)
require.NoError(t, err)
aiBridgeFeature := entitlements.Features[codersdk.FeatureAIBridge]
assert.Equal(t, codersdk.EntitlementNotEntitled, aiBridgeFeature.Entitlement)
require.NotContains(t, entitlements.Warnings, aiBridgeWarningMessage)
})
}
func TestUsageLimitFeatures(t *testing.T) {
t.Parallel()
@@ -3781,8 +3621,6 @@ func TestAIGovernanceAddon(t *testing.T) {
// AI Bridge should be enabled without warning when addon is present.
aibridgeFeature := entitlements.Features[codersdk.FeatureAIBridge]
require.True(t, aibridgeFeature.Enabled, "AI Bridge should be enabled when addon is present and enablements are set")
aiBridgeWarningMessage := "The AI Governance add-on is required to use AI Gateway. Please reach out to your account team or sales@coder.com to learn more."
require.NotContains(t, entitlements.Warnings, aiBridgeWarningMessage, "AI Bridge warning should not appear when AI Governance addon is present")
// require.Equal(t, codersdk.EntitlementEntitled, aibridgeFeature.Entitlement, "AI Bridge should be entitled when addon is present")