fix(billing): org default wins over provider derivation (core#2608) #2612

Merged
devops-engineer merged 3 commits from fix/2608-billing-org-default into main 2026-06-12 00:07:14 +00:00
6 changed files with 249 additions and 93 deletions
@@ -44,6 +44,7 @@ import (
"errors"
"fmt"
"log"
"strings"
"sync"
"git.moleculesai.app/molecule-ai/molecule-core/workspace-server/internal/crypto"
@@ -120,7 +121,7 @@ type BillingModeResolution struct {
WorkspaceID string `json:"workspace_id"`
ResolvedMode string `json:"resolved_mode"`
WorkspaceOverride *string `json:"workspace_override"` // nil = inherit
OrgDefault string `json:"org_default"` // RETIRED as a billing source (internal#718 P2-B); always platform_managed, kept for wire-compat
OrgDefault string `json:"org_default"` // Org-level default delivered via tenant_config / MOLECULE_LLM_BILLING_MODE. Consulted in the decision when no workspace override exists (core#2608).
Source BillingModeSource `json:"source"`
// ProviderSelection surfaces the DERIVED provider name (internal#718 P2-B)
// when the mode came from the registry derivation — the literal provider the
@@ -169,6 +170,30 @@ func isKnownBillingMode(s string) bool {
}
}
// orgDefaultForDisplay normalizes the raw org-mode string for the wire-format
// OrgDefault field. Returns the recognized lower-case value when known, else
// the closed platform default. This keeps the admin route response honest
// about which org default was actually consulted.
func orgDefaultForDisplay(orgMode string) string {
mode := strings.ToLower(strings.TrimSpace(orgMode))
if isKnownBillingMode(mode) {
return mode
}
return LLMBillingModePlatformManaged
}
// recognizedOrgDefault returns the normalized, recognized org default value
// and true when orgMode is a known billing mode (after trimming whitespace and
// lower-casing). Callers use this single normalization point so the decision
// semantics and the display value cannot drift.
func recognizedOrgDefault(orgMode string) (string, bool) {
mode := strings.ToLower(strings.TrimSpace(orgMode))
if isKnownBillingMode(mode) {
return mode, true
}
return LLMBillingModePlatformManaged, false
}
// readWorkspaceBillingOverride reads the OPTIONAL explicit operator override
// (workspaces.llm_billing_mode). Returns:
//
@@ -198,23 +223,19 @@ func readWorkspaceBillingOverride(ctx context.Context, workspaceID string) (stri
}
// ResolveLLMBillingModeDerived is the SSOT billing-mode resolver (internal#718
// P2-B). It DERIVES the provider from (runtime, model) via the provider
// registry and decides platform-vs-byok from IsPlatform(derived) — it does NOT
// read a stored LLM_PROVIDER (superseding #1966's stored-read approach) and
// does NOT read the org rung (retired, CTO 2026-05-27).
// P2-B + core#2608). It consults (in precedence order):
//
// Precedence (highest first):
//
// 1. EXPLICIT operator override (workspaces.llm_billing_mode, a recognized
// value). The only stored billing signal that survives — an escape hatch,
// not the primary signal.
// 2. DERIVE: providers.DeriveProvider(runtime, model, availableAuthEnv).
// 1. EXPLICIT workspace operator override (workspaces.llm_billing_mode).
// 2. ORG default (passed via tenant_config / MOLECULE_LLM_BILLING_MODE). A
// recognized org default wins over provider derivation so a SaaS org pinned
// to platform_managed is not flipped to byok for models whose provider is
// not the closed `platform` provider (core#2608 first-run failure).
// 3. DERIVE: providers.DeriveProvider(runtime, model, availableAuthEnv).
// - resolves to the closed `platform` provider → platform_managed
// - resolves to any other (BYOK/third-party) provider → byok ← THE FIX
// 3. DEFAULT-CLOSED: derive fails (no model, unknown runtime, unregistered or
// ambiguous model) → platform_managed (CTO "unset → platform default"). A
// derive failure NEVER silently flips a workspace to byok (which would
// strip the platform creds it may legitimately need).
// - resolves to any other (BYOK/third-party) provider → byok
// 4. DEFAULT-CLOSED: derive fails or org default is absent/unrecognized
// platform_managed when a platform proxy is configured, else byok on
// self-host.
//
// availableAuthEnv is the set of auth-env-var NAMES present for the workspace
// (never secret values) — the same disambiguation input DeriveProvider uses to
@@ -222,20 +243,28 @@ func readWorkspaceBillingOverride(ctx context.Context, workspaceID string) (stri
//
// A returned error never prevents a decision: ResolvedMode is always a valid
// enum value (default-closed). The error is informational (log + surface).
func ResolveLLMBillingModeDerived(ctx context.Context, workspaceID, runtime, model string, availableAuthEnv []string) (BillingModeResolution, error) {
func ResolveLLMBillingModeDerived(ctx context.Context, workspaceID, runtime, model, orgMode string, availableAuthEnv []string) (BillingModeResolution, error) {
res := BillingModeResolution{
WorkspaceID: workspaceID,
// OrgDefault is retired as a billing source (internal#718 P2-B). Kept on
// the struct for wire-compat (admin route / CP mirror) but always the
// closed constant — never consulted in the decision.
OrgDefault: LLMBillingModePlatformManaged,
// OrgDefault reflects the passed org default for wire-compat /
// observability. It is consulted in the decision when no workspace
// override exists (core#2608).
OrgDefault: orgDefaultForDisplay(orgMode),
}
// Normalize once and use the same value for both the decision and the
// empty-workspace path so callers cannot accidentally skip an org default
// because of casing or whitespace.
orgDefault, orgDefaultOK := recognizedOrgDefault(orgMode)
// Pre-provision context (no workspace row yet): no override to read, default
// closed. (DeriveProvider could still run from the passed runtime/model, but
// the no-id path historically does no DB work and the strip gate only runs
// post-create, so keep it a pure default to preserve that contract.)
// Pre-provision context (no workspace row yet): no override to read.
// A recognized org default still applies (no DB needed); otherwise default
// closed. This path is reached from ResolveLLMBillingMode with an empty id.
if workspaceID == "" {
if orgDefaultOK {
res.ResolvedMode = orgDefault
res.Source = BillingModeSourceOrgDefault
return res, nil
}
res.ResolvedMode = defaultClosedBillingMode()
res.Source = BillingModeSourceDerivedDefault
return res, nil
@@ -255,7 +284,19 @@ func ResolveLLMBillingModeDerived(ctx context.Context, workspaceID, runtime, mod
return res, nil
}
// Precedence 2: DERIVE the provider from (runtime, model).
// Precedence 2: org default. A recognized org-level default (delivered via
// tenant_config / MOLECULE_LLM_BILLING_MODE) wins over provider derivation
// so a SaaS org pinned to platform_managed does not get flipped to byok for
// models whose provider is not the closed `platform` provider. This closes
// core#2608: fresh SaaS tenants with platform_managed org default failed
// provision with MISSING_BYOK_CREDENTIAL because derivation ran first.
if orgDefaultOK {
res.ResolvedMode = orgDefault
res.Source = BillingModeSourceOrgDefault
return res, nil
}
// Precedence 3: DERIVE the provider from (runtime, model).
manifest, mErr := providerRegistry()
if mErr != nil || manifest == nil {
// Registry unavailable (malformed embedded YAML — a build-time defect the
@@ -309,11 +350,9 @@ func ResolveLLMBillingModeDerived(ctx context.Context, workspaceID, runtime, mod
// branch. The error is informational: log it, surface it to operators, but
// the strip-gate decision is already safe.
func ResolveLLMBillingMode(ctx context.Context, workspaceID, orgMode string) (BillingModeResolution, error) {
_ = orgMode // org rung retired (internal#718 P2-B); parameter ignored.
if workspaceID == "" {
// Pre-provision context (templating, validation): default closed, no DB.
return ResolveLLMBillingModeDerived(ctx, "", "", "", nil)
return ResolveLLMBillingModeDerived(ctx, "", "", "", orgMode, nil)
}
// Precedence 1: explicit operator override. Read it FIRST so an overridden
@@ -323,7 +362,7 @@ func ResolveLLMBillingMode(ctx context.Context, workspaceID, orgMode string) (Bi
if mode, ok, err := readWorkspaceBillingOverride(ctx, workspaceID); err != nil {
return BillingModeResolution{
WorkspaceID: workspaceID,
OrgDefault: LLMBillingModePlatformManaged,
OrgDefault: orgDefaultForDisplay(orgMode),
ResolvedMode: LLMBillingModePlatformManaged,
Source: BillingModeSourceConstantFallback,
}, err
@@ -331,7 +370,7 @@ func ResolveLLMBillingMode(ctx context.Context, workspaceID, orgMode string) (Bi
m := mode
return BillingModeResolution{
WorkspaceID: workspaceID,
OrgDefault: LLMBillingModePlatformManaged,
OrgDefault: orgDefaultForDisplay(orgMode),
ResolvedMode: mode,
WorkspaceOverride: &m,
Source: BillingModeSourceWorkspaceOverride,
@@ -349,7 +388,7 @@ func ResolveLLMBillingMode(ctx context.Context, workspaceID, orgMode string) (Bi
// independently-callable SSOT rather than splitting its precedence across two
// functions.
runtime, model, authEnv := readWorkspaceDeriveInputs(ctx, workspaceID)
return ResolveLLMBillingModeDerived(ctx, workspaceID, runtime, model, authEnv)
return ResolveLLMBillingModeDerived(ctx, workspaceID, runtime, model, orgMode, authEnv)
}
// readWorkspaceDeriveInputs loads the workspace's stored runtime + selected
@@ -1,18 +1,18 @@
package handlers
// llm_billing_mode_derived_test.go — tests for the DERIVED billing-mode
// resolver (internal#718 P2-B). The platform-vs-byok decision now DERIVES the
// provider from (runtime, model) via the provider registry and keys off
// IsPlatform(derived) — it does NOT read a stored LLM_PROVIDER (supersedes
// #1966's stored-read approach) and does NOT read the org rung (retired,
// CTO 2026-05-27). `workspaces.llm_billing_mode` survives ONLY as an optional
// explicit operator override (first precedence).
// resolver (internal#718 P2-B + core#2608). The platform-vs-byok decision
// consults (1) explicit workspace override, (2) org default, and only then
// (3) derives the provider from (runtime, model). The org rung is restored
// above derivation so a SaaS org pinned to platform_managed is not flipped to
// byok for models whose provider is not the closed `platform` provider.
//
// This file pins the explicit BEHAVIOR DELTA the RFC's P2 calls out:
// - platform-derived (or unset → platform default) → platform_managed (UNCHANGED)
// - non-platform-derived → byok (THE FIX — the Reno leak class)
// - explicit override → wins over derive
// - derive error / unregistered → platform_managed (default-closed)
// - workspace override → wins over everything
// - org default (platform_managed/byok/disabled) → wins over derive
// - platform-derived (or unset → platform default) → platform_managed
// - non-platform-derived → byok (when org default is absent)
// - derive error / unregistered → platform_managed (default-closed)
import (
"context"
@@ -180,7 +180,7 @@ func TestResolveLLMBillingModeDerived_BehaviorDelta(t *testing.T) {
mock := setupTestDB(t)
expectOverrideQuery(mock, wsID, c.override)
res, err := ResolveLLMBillingModeDerived(ctx, wsID, c.runtime, c.model, c.authEnv)
res, err := ResolveLLMBillingModeDerived(ctx, wsID, c.runtime, c.model, "", c.authEnv)
if (err != nil) != c.wantErr {
t.Fatalf("err: got %v wantErr=%v", err, c.wantErr)
}
@@ -215,7 +215,7 @@ func TestResolveLLMBillingModeDerived_OverrideDBError_DefaultClosed(t *testing.T
WithArgs(wsID).
WillReturnError(errors.New("connection refused"))
res, err := ResolveLLMBillingModeDerived(ctx, wsID, "claude-code", "kimi-for-coding", nil)
res, err := ResolveLLMBillingModeDerived(ctx, wsID, "claude-code", "kimi-for-coding", "", nil)
if err == nil {
t.Fatalf("expected propagated DB error, got nil")
}
@@ -234,7 +234,7 @@ func TestResolveLLMBillingModeDerived_EmptyWorkspaceID_PlatformDefault(t *testin
withProxyConfigured(t) // SaaS context.
ctx := context.Background()
mock := setupTestDB(t) // no query expected
res, err := ResolveLLMBillingModeDerived(ctx, "", "claude-code", "kimi-for-coding", nil)
res, err := ResolveLLMBillingModeDerived(ctx, "", "claude-code", "kimi-for-coding", "", nil)
if err != nil {
t.Fatalf("unexpected err: %v", err)
}
@@ -265,7 +265,7 @@ func TestResolveLLMBillingModeDerived_SelfHost_DefaultsBYOK(t *testing.T) {
t.Run("unset_model_defaults_byok_on_selfhost", func(t *testing.T) {
mock := setupTestDB(t)
expectOverrideQuery(mock, wsID, "") // NULL override
res, err := ResolveLLMBillingModeDerived(ctx, wsID, "claude-code", "", nil)
res, err := ResolveLLMBillingModeDerived(ctx, wsID, "claude-code", "", "", nil)
if err != nil {
t.Fatalf("unexpected err: %v", err)
}
@@ -283,7 +283,7 @@ func TestResolveLLMBillingModeDerived_SelfHost_DefaultsBYOK(t *testing.T) {
t.Run("unregistered_model_defaults_byok_on_selfhost", func(t *testing.T) {
mock := setupTestDB(t)
expectOverrideQuery(mock, wsID, "")
res, err := ResolveLLMBillingModeDerived(ctx, wsID, "claude-code", "totally-made-up-model-xyz", nil)
res, err := ResolveLLMBillingModeDerived(ctx, wsID, "claude-code", "totally-made-up-model-xyz", "", nil)
if err != nil {
t.Fatalf("unexpected err: %v", err)
}
@@ -300,7 +300,7 @@ func TestResolveLLMBillingModeDerived_SelfHost_DefaultsBYOK(t *testing.T) {
t.Run("empty_workspace_id_defaults_byok_on_selfhost", func(t *testing.T) {
mock := setupTestDB(t) // no query expected (pre-provision path)
res, err := ResolveLLMBillingModeDerived(ctx, "", "claude-code", "kimi-for-coding", nil)
res, err := ResolveLLMBillingModeDerived(ctx, "", "claude-code", "kimi-for-coding", "", nil)
if err != nil {
t.Fatalf("unexpected err: %v", err)
}
@@ -317,7 +317,7 @@ func TestResolveLLMBillingModeDerived_SelfHost_DefaultsBYOK(t *testing.T) {
// no-proxy default only governs the derive-failure fallback.
mock := setupTestDB(t)
expectOverrideQuery(mock, wsID, LLMBillingModePlatformManaged)
res, err := ResolveLLMBillingModeDerived(ctx, wsID, "claude-code", "", nil)
res, err := ResolveLLMBillingModeDerived(ctx, wsID, "claude-code", "", "", nil)
if err != nil {
t.Fatalf("unexpected err: %v", err)
}
@@ -332,3 +332,88 @@ func TestResolveLLMBillingModeDerived_SelfHost_DefaultsBYOK(t *testing.T) {
}
})
}
// TestResolveLLMBillingModeDerived_OrgDefaultWins asserts that a recognized
// org default (delivered via MOLECULE_LLM_BILLING_MODE / tenant_config) takes
// precedence over provider derivation when no workspace override exists. This
// closes core#2608: fresh SaaS tenants with org default platform_managed failed
// concierge provision because a non-platform-derived model flipped the workspace
// to byok before any credential existed.
func TestResolveLLMBillingModeDerived_OrgDefaultWins(t *testing.T) {
withProxyConfigured(t) // SaaS context.
ctx := context.Background()
const wsID = "66666666-6666-6666-6666-666666666666"
t.Run("org_platform_managed_wins_over_non_platform_derive", func(t *testing.T) {
mock := setupTestDB(t)
expectOverrideQuery(mock, wsID, "") // NULL override
res, err := ResolveLLMBillingModeDerived(ctx, wsID, "claude-code", "kimi-for-coding", LLMBillingModePlatformManaged, nil)
if err != nil {
t.Fatalf("unexpected err: %v", err)
}
if res.ResolvedMode != LLMBillingModePlatformManaged {
t.Errorf("org default platform_managed: got %q want platform_managed", res.ResolvedMode)
}
if res.Source != BillingModeSourceOrgDefault {
t.Errorf("source: got %q want %q", res.Source, BillingModeSourceOrgDefault)
}
if err := mock.ExpectationsWereMet(); err != nil {
t.Errorf("sqlmock expectations: %v", err)
}
})
t.Run("org_byok_wins_over_platform_derive", func(t *testing.T) {
mock := setupTestDB(t)
expectOverrideQuery(mock, wsID, "") // NULL override
res, err := ResolveLLMBillingModeDerived(ctx, wsID, "claude-code", "anthropic/claude-opus-4-7", LLMBillingModeBYOK, nil)
if err != nil {
t.Fatalf("unexpected err: %v", err)
}
if res.ResolvedMode != LLMBillingModeBYOK {
t.Errorf("org default byok: got %q want byok", res.ResolvedMode)
}
if res.Source != BillingModeSourceOrgDefault {
t.Errorf("source: got %q want %q", res.Source, BillingModeSourceOrgDefault)
}
if err := mock.ExpectationsWereMet(); err != nil {
t.Errorf("sqlmock expectations: %v", err)
}
})
t.Run("workspace_override_still_wins_over_org_default", func(t *testing.T) {
mock := setupTestDB(t)
expectOverrideQuery(mock, wsID, LLMBillingModeBYOK)
res, err := ResolveLLMBillingModeDerived(ctx, wsID, "claude-code", "anthropic/claude-opus-4-7", LLMBillingModePlatformManaged, nil)
if err != nil {
t.Fatalf("unexpected err: %v", err)
}
if res.ResolvedMode != LLMBillingModeBYOK {
t.Errorf("workspace override must beat org default: got %q want byok", res.ResolvedMode)
}
if res.Source != BillingModeSourceWorkspaceOverride {
t.Errorf("source: got %q want %q", res.Source, BillingModeSourceWorkspaceOverride)
}
if err := mock.ExpectationsWereMet(); err != nil {
t.Errorf("sqlmock expectations: %v", err)
}
})
t.Run("unrecognized_org_default_ignored", func(t *testing.T) {
mock := setupTestDB(t)
expectOverrideQuery(mock, wsID, "") // NULL override
res, err := ResolveLLMBillingModeDerived(ctx, wsID, "claude-code", "kimi-for-coding", "not-a-real-mode", nil)
if err != nil {
t.Fatalf("unexpected err: %v", err)
}
if res.ResolvedMode != LLMBillingModeBYOK {
t.Errorf("unrecognized org default ignored: got %q want byok", res.ResolvedMode)
}
if res.Source != BillingModeSourceDerivedProvider {
t.Errorf("source: got %q want %q", res.Source, BillingModeSourceDerivedProvider)
}
if err := mock.ExpectationsWereMet(); err != nil {
t.Errorf("sqlmock expectations: %v", err)
}
})
}
@@ -57,12 +57,12 @@ func expectDeriveShimQueries(m sqlmock.Sqlmock, wsID, runtime, model string) {
nullOverride()
}
// internal#718 P2-B: org rung retired. A no-override workspace's mode is now
// DERIVED from its stored (runtime, model). A claude-code workspace with a
// non-platform-deriving model (kimi-for-coding) resolves byok via
// derived_provider — NOT the old "inherit org default".
// internal#718 P2-B + core#2608: with no workspace override and an unset org
// default, the mode is DERIVED from its stored (runtime, model). A claude-code
// workspace with a non-platform-deriving model (kimi-for-coding) resolves byok
// via derived_provider.
func TestGetWorkspaceLLMBillingMode_HappyPath_DerivesByokFromModel(t *testing.T) {
t.Setenv("MOLECULE_LLM_BILLING_MODE", LLMBillingModeBYOK) // org env ignored now
t.Setenv("MOLECULE_LLM_BILLING_MODE", "") // no org default; derivation decides
mock := setupTestDB(t)
expectDeriveShimQueries(mock, testWSID, "claude-code", "kimi-for-coding")
@@ -144,7 +144,7 @@ func TestPutWorkspaceLLMBillingMode_SetByok(t *testing.T) {
}
func TestPutWorkspaceLLMBillingMode_ExplicitNullClearsOverride(t *testing.T) {
t.Setenv("MOLECULE_LLM_BILLING_MODE", LLMBillingModePlatformManaged)
t.Setenv("MOLECULE_LLM_BILLING_MODE", "") // no org default; derivation decides
withProxyConfigured(t) // SaaS context: cleared override → derived_default → platform_managed.
mock := setupTestDB(t)
mock.ExpectExec(`UPDATE workspaces SET llm_billing_mode = NULL WHERE id = \$1`).
@@ -1,12 +1,12 @@
package handlers
// llm_billing_mode_test.go — tests for the LEGACY-signature resolver
// ResolveLLMBillingMode after internal#718 P2-B. The org rung is RETIRED: the
// legacy shim now reads the explicit override first, then DERIVES the provider
// from the workspace's stored (runtime, model) via the registry (no org
// default). The dedicated derived-resolver cases live in
// llm_billing_mode_derived_test.go; this file pins the legacy shim's DB-read
// sequence + that it routes through the derived semantics.
// ResolveLLMBillingMode after internal#718 P2-B + core#2608. The legacy shim
// reads the explicit override first, then the org default, then DERIVES the
// provider from the workspace's stored (runtime, model). The dedicated
// derived-resolver cases live in llm_billing_mode_derived_test.go; this file
// pins the legacy shim's DB-read sequence + that it routes through the derived
// semantics when no org default is present.
import (
"context"
@@ -57,6 +57,7 @@ func TestResolveLLMBillingMode_LegacyShimDerives(t *testing.T) {
}
type tc struct {
name string
orgMode string
setupMock func(m sqlmock.Sqlmock)
want want
wantErr bool
@@ -64,9 +65,9 @@ func TestResolveLLMBillingMode_LegacyShimDerives(t *testing.T) {
cases := []tc{
{
// Explicit override still wins (first precedence; only stored signal
// that survives P2-B). No runtime/secrets read needed.
name: "explicit_override_byok_wins",
// Explicit override still wins (first precedence).
name: "explicit_override_byok_wins",
orgMode: "",
setupMock: func(m sqlmock.Sqlmock) {
m.ExpectQuery(`SELECT llm_billing_mode FROM workspaces WHERE id = \$1`).
WithArgs(wsID).
@@ -75,34 +76,45 @@ func TestResolveLLMBillingMode_LegacyShimDerives(t *testing.T) {
want: want{mode: LLMBillingModeBYOK, source: BillingModeSourceWorkspaceOverride, hasOverride: true},
},
{
// No override + a non-platform-deriving model → byok via derive (THE
// FIX: pre-P2 this was platform_managed via the org rung).
name: "no_override_derives_byok_from_model",
// No override + org default platform_managed wins over non-platform derive.
name: "org_default_platform_managed_wins_over_derive",
orgMode: LLMBillingModePlatformManaged,
setupMock: func(m sqlmock.Sqlmock) {
expectLegacyShimQueries(m, wsID, "claude-code", "kimi-for-coding")
},
want: want{mode: LLMBillingModePlatformManaged, source: BillingModeSourceOrgDefault, hasOverride: false},
},
{
// No override + no org default + non-platform-deriving model → byok via derive.
name: "no_override_derives_byok_from_model",
orgMode: "",
setupMock: func(m sqlmock.Sqlmock) {
expectLegacyShimQueries(m, wsID, "claude-code", "kimi-for-coding")
},
want: want{mode: LLMBillingModeBYOK, source: BillingModeSourceDerivedProvider, hasOverride: false},
},
{
// No override + a platform-namespaced model → platform_managed (UNCHANGED).
name: "no_override_derives_platform_from_model",
// No override + no org default + platform-namespaced model → platform_managed.
name: "no_override_derives_platform_from_model",
orgMode: "",
setupMock: func(m sqlmock.Sqlmock) {
expectLegacyShimQueries(m, wsID, "claude-code", "anthropic/claude-opus-4-7")
},
want: want{mode: LLMBillingModePlatformManaged, source: BillingModeSourceDerivedProvider, hasOverride: false},
},
{
// No override + no model → derived_default → platform_managed (unset → platform).
name: "no_override_no_model_platform_default",
// No override + no org default + no model → derived_default → platform_managed.
name: "no_override_no_model_platform_default",
orgMode: "",
setupMock: func(m sqlmock.Sqlmock) {
expectLegacyShimQueries(m, wsID, "claude-code", "")
},
want: want{mode: LLMBillingModePlatformManaged, source: BillingModeSourceDerivedDefault, hasOverride: false},
},
{
// Garbled override is NOT honored — falls through to derive
// (default-closed). Here no model → platform default.
name: "garbled_override_falls_through_to_derive_default_closed",
// Garbled override is NOT honored — falls through to org default if present.
name: "garbled_override_falls_through_to_org_default",
orgMode: LLMBillingModePlatformManaged,
setupMock: func(m sqlmock.Sqlmock) {
// override read 1 (garbled → not honored), runtime, secrets,
// override read 2 (garbled again, derived resolver re-check).
@@ -119,11 +131,12 @@ func TestResolveLLMBillingMode_LegacyShimDerives(t *testing.T) {
WithArgs(wsID).
WillReturnRows(sqlmock.NewRows([]string{"llm_billing_mode"}).AddRow("byokk"))
},
want: want{mode: LLMBillingModePlatformManaged, source: BillingModeSourceDerivedDefault, hasOverride: false},
want: want{mode: LLMBillingModePlatformManaged, source: BillingModeSourceOrgDefault, hasOverride: false},
},
{
// DB error on the override read → default-closed + propagated error.
name: "override_db_error_default_closed_with_error",
name: "override_db_error_default_closed_with_error",
orgMode: "",
setupMock: func(m sqlmock.Sqlmock) {
m.ExpectQuery(`SELECT llm_billing_mode FROM workspaces WHERE id = \$1`).
WithArgs(wsID).
@@ -139,8 +152,7 @@ func TestResolveLLMBillingMode_LegacyShimDerives(t *testing.T) {
mock := setupTestDB(t)
c.setupMock(mock)
// orgMode arg is retired/ignored; pass a value to prove it has no effect.
res, err := ResolveLLMBillingMode(ctx, wsID, LLMBillingModeBYOK)
res, err := ResolveLLMBillingMode(ctx, wsID, c.orgMode)
if (err != nil) != c.wantErr {
t.Fatalf("err: got %v wantErr=%v", err, c.wantErr)
}
@@ -161,18 +173,37 @@ func TestResolveLLMBillingMode_LegacyShimDerives(t *testing.T) {
}
// TestResolveLLMBillingMode_EmptyWorkspaceID_PlatformDefault: pre-provision
// (no workspace id) defaults closed with no DB read (org rung retired, so the
// old "org_only" behavior is gone — it's now the platform default).
// (no workspace id) defaults closed with no DB read. An org default is still
// honored in this path (it is purely a string decision, no DB needed).
func TestResolveLLMBillingMode_EmptyWorkspaceID_PlatformDefault(t *testing.T) {
withProxyConfigured(t) // SaaS context.
ctx := context.Background()
mock := setupTestDB(t) // no DB read expected
res, err := ResolveLLMBillingMode(ctx, "", "")
if err != nil {
t.Fatalf("unexpected err: %v", err)
}
if res.ResolvedMode != LLMBillingModePlatformManaged {
t.Errorf("empty ws id must default platform_managed, got %q", res.ResolvedMode)
}
if err := mock.ExpectationsWereMet(); err != nil {
t.Errorf("sqlmock expectations: %v", err)
}
}
func TestResolveLLMBillingMode_EmptyWorkspaceID_OrgDefaultHonored(t *testing.T) {
withProxyConfigured(t)
ctx := context.Background()
mock := setupTestDB(t) // no DB read expected
res, err := ResolveLLMBillingMode(ctx, "", LLMBillingModeBYOK)
if err != nil {
t.Fatalf("unexpected err: %v", err)
}
if res.ResolvedMode != LLMBillingModePlatformManaged {
t.Errorf("empty ws id must default platform_managed, got %q", res.ResolvedMode)
if res.ResolvedMode != LLMBillingModeBYOK {
t.Errorf("empty ws id with org byok: got %q want byok", res.ResolvedMode)
}
if res.Source != BillingModeSourceOrgDefault {
t.Errorf("source: got %q want %q", res.Source, BillingModeSourceOrgDefault)
}
if err := mock.ExpectationsWereMet(); err != nil {
t.Errorf("sqlmock expectations: %v", err)
@@ -205,7 +236,7 @@ func TestResolveLLMBillingMode_ResolvedModeIsAlwaysValid(t *testing.T) {
WithArgs(wsID).
WillReturnRows(sqlmock.NewRows([]string{"llm_billing_mode"}).AddRow("totally-bogus"))
res, err := ResolveLLMBillingMode(ctx, wsID, "also-bogus")
res, err := ResolveLLMBillingMode(ctx, wsID, "")
if err != nil {
t.Fatalf("unexpected err: %v", err)
}
@@ -1065,11 +1065,11 @@ func effectiveModelForBilling(model string, envVars map[string]string) string {
type platformLLMEnvResult struct {
ResolvedMode string
HasUsableLLMCred bool
// Source records which layer decided the mode (internal#718 P2-B):
// derived_provider (registry derivation), derived_default (derive failed →
// platform default), workspace_override (explicit operator pin), or
// constant_fallback (DB error). Surfaced for observability + asserted by the
// behavior-delta tests so a regression of "derived, not stored" flips red.
// Source records which layer decided the mode (internal#718 P2-B + core#2608):
// workspace_override, org_default, derived_provider (registry derivation),
// derived_default (derive failed → platform default), or constant_fallback
// (DB error). Surfaced for observability + asserted by the behavior-delta
// tests so a regression of "derived, not stored" flips red.
Source BillingModeSource
}
@@ -1092,6 +1092,7 @@ func applyPlatformManagedLLMEnv(ctx context.Context, envVars map[string]string,
// of recognized provider auth-env-var NAMES present in envVars (the same
// disambiguation input the registry uses to split oauth-vs-api). The org-env
// MOLECULE_LLM_BILLING_MODE is NO LONGER read into the decision (retired).
orgMode := strings.ToLower(strings.TrimSpace(os.Getenv("MOLECULE_LLM_BILLING_MODE")))
availableAuthEnv := availableAuthEnvNames(envVars)
// molecule-core#1994: derive billing mode from the EFFECTIVE model, not the
// raw payload.Model. On a re-provision (restart/resume/auto-restart) the
@@ -1106,7 +1107,7 @@ func applyPlatformManagedLLMEnv(ctx context.Context, envVars map[string]string,
// read-path's — keeping the two resolvers in parity (the #1994 regression
// guard test asserts this).
effectiveModel := effectiveModelForBilling(model, envVars)
res, resolveErr := ResolveLLMBillingModeDerived(ctx, workspaceID, runtime, effectiveModel, availableAuthEnv)
res, resolveErr := ResolveLLMBillingModeDerived(ctx, workspaceID, runtime, effectiveModel, orgMode, availableAuthEnv)
if resolveErr != nil {
// resolveErr != nil ⇒ resolver hit a DB error AND already defaulted
// res.ResolvedMode to platform_managed. Log + proceed; the safe default
@@ -1112,7 +1112,7 @@ func TestApplyPlatformManagedLLMEnv_NoopsOutsidePlatformManaged(t *testing.T) {
WithArgs(wsID).
WillReturnRows(sqlmock.NewRows([]string{"llm_billing_mode"}).AddRow(nil))
t.Setenv("MOLECULE_LLM_BILLING_MODE", "platform_managed") // org env ignored now
t.Setenv("MOLECULE_LLM_BILLING_MODE", "") // no org default; derivation decides
t.Setenv("MOLECULE_LLM_BASE_URL", "https://api.example.test/api/v1/internal/llm/openai/v1")
t.Setenv("MOLECULE_LLM_USAGE_TOKEN", "tenant-admin-token")
@@ -1273,7 +1273,7 @@ func TestApplyPlatformManagedLLMEnv_DERIVED_PlatformModelKeepsPlatformCreds(t *t
WithArgs(wsID).
WillReturnRows(sqlmock.NewRows([]string{"llm_billing_mode"}).AddRow(nil)) // NO override → derive
t.Setenv("MOLECULE_LLM_BILLING_MODE", LLMBillingModeBYOK) // org env IGNORED now
t.Setenv("MOLECULE_LLM_BILLING_MODE", "") // no org default; derivation decides
t.Setenv("MOLECULE_LLM_BASE_URL", "https://api.example.test/api/v1/internal/llm/openai/v1")
t.Setenv("MOLECULE_LLM_ANTHROPIC_BASE_URL", "https://api.example.test/api/v1/internal/llm/anthropic")
t.Setenv("MOLECULE_LLM_USAGE_TOKEN", "tenant-admin-token")
@@ -1314,7 +1314,7 @@ func TestApplyPlatformManagedLLMEnv_DERIVED_ByokNoCredentialFailsClosed(t *testi
WithArgs(wsID).
WillReturnRows(sqlmock.NewRows([]string{"llm_billing_mode"}).AddRow(nil)) // NO override → derive
t.Setenv("MOLECULE_LLM_BILLING_MODE", LLMBillingModePlatformManaged) // org env IGNORED now
t.Setenv("MOLECULE_LLM_BILLING_MODE", "") // no org default; derivation decides
t.Setenv("MOLECULE_LLM_BASE_URL", "https://api.example.test/api/v1/internal/llm/openai/v1")
t.Setenv("MOLECULE_LLM_USAGE_TOKEN", "tenant-admin-token")
@@ -1353,7 +1353,7 @@ func TestApplyPlatformManagedLLMEnv_DERIVED_UnsetModelPlatformDefault(t *testing
WithArgs(wsID).
WillReturnRows(sqlmock.NewRows([]string{"llm_billing_mode"}).AddRow(nil)) // NO override
t.Setenv("MOLECULE_LLM_BILLING_MODE", LLMBillingModeBYOK) // org env IGNORED now
t.Setenv("MOLECULE_LLM_BILLING_MODE", "") // no org default; derivation decides
t.Setenv("MOLECULE_LLM_BASE_URL", "https://api.example.test/api/v1/internal/llm/openai/v1")
t.Setenv("MOLECULE_LLM_ANTHROPIC_BASE_URL", "https://api.example.test/api/v1/internal/llm/anthropic")
t.Setenv("MOLECULE_LLM_USAGE_TOKEN", "tenant-admin-token")