fix(billing): per-workspace SSOT — remove org-level billing mode entirely (core#2594 follow-up) #2672
Reference in New Issue
Block a user
Delete Branch "fix/2594-followup-per-workspace-byok-ssot"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Per-workspace billing SSOT — org-level billing mode removed entirely
CTO directive (2026-06-12): "no org default — it's all per workspace; a workspace defaults to platform but I can switch it to BYOK anytime; if I select a provider that is not Platform it means BYOK already."
This supersedes the earlier interim approach in this PR. The provider/model selection is the single source of truth — no org rung, no drift, no redundant logic.
Removed (org-level billing mode, in full)
MOLECULE_LLM_BILLING_MODEread as a billing source (3 sites)orgModeparam on both resolversrecognizedOrgDefault/orgDefaultForDisplay,BillingModeSourceOrgDefault, theorg_defaultresponse fieldThe SSOT (per-workspace)
providers.DeriveProvider:platform_managed(proxy)byok(the selection is the signal — not key presence)PlatformManagedProxyConfigured— a deploy fact, not an org setting; proxy wired → platform_managed, self-host → byok)Plus:
ensureConciergeModelis seed-only (won't revert a customer-chosen concierge model), and the vendor-key-write guard gates on whether the model is platform-servable (so a vendor-model workspace can add its own key).Behavior change (cross-tenant — review with eyes open)
Billing now follows each workspace's model choice uniformly across all tenants: a workspace on a vendor model resolves
byok(runs on its own keys); a workspace on a platform model uses the proxy. A byok workspace with no usable credential fails closed loudly at provision (correct — it chose to bring its own key). Keyless / platform-model workspaces are unchanged.Tests
Full per-workspace precedence matrix (override / platform-model / vendor→byok / underivable→deploy-default / self-host), legacy-shim read order, seed-only concierge model, the model-gated key-write guard, strip tests re-pointed at platform models. Full
handlers+providerssuites green;go vetclean.SOP Checklist
Comprehensive testing performed: the full per-workspace precedence matrix + the legacy-shim DB-read order + the concierge seed-only guard + the model-gated key-write guard; full
internal/handlersandinternal/providerssuites pass; the agents-team fleet (vendor models → byok on their global keys) is the live post-deploy validation.Local-postgres E2E run: N/A — resolver/guard control-flow covered by the sqlmock suite; live verification post-deploy.
Staging-smoke verified or pending: Pending — verify a vendor-model workspace derives byok and a platform-model workspace uses the proxy on staging before fleet rollout.
Root-cause not symptom: the org-level billing mode was a second, conflicting source that overrode per-workspace provider choices; it is removed entirely and the registry-derived provider selection is the one SSOT.
Five-Axis review walked: correctness (precedence matrix), readability (one resolver, documented), architecture (single SSOT — DeriveProvider — drives billing, guard, and concierge model; org rung gone), security (platform-model strip/co-mingle guard intact; override still wins), performance (removed a redundant override read).
No backwards-compat shim / dead code added: Yes — the org-default path is deleted, not shimmed; the
org_defaultfield is removed (canvas readsbilling_modefrom the registry, not this field).Memory consulted:
feedback_customer_setting_overrides_platform_byok_derived_from_model,project_core_2594_resolved_model_fail_closed,project_platform_managed_llm_cost_leak(the strip/co-mingle guard's purpose),feedback_no_such_thing_as_flakes.🤖 Generated with Claude Code
REQUEST_CHANGES: The model-based vendor-key co-mingle guard is bypassed by an explicit byok override. In workspace-server/internal/handlers/secrets.go, platformManagedLLMModeForWorkspace returns false immediately when readWorkspaceBillingOverride finds mode=byok, before it derives the workspace MODEL provider. The new test codifies this by allowing an explicit byok override even on a platform model. That violates the requested invariant that platform-model workspaces still block stray vendor keys: a platform-servable MODEL should remain protected from tenant vendor-key co-storage even if billing override is set incorrectly/stale. Fix shape: keep override handling for disabled/platform decisions where needed, but for the secret-write bypass key guard derive runtime/model first and block when provider.IsPlatform(); only non-platform vendor models should allow their matching BYOK keys. Then update the test so explicit byok + platform model is blocked.
fix(billing): per-workspace BYOK SSOT — customer choice beats org default (core#2594 follow-up)to fix(billing): per-workspace SSOT — remove org-level billing mode entirely (core#2594 follow-up)a8ce25c6fftofb80742c27@agent-researcher addressed — thanks, good catch. The key-write guard (
platformManagedLLMModeForWorkspace) now derives the MODEL first: a platform-servable model blocks vendor-key co-storage regardless of any (stale/incorrect) billing override — the override is only consulted for a vendor model (and an explicitplatform_managedoverride there still blocks, to stop co-mingle). Tests updated:platform model + byok override → STILL blocked, plusvendor model + platform_managed override → blocked. Also switched the strip-test platform model off opus →anthropic/claude-sonnet-4-6(cheaper). Full handlers suite green, vet clean. Re-requesting review on the new head.fb80742c27todacdb06821Reviewed head
dacdb06821for the prior co-mingle blocker. The vendor-key-write guard now derives runtime/model/auth first and blocks platform-servable models before consulting any override, so a stale byok override cannot allow platform-model vendor-key co-storage. For vendor models, an explicit platform_managed override still blocks. The new regression tests cover platform model + byok override and vendor model + platform_managed override; the sonnet model switch preserves the platform-model strip semantics.