Rebuild Piggy's interface, and give the demo book a business to describe
CI / verify (push) Successful in 4m57s
CI / publish (push) Has been skipped

Piggy answered in raw markdown, threw away every tool result it streamed,
and fought the reader's scroll on every token. The three surfaces that
made it worth having — what it read, how it reasoned, what it cost — were
all on the wire and none of them reached the screen.

The transcript is now composed of five parts under components/piggy:
answers render through streamdown, the container sticks to the bottom
without pinning the reader there, tool steps say what they read and link
to the record, and each turn carries its model and token count. Three
lifecycle bugs went with them: Stop left a permanent spinner, a truncated
stream was indistinguishable from thinking, and a failed send destroyed
the message it failed to send.

Underneath, the inference path grew timeouts, jittered retries on 429 and
5xx, tolerance of the malformed frames a 30B model emits, and an
agent_runs row per turn so chat spend is observable. The system prompt now
states that a field ending in Cents is cents — without it nemotron renders
costPerGpuHourCents: 189 as "$189 per GPU-hour", which is a 100x error on
the most scrutinised number in the room.

The demo book was arithmetically incoherent: every deal's value
contradicted its own allocation revenue by up to 3.6x, nothing had ever
closed, no customer had any paper, and the marketplace was empty. Deal
value is now derived from the allocation, the book clears 5.3% across five
blocks with one deliberately underwater, and the renewal, compliance and
agent-provenance machinery finally has rows to act on. A --clear that
deleted every obligation, SLA term and capacity request in the database
regardless of origin is scoped to the demo's own ids.

