Budget Guard Viz
An interactive visualisation of why agent cost grows quadratically with the number of turns, and why budget guardrails are not optional. Pressing Run Agent animates two cost curves in parallel: an unguarded agent that keeps reading its growing scratchpad each turn (the red curve climbs past $47), and a guarded agent that hits its budget cap at 60% / 80% / 95% / 100% threshold markers before being terminated.
The cost model: each turn n reads its accumulated scratchpad — entry k weighs roughly S·√k tokens (S ≈ 11,200). Sum across n turns, multiply by $3 / 1M tokens, and the cumulative curve hits ≈$47 at turn 30 — the quadratic-growth insight. Four guardrail toggles (Budget cap, Loop detection, Turn limit, Human approval gate) change the guarded curve in real time — flip them all off and both agents run unchecked.
Guardrails
Cost per turn (marginal)
Model: $3/M input tokens. Turn n reads ~11200·√n accumulated scratchpad tokens.
Each agent turn reads all previous turns. Press Run to watch what happens to cost as the agent loops.
Installation
npx shadcn@latest add https://craftbits.dev/r/budget-guard-viz.jsonUsage
import { BudgetGuardViz } from "@craft-bits/viz/budget-guard-viz";
<BudgetGuardViz />Tune the cap or the y-axis ceiling:
<BudgetGuardViz budgetCap={10} yMax={80} />Subscribe to the final tally for an external chart:
<BudgetGuardViz
onSimulationComplete={({ unguardedCost, guardedCost }) => {
/* lift the final burn into an external chart */
}}
/>Understanding the component
- The chart. A standard line chart with cumulative cost on the y-axis and agent turns on the x-axis. Both curves start at
(0, 0)and trace identical paths until a guardrail intervenes. - Cost model. Each turn
nreads its accumulated scratchpad — entrykweighs roughlyS·√ktokens. Sum acrossnturns, multiply by$3 / 1M tokens, and the cumulative curve hits ≈$47at turn 30. - Guardrails. Four toggles drive the guarded curve: the budget cap stops it at the dotted budget line; the turn limit caps it at 15 turns; loop detection and human-approval are visual signals — they don't change the curve in this simulation but gate which threshold markers render.
- Threshold markers. At
60%/80%/95%/100%of the budget cap, dot markers appear on the guarded curve with their meaning: debug-log enable, observability metric, exponential backoff, hard stop. - Reduced motion. Under
prefers-reduced-motion: reduce, the path drawing collapses to a single snap, the run advances roughly three times faster, and every entrance animation disables.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
maxTurns | number | 30 | Total agent turns modelled by the simulation. |
budgetCap | number | 5 | Hard spending cap (dollars) for the guarded agent. |
yMax | number | 50 | Chart y-axis ceiling (dollars). |
defaultGuards | Partial<Record<BudgetGuardVizGuardId, boolean>> | all on | Initial state of the four guardrail toggles. |
transition | Transition | SPRINGS.snap | Override marker entrance / chart annotation transition. |
onSimulationComplete | (summary) => void | — | Fires after the run finishes with { unguardedCost, guardedCost, guardedStoppedAtTurn }. |
className | string | — | Merged onto the root via cn(). |
Accessibility
- The chart is
role="figure"with anaria-labelsummarising the final unguarded vs guarded burn so screen-reader users get the headline without needing the visual. - Two polite live regions announce (a) the running cost readout and (b) a per-frame status string with the current turn count and both agents' costs — together they let an assistive-tech user follow the simulation in real time.
- The
Run Agentbutton has a visible focus ring and disables itself while the simulation is running so repeat-clicks can't queue a second run. - The four guardrail buttons use
role="switch"witharia-checked; arrow keys andSpacetoggle them via the platform-default switch model. - Colour is never the only signal — the readout strip pairs every dollar number with its agent label, and the threshold marker text encodes the policy step (
Debug logging enabled,HARD STOP — agent terminated, …). - Motion respects
prefers-reduced-motion: reduce— the path drawing collapses to a single snap and every entrance / pulse disables.
Credits
- Extracted from:
craftingattention(app/src/lessons/primitives/systems/BudgetGuardViz.tsx). The source was a lesson component that bundled lesson narration,SvgLabelchrome, and aChallengeBtnpredict-the-outcome quiz. The viz extract keeps only the interactive simulation + cost-model + guardrail toggle panel — the quiz round was curriculum-specific and lives in the lesson source. Per-track palette tokens (--color-fail-400,--color-success-400,--color-warn-400,--color-accent-400,--color-ink-*) are remapped tovar(--cb-error)/var(--cb-success)/var(--cb-warning)/var(--cb-accent)/var(--cb-fg-*)so consumer themes repaint freely, and inlineSPRINGS.snappy/SPRINGS.gentleare re-keyed to the canonicalSPRINGS.snap/SPRINGS.smoothfrom@craft-bits/core/motion. The infinite pulse-ring animation on threshold markers was dropped —userinterface-wiki duration-max-300msrules it out, and the bouncy entrance + reach-state colour shift carry the signal.