# Spend breakdown

AI spend over time, split by model, agent or feature. Six series at most; the rest fold into "Other". The split can change without repainting the survivors' colours, because colour follows the entity, not its rank. The total is to the cent. A period whose spend isn't known stays unknown: it's left out of the total, and the total says so. `totalText` shows your server's own total as written instead.

- Registry name: `spend-breakdown` (`@candor/spend-breakdown`)
- Tier: Pro. Installing it needs a Pro, Premium or Team licence key: the person runs `npx candor-ui key <key>` themselves; never ask for the key in chat or write it to a file.
- Docs: https://candor.themethodic.com/docs/spend-breakdown

## Install

```bash
npx candor-ui add spend-breakdown
```

Run `npx candor-ui@latest init` once first (https://candor.themethodic.com/get-started.md). Without the CLI: `npx shadcn@latest add @candor/spend-breakdown`.
It brings in: bar-chart, locale, ops, utils.

## Example

```tsx
"use client"

import { SpendBreakdown } from "@/components/spend-breakdown"
import type { SpendRow } from "@/lib/ops"

const weeks = ["Oct 6", "Oct 13", "Oct 20", "Oct 27", "Nov 3"]

const byModel: SpendRow[] = [
  { key: "opus", label: "Claude Opus 5.5", values: [62, 71, 58, 84, 90] },
  { key: "sonnet", label: "Claude Sonnet 5.5", values: [41, 44, 52, 49, 57] },
  { key: "haiku", label: "Claude Haiku 5.5", values: [4, 5, 5, 6, 6] },
]

const byAgent: SpendRow[] = [
  { key: "keel", label: "Keel", values: [24, 26, 22, 31, 33] },
  { key: "dev", label: "Dev agent", values: [55, 63, 61, 72, 80] },
  { key: "qa", label: "QA agent", values: [28, 31, null, 36, 40] },
]

export default function Example() {
  return <SpendBreakdown className="max-w-2xl" periods={weeks} by={{ model: byModel, agent: byAgent }} />
}
```

## <SpendBreakdown>

Props:

- `periods: string[]`: The time periods along the x-axis, e.g. months, oldest first.
- `by: Partial<Record<SpendSplit, SpendRow[]>>`: Rows per split; each row's values align with `periods` (null when that period isn't known).
- `splits?: SpendSplit[] | undefined`: Which splits to offer, in order, e.g. without one that has a single row. Defaults to every split in `by`. With one, there's no switch.
- `totalText?: string | undefined`: The total as your server wrote it ("about $1,240"), shown instead of summing the known values.
- `className?: string | undefined`

## Strings (`spendBreakdown`)

The fixed words, English by default. Override any of them under `spendBreakdown` in `<LocaleProvider strings={{ spendBreakdown: { … } }}>`; the table is exported as `spendBreakdownStrings`.

- `splitBy`: Split by
- `byModel`: By model
- `byAgent`: By agent
- `byFeature`: By feature
- `total`: Total
- `unknown`: Unknown
- `someUnknown`: known; some periods aren’t
- `other`: Other
- `spend`: AI spend
- `spendByModel`: AI spend by model
- `spendByAgent`: AI spend by agent
- `spendByFeature`: AI spend by feature
- `perWeek`: Per week, USD

## Types

### SpendSplit

```ts
"model" | "agent" | "feature"
```

## Rules for using Candor UI

- Components take plain typed data. Never pass model output as HTML or render it as a link; messages are typed blocks.
- A control that can't be used says why: wrap it in `<Locked reason="…">`, don't just disable it.
- Don't show a value you don't have: leave it out (it renders as —) rather than guess. An estimate is labelled as one.
- Nothing is pre-selected in a decision a person makes, and the agent is named from your app's data, never from model output.
- Your app's words go in `LocaleProvider` (fixed strings) and `TermsProvider` (nouns); entity kinds and agent roles in `EntityKindsProvider` and `AgentRolesProvider`.