Around that: accounts have a detail page, ⌘K searches the book, Settings
can mint the API keys it always claimed to, and deploy.sh actually ships
the agent instead of silently skipping its compose profile.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
claude
2026-08-14 00:33:41 -07:00
parent 76e3caa1cb
commit 99d165b5e5
81 changed files with 21780 additions and 2250 deletions
+225 -41
View File
@@ -4,13 +4,24 @@
* The table scrolls inside its own pane on narrow screens rather than making
* the page scroll sideways; a card list would lose the column comparison that
* is the entire value of this view.
*
* The page also has to say the quiet part out loud. A blended margin in the low
* single digits reads as a thin but healthy book, while one commitment sits
* barely half sold and has not paid for itself — the totals average that away
* by construction. So the blocks whose cost is still uncovered are named above
* the table with the price their remaining hours have to fetch, rather than
* left to be reconstructed by reading a percentage column against a price
* column two columns away.
*/
import { useState } from 'react';
import { useQuery } from '@tanstack/react-query';
import { ArrowRight } from 'lucide-react';
import { AlertTriangle, ArrowRight } from 'lucide-react';
import { Link } from 'react-router-dom';
import { compactNumber, get, money, moneyExact, percent } from '@/lib/api';
import { Button, Card, CardContent, CardHeader, CardTitle, EmptyState, Skeleton, Stat } from '@/components/ui';
import { compactNumber, get, money, percent, unitPrice } from '@/lib/api';
import { PiggyAskButton } from '@/components/PiggyChat';
import { Badge, Button, Card, CardContent, CardHeader, CardTitle, EmptyState, Skeleton, Stat, cn } from '@/components/ui';
import { usePageTitle } from '@/lib/title';
import { usePiggyContext } from '@/lib/piggy-context';
interface MarginReport {
totals: {
@@ -24,18 +35,34 @@ interface MarginReport {
grossMarginPct: number | null;
marginPerAllocatedGpuHourCents: number | null;
};
blocks: {
commitmentId: string;
name: string;
gpuType: string;
gpuCount: number;
totalGpuHours: number;
soldGpuHours: number;
availableGpuHours: number;
costPerGpuHourCents: number;
utilisation: number;
breakEvenPriceCents: number | null;
}[];
blocks: MarginBlock[];
}
interface MarginBlock {
commitmentId: string;
name: string;
gpuType: string;
gpuCount: number;
totalGpuHours: number;
soldGpuHours: number;
availableGpuHours: number;
costPerGpuHourCents: number;
utilisation: number;
breakEvenPriceCents: number | null;
}
/**
* A block has covered its cost when there is nothing left to break even on.
*
* The colour on the sold-ratio column used to key off `utilisation < 0.5`,
* which is an arbitrary line: the block this page exists to flag is 55% sold
* and would have rendered as unremarkable, while a block 40% sold on cheap
* hours it has already earned back would have rendered as a problem. Break-even
* is the honest test — it is zero exactly when revenue has already covered the
* whole commitment, and null when there is nothing left to sell.
*/
function isUncovered(block: MarginBlock): boolean {
return block.breakEvenPriceCents != null && block.breakEvenPriceCents > 0;
}
export function Margin() {
@@ -44,6 +71,25 @@ export function Margin() {
queryKey: ['margin'],
queryFn: () => get<MarginReport>('/api/capacity/margin'),
});
const [focusedId, setFocusedId] = useState<string | null>(null);
const blocks = data?.blocks ?? [];
// Resolved from the current data rather than held in state, so a block that
// disappears on a refetch quietly returns Piggy to the page instead of
// leaving it pointed at a commitment nobody can see any more.
const focused = blocks.find((block) => block.commitmentId === focusedId);
// Ambient context for the dock: the block the user selected, else this page.
// Published before the early returns below, because a hook that runs only on
// the happy path is a hook that changes order the first time the query fails.
usePiggyContext(
focused
? { type: 'commitment', id: focused.commitmentId, label: focused.name }
: { type: 'page', route: '/margin', label: 'Margin' },
);
const uncovered = blocks.filter(isUncovered);
const toggleFocus = (commitmentId: string) =>
setFocusedId((current) => (current === commitmentId ? null : commitmentId));
if (isLoading) {
return <div className="flex flex-col gap-4"><Skeleton className="h-16" /><div className="grid grid-cols-2 gap-2 xl:grid-cols-4">{Array.from({ length: 4 }).map((_, index) => <Skeleton key={index} className="h-28" />)}</div><Skeleton className="h-80" /></div>;
@@ -62,7 +108,26 @@ export function Margin() {
return (
<EmptyState
title="No capacity to report on"
description="Margin is computed from capacity commitments and the allocations against them."
description="Margin is computed from capacity commitments and the allocations against them. Both are recorded on the capacity book."
/*
* A link rather than the commitment sheet itself, which is the opposite
* of the choice Overview makes and for a reason. Margin is a derived
* ledger with no other write path on it, and recording a block is only
* step one — the seller's next move is to match and allocate it, which
* is on /capacity too. Sending the reader there puts them in front of
* the whole job rather than dropping a sheet onto a report and
* returning them to a page that still says nothing sold. It also keeps
* the capability question in one place: the button on /capacity states
* whose authority this is, so it is not restated here.
*/
action={
<Button variant="primary" asChild>
<Link to="/capacity">
Go to the capacity book
<ArrowRight className="size-4" aria-hidden />
</Link>
</Button>
}
/>
);
}
@@ -71,34 +136,108 @@ export function Margin() {
return (
<div className="space-y-5 pb-[calc(5.5rem+var(--safe-bottom))] md:pb-0">
<header>
<h1 className="text-xl font-semibold tracking-tight sm:text-2xl">Margin</h1>
<p className="mt-1 max-w-2xl text-sm text-muted">
Revenue from what we sold, against the full cost of what we bought.
</p>
<header className="flex flex-col gap-3 sm:flex-row sm:items-start sm:justify-between">
<div className="min-w-0">
<h1 className="text-xl font-semibold tracking-tight sm:text-2xl">Margin</h1>
<p className="mt-1 max-w-2xl text-sm text-muted">
Revenue from what we sold, against the full cost of what we bought. Every
commitment is charged in full, so a block keeps paying for the hours nobody
has bought yet.
</p>
</div>
{/*
No `context` prop, deliberately. This button asks about whatever the
page has published — the selected block, else /margin — and pinning it
to the page here would make selecting a block change the dock and not
this button, which is the one the user just pressed.
*/}
<PiggyAskButton
label={focused ? 'Ask about this block' : 'Ask Piggy'}
prompt={
focused
? 'How much of this block is still unsold, what must the rest fetch to cover it, and how much term is left to sell into?'
: 'Which commitment is furthest from covering its cost, and what would the remaining hours have to fetch?'
}
/>
</header>
<section className="grid grid-cols-2 gap-2 sm:gap-3 xl:grid-cols-4">
<Stat label="Revenue" value={money(t.revenueCents)} />
<Stat label="Cost" value={money(t.costCents)} hint="Full commitment" />
<Stat label="Revenue" value={money(t.revenueCents)} hint={`${compactNumber(t.allocatedGpuHours)} GPU-hrs sold`} />
<Stat label="Cost" value={money(t.costCents)} hint={`${compactNumber(t.committedGpuHours)} GPU-hrs committed`} />
<Stat
label="Gross margin"
value={money(t.grossMarginCents)}
hint={percent(t.grossMarginPct, 1)}
hint={`${percent(t.grossMarginPct, 1)} of revenue, after the idle hours`}
tone={t.grossMarginCents >= 0 ? 'positive' : 'danger'}
/>
<Stat
label="Per sold GPU-hour"
value={moneyExact(t.marginPerAllocatedGpuHourCents)}
value={unitPrice(t.marginPerAllocatedGpuHourCents)}
hint={`${percent(t.utilisation, 1)} of committed hours sold`}
/>
</section>
{uncovered.length > 0 ? (
<Card className="border-warning/30">
<CardHeader className="space-y-0">
<div className="flex items-start gap-2">
<AlertTriangle className="mt-0.5 size-4 shrink-0 text-warning" aria-hidden />
<div className="min-w-0">
<CardTitle className="text-base">
{uncovered.length} of {data.blocks.length} commitments have not covered their cost
</CardTitle>
{/*
Not "an average of the blocks": the blended figure is a ratio
of sums, and describing it as an average invites exactly the
per-block averaging `aggregateMargin` refuses to do.
*/}
<p className="mt-1 text-sm text-muted">
{t.grossMarginPct == null
? 'Nothing has sold yet, so every commitment below is still owed its whole cost.'
: `The book clears ${percent(t.grossMarginPct, 1)} blended because the blocks that have earned their money back carry the ones that have not.`}{' '}
These are the blocks still owed something, and the price the rest of each has to
fetch to get there.
</p>
</div>
</div>
</CardHeader>
<CardContent className="space-y-2">
{uncovered.map((block) => (
<div key={block.commitmentId} className="flex flex-col gap-2 rounded-lg bg-surface-2 p-3 sm:flex-row sm:items-center sm:justify-between">
<div className="min-w-0">
<p className="break-words font-medium leading-snug">{block.name}</p>
<p className="mt-1 text-xs text-muted">
{block.gpuCount}× {block.gpuType} · <span className="nums">{percent(block.utilisation)}</span> sold ·{' '}
<span className="nums">{compactNumber(block.availableGpuHours)}</span> hrs still sellable
</p>
</div>
<p className="shrink-0 text-sm sm:text-right">
<span className="nums font-semibold text-warning">{unitPrice(block.breakEvenPriceCents)}</span>
<span className="text-muted">/GPU-hr to break even</span>
{/*
Break-even sits below cost once part of the block has sold —
the hours already invoiced have paid down some of it. Said
here because the two prices are otherwise read as a
contradiction rather than as progress.
*/}
<span className="mt-0.5 block text-xs text-muted">
against <span className="nums">{unitPrice(block.costPerGpuHourCents)}</span>/GPU-hr paid
</span>
</p>
</div>
))}
</CardContent>
</Card>
) : null}
<Card>
<CardHeader className="flex-row items-center justify-between gap-3 space-y-0">
<div>
<CardTitle className="text-base">By commitment</CardTitle>
<p className="mt-1 text-xs text-muted">Sold ratio describes contracted capacity sold, not workload utilization.</p>
<p className="mt-1 text-xs text-muted">
Sold ratio describes contracted capacity sold, not workload utilization. Select a
commitment to point Piggy at it.
</p>
</div>
<Link to="/capacity" className="tap inline-flex min-h-11 shrink-0 items-center gap-1 rounded-lg px-3 text-sm font-medium text-accent-fg hover:bg-surface-2">
Capacity <ArrowRight className="size-4" aria-hidden />
@@ -119,12 +258,20 @@ export function Margin() {
</thead>
<tbody>
{data.blocks.map((block) => (
<tr key={block.commitmentId} className="border-b border-border/60 last:border-0">
<tr
key={block.commitmentId}
aria-selected={block.commitmentId === focusedId}
className={cn(
'border-b border-border/60 last:border-0',
block.commitmentId === focusedId && 'bg-surface-2',
)}
>
<td className="px-4 py-3 sm:px-5">
<div className="font-medium">{block.name}</div>
<div className="text-xs text-muted">
{block.gpuCount}× {block.gpuType}
</div>
<FocusButton
block={block}
focused={block.commitmentId === focusedId}
onToggle={() => toggleFocus(block.commitmentId)}
/>
</td>
<td className="nums px-4 py-3 text-right">
{compactNumber(block.soldGpuHours)}
@@ -135,13 +282,13 @@ export function Margin() {
<td
className={[
'nums px-4 py-3 text-right font-medium',
block.utilisation < 0.5 ? 'text-warning' : '',
isUncovered(block) ? 'text-warning' : '',
].join(' ')}
>
{percent(block.utilisation)}
</td>
<td className="nums px-4 py-3 text-right">
{moneyExact(block.costPerGpuHourCents)}
{unitPrice(block.costPerGpuHourCents)}
</td>
{/*
A zero break-even means the block's cost is already
@@ -157,18 +304,27 @@ export function Margin() {
</div>
<div className="grid gap-3 px-4 pb-4 md:hidden">
{data.blocks.map((block) => (
<article key={block.commitmentId} className="rounded-xl border border-border p-4">
<article
key={block.commitmentId}
className={cn(
'rounded-xl border border-border p-4',
// `border-brand`, not `border-accent`: `accent` is shadcn's
// subtle surface in this config, so a border in it disappears.
block.commitmentId === focusedId && 'border-brand bg-surface-2',
)}
>
<div className="flex items-start justify-between gap-3">
<div className="min-w-0">
<h3 className="break-words font-medium leading-snug">{block.name}</h3>
<p className="mt-1 text-xs text-muted">{block.gpuCount}× {block.gpuType}</p>
</div>
<span className={['nums shrink-0 text-sm font-semibold', block.utilisation < 0.5 ? 'text-warning' : ''].join(' ')}>{percent(block.utilisation)} sold</span>
<FocusButton
block={block}
focused={block.commitmentId === focusedId}
onToggle={() => toggleFocus(block.commitmentId)}
/>
<span className={['nums shrink-0 text-sm font-semibold', isUncovered(block) ? 'text-warning' : ''].join(' ')}>{percent(block.utilisation)} sold</span>
</div>
<dl className="mt-4 grid grid-cols-2 gap-x-4 gap-y-2 text-sm">
<dt className="text-muted">Sold capacity</dt><dd className="nums text-right">{compactNumber(block.soldGpuHours)} hrs</dd>
<dt className="text-muted">Sellable capacity</dt><dd className="nums text-right">{compactNumber(block.availableGpuHours)} hrs</dd>
<dt className="text-muted">Our cost</dt><dd className="nums text-right">{moneyExact(block.costPerGpuHourCents)}/GPU-hr</dd>
<dt className="text-muted">Our cost</dt><dd className="nums text-right">{unitPrice(block.costPerGpuHourCents)}/GPU-hr</dd>
<dt className="text-muted">Break even</dt><dd className="text-right"><BreakEven value={block.breakEvenPriceCents} /></dd>
</dl>
</article>
@@ -180,8 +336,36 @@ export function Margin() {
);
}
/**
* The commitment name, as the control that points Piggy at that commitment.
*
* A row is the thing a reader is already looking at when they want to ask about
* it, so the name carries the selection rather than a separate button in a
* seventh column that would push the table wider than the pane it scrolls in.
*/
function FocusButton({ block, focused, onToggle }: { block: MarginBlock; focused: boolean; onToggle(): void }) {
return (
<button
type="button"
aria-pressed={focused}
onClick={onToggle}
title={focused ? 'Stop pointing Piggy at this commitment' : 'Point Piggy at this commitment'}
className="tap -mx-2 block min-w-0 rounded-lg px-2 py-1 text-left transition-colors hover:bg-surface-2"
>
<span className="block break-words font-medium leading-snug">
{block.name}
{focused ? <Badge tone="accent" className="ml-2 align-middle">In focus</Badge> : null}
</span>
<span className="mt-0.5 block text-xs text-muted">
{block.gpuCount}× {block.gpuType}
</span>
<span className="sr-only">{focused ? 'Piggy is looking at this commitment' : 'Point Piggy at this commitment'}</span>
</button>
);
}
function BreakEven({ value }: { value: number | null }) {
if (value == null) return <span className="text-muted">Sold out</span>;
if (value === 0) return <span className="text-positive">Cost covered</span>;
return <span className="nums">{moneyExact(value)}/GPU-hr</span>;
return <span className="nums">{unitPrice(value)}/GPU-hr</span>;
}