refactor(canvas): drop org-tier inherit/source from LLM Billing Config section (internal#691 follow-up) #1931

Closed
hongming wants to merge 1 commits from refactor/drop-org-tier-llm-billing-mode-canvas into main
2 changed files with 53 additions and 85 deletions
@@ -9,10 +9,12 @@ import {
} from "@testing-library/react";
import { LLMBillingSection } from "../llm-billing-section";
// Tests for LLMBillingSection (internal#691). Locks in:
// - the section renders the resolved mode + source label
// - the dropdown maps "inherit" → PUT {mode: null}
// - the dropdown maps "byok" → PUT {mode: "byok"}
// Tests for LLMBillingSection (internal#691 + 2026-05-26 simplification).
// Post-CTO direction: no org-tier, no "inherit" option. The dropdown is
// strictly {platform_managed | byok | disabled} and every change PUTs an
// explicit mode string. Locks in:
// - the section renders the resolved mode
// - the dropdown maps each choice → PUT {mode: choice} (no null body)
// - a garbled override surfaces the warning banner
// - the post-write resolution updates the UI without a refetch
@@ -51,14 +53,13 @@ afterEach(() => {
cleanup();
});
describe("LLMBillingSection — internal#691", () => {
it("renders the resolved mode + source for an inherited workspace", async () => {
describe("LLMBillingSection — internal#691 post-org-tier-removal", () => {
it("renders the resolved mode for a constant-fallback workspace", async () => {
apiGet.mockResolvedValueOnce({
workspace_id: "ws-1",
resolved_mode: "platform_managed",
workspace_override: null,
org_default: "platform_managed",
source: "org_default",
source: "constant_fallback",
});
render(<LLMBillingSection workspaceId="ws-1" />);
@@ -68,12 +69,7 @@ describe("LLMBillingSection — internal#691", () => {
"/admin/workspaces/ws-1/llm-billing-mode",
);
});
// Resolved mode appears.
expect(screen.getByText(/Resolved mode:/i).textContent).toMatch(/platform_managed/);
// Source label appears.
expect(
screen.getByText(/inherited from org default/i),
).toBeTruthy();
expect(screen.getByText(/Current mode:/i).textContent).toMatch(/platform_managed/);
});
it('PUTs {mode: "byok"} when user picks BYOK and reflects the new resolution', async () => {
@@ -81,14 +77,12 @@ describe("LLMBillingSection — internal#691", () => {
workspace_id: "ws-2",
resolved_mode: "platform_managed",
workspace_override: null,
org_default: "platform_managed",
source: "org_default",
source: "constant_fallback",
});
apiPut.mockResolvedValueOnce({
workspace_id: "ws-2",
resolved_mode: "byok",
workspace_override: "byok",
org_default: "platform_managed",
source: "workspace_override",
});
@@ -96,7 +90,7 @@ describe("LLMBillingSection — internal#691", () => {
await waitFor(() => expect(apiGet).toHaveBeenCalled());
const select = (await screen.findByLabelText(
/llm billing mode override/i,
/llm billing mode/i,
)) as HTMLSelectElement;
fireEvent.change(select, { target: { value: "byok" } });
@@ -106,42 +100,38 @@ describe("LLMBillingSection — internal#691", () => {
{ mode: "byok" },
);
});
// Post-write resolution propagated to UI.
// Post-write resolution propagated to UI: current mode is now byok.
await waitFor(() => {
expect(
screen.getByText(/explicit override on this workspace/i),
).toBeTruthy();
expect(screen.getByText(/Current mode:/i).textContent).toMatch(/byok/);
});
});
it("PUTs {mode: null} when user picks Inherit (clears the override)", async () => {
it('PUTs {mode: "platform_managed"} when user picks platform-managed (no null/inherit path)', async () => {
apiGet.mockResolvedValueOnce({
workspace_id: "ws-3",
resolved_mode: "byok",
workspace_override: "byok",
org_default: "platform_managed",
source: "workspace_override",
});
apiPut.mockResolvedValueOnce({
workspace_id: "ws-3",
resolved_mode: "platform_managed",
workspace_override: null,
org_default: "platform_managed",
source: "org_default",
workspace_override: "platform_managed",
source: "workspace_override",
});
render(<LLMBillingSection workspaceId="ws-3" />);
await waitFor(() => expect(apiGet).toHaveBeenCalled());
const select = (await screen.findByLabelText(
/llm billing mode override/i,
/llm billing mode/i,
)) as HTMLSelectElement;
fireEvent.change(select, { target: { value: "inherit" } });
fireEvent.change(select, { target: { value: "platform_managed" } });
await waitFor(() => {
expect(apiPut).toHaveBeenCalledWith(
"/admin/workspaces/ws-3/llm-billing-mode",
{ mode: null },
{ mode: "platform_managed" },
);
});
});
@@ -151,8 +141,7 @@ describe("LLMBillingSection — internal#691", () => {
workspace_id: "ws-4",
resolved_mode: "platform_managed", // resolver fell through, default-closed
workspace_override: "byokk", // typo persisted somehow
org_default: "platform_managed",
source: "org_default",
source: "constant_fallback",
});
render(<LLMBillingSection workspaceId="ws-4" />);
@@ -3,17 +3,21 @@
// llm-billing-section.tsx — Config-tab section for the per-workspace
// llm_billing_mode override (internal#691).
//
// Post-CTO-simplification (2026-05-26 23:54Z): there is no org-tier
// default. The workspace is the unit of decision. The dropdown shows the
// workspace-level value directly; there is no "inherit" option and no
// org_default display. NULL on the server side resolves via constant
// fallback to platform_managed (the bootstrap floor) and the dropdown
// reflects that as the current value.
//
// Surfaces:
// - The currently RESOLVED mode for this workspace (the mode the
// - The currently resolved mode for this workspace (the mode the
// workspace-server's strip gate will use at next provision).
// - The org-level default (so the user sees what they're inheriting).
// - A dropdown to set / clear the workspace-level override.
// - A "source" line so operators can answer "is this inherited or
// explicit?" without DB archeology (RFC Observability hot-spot).
// - A dropdown to set the workspace-level mode.
//
// Hits:
// GET /admin/workspaces/:id/llm-billing-mode — read resolution
// PUT /admin/workspaces/:id/llm-billing-mode — write {mode: "..."|null}
// PUT /admin/workspaces/:id/llm-billing-mode — write {mode: "..."}
//
// Both routes are on the per-tenant workspace-server (same origin as the
// other canvas /admin calls). CP's proxy at /cp/admin/workspaces/:id/
@@ -26,34 +30,36 @@ import { Section } from "./form-inputs";
// Mirrors workspace-server/internal/handlers/llm_billing_mode.go::BillingModeResolution.
// Kept as a literal shape (not imported) because canvas has no Go-type bridge.
//
// Note: `source` is still part of the wire response (operator debug — see
// the Go-side comment) but the canvas does not render it post-simplification
// since there's no policy-source distinction left to surface to a workspace
// user. Operators reading the admin route directly still get the field.
export interface BillingModeResolution {
workspace_id: string;
resolved_mode: "platform_managed" | "byok" | "disabled";
// Pointer-typed on the Go side: nil = inherit, non-nil = the raw
// workspace-level override (even if garbled and falling through).
// Pointer-typed on the Go side: nil = no explicit override (constant
// fallback applies), non-nil = the raw workspace-level override (even
// if garbled and falling through).
workspace_override: string | null;
org_default: "platform_managed" | "byok" | "disabled";
source: "workspace_override" | "org_default" | "constant_fallback";
source: "workspace_override" | "constant_fallback";
}
// The dropdown emits one of these values. "inherit" is the UX-only label
// that maps to a `null` body in the PUT request.
type DropdownChoice = "inherit" | "platform_managed" | "byok" | "disabled";
// The dropdown emits one of these values. There is no "inherit" option
// after the org-tier removal — every choice maps to an explicit PUT body.
type DropdownChoice = "platform_managed" | "byok" | "disabled";
interface Props {
workspaceId: string;
}
const MODE_LABELS: Record<DropdownChoice, string> = {
inherit: "Inherit from org default",
platform_managed: "Platform-managed (uses Molecule credits)",
byok: "BYOK (your own OAuth / vendor keys)",
disabled: "Disabled (no LLM access)",
};
const MODE_DESCRIPTIONS: Record<DropdownChoice, string> = {
inherit:
"Use whichever mode is set at the organization level. Recommended unless this specific workspace needs a different billing source.",
platform_managed:
"Strip CLAUDE_CODE_OAUTH_TOKEN and vendor API keys from the workspace; route all LLM traffic through Molecule's proxy and bill your org credits.",
byok:
@@ -62,13 +68,6 @@ const MODE_DESCRIPTIONS: Record<DropdownChoice, string> = {
"Block all LLM access for this workspace. Useful for sandbox workspaces that should not consume credits or hit external providers.",
};
const SOURCE_LABELS: Record<BillingModeResolution["source"], string> = {
workspace_override: "explicit override on this workspace",
org_default: "inherited from org default",
constant_fallback:
"fallback (workspace + org defaults missing or unrecognized — defaulted to platform_managed)",
};
export function LLMBillingSection({ workspaceId }: Props) {
const [resolution, setResolution] = useState<BillingModeResolution | null>(
null,
@@ -97,23 +96,11 @@ export function LLMBillingSection({ workspaceId }: Props) {
void load();
}, [load]);
// Current dropdown selection is derived from the resolution. If the
// override is null, we show "inherit"; otherwise we mirror the raw
// workspace_override (NOT resolved_mode — that would conflate "explicit
// platform_managed override" with "inherit while org happens to be
// platform_managed", which has different semantics on the write side).
const currentChoice: DropdownChoice = (() => {
if (!resolution) return "inherit";
if (resolution.workspace_override == null) return "inherit";
const raw = resolution.workspace_override;
if (raw === "platform_managed" || raw === "byok" || raw === "disabled") {
return raw;
}
// Garbled value persisted via some external write. Show inherit so
// the user can pick a clean value; on save they'll either clear it
// (PUT null) or overwrite it with a valid one.
return "inherit";
})();
// Dropdown selection mirrors the resolved mode. With no org tier, the
// resolved mode is either the explicit workspace override or the
// constant-fallback platform_managed; either way the user can see what
// the strip gate will do at next provision and pick a different value.
const currentChoice: DropdownChoice = resolution?.resolved_mode ?? "platform_managed";
const handleChange = async (choice: DropdownChoice) => {
if (!resolution) return;
@@ -121,11 +108,9 @@ export function LLMBillingSection({ workspaceId }: Props) {
setError(null);
setSuccess(false);
try {
// "inherit" → PUT {mode: null}; otherwise → PUT {mode: choice}.
const body = choice === "inherit" ? { mode: null } : { mode: choice };
const updated = await api.put<BillingModeResolution>(
`/admin/workspaces/${workspaceId}/llm-billing-mode`,
body,
{ mode: choice },
);
setResolution(updated);
setSuccess(true);
@@ -156,24 +141,18 @@ export function LLMBillingSection({ workspaceId }: Props) {
{resolution && (
<div className="space-y-2">
<div className="text-[10px] text-ink-mid">
Resolved mode: <strong className="text-ink">{resolution.resolved_mode}</strong>{" "}
<span className="text-ink-mid">
({SOURCE_LABELS[resolution.source]})
</span>
</div>
<div className="text-[10px] text-ink-mid">
Org default: <span className="text-ink">{resolution.org_default}</span>
Current mode: <strong className="text-ink">{resolution.resolved_mode}</strong>
</div>
<label
className="block text-[10px] text-ink-mid"
htmlFor={`llm-billing-mode-${workspaceId}`}
>
Override
Mode
</label>
<select
id={`llm-billing-mode-${workspaceId}`}
aria-label="LLM billing mode override"
aria-label="LLM billing mode"
value={currentChoice}
disabled={saving}
onChange={(e) => void handleChange(e.target.value as DropdownChoice)}
@@ -209,7 +188,7 @@ export function LLMBillingSection({ workspaceId }: Props) {
>
Workspace override has a non-standard value (
<code>{resolution.workspace_override}</code>) and is being
ignored. Pick a valid mode above to clear the corrupt value.
ignored. Pick a valid mode above to overwrite the corrupt value.
</div>
)}
</div>