Skip to content

Commit

Costs & margin: one total for who g1t paid, own spend explained, rates and replaced rises said plainly

Who g1t paid is now All in's cost: Cloudflare's usage, its subscriptions over the range and the model providers ($32.49, not $2.49 beside an All in of $32.49). g1t's own spend says what it is and why it differs from the statement: the calendar month so far at cost as each charge settled (the price book's cost for sandboxes, where the statement takes Cloudflare's bill, $0 inside included usage), the whole month's subscriptions, and spend on workspaces a testing reset later wiped. Billing returns that last part (SpendCaps.reset_micros, reset_workspaces): syntaqx's $7.41 of 10-02 to 10-05 is most of the $7.61 "charged without real money", which is at cost, not at price; its note now says so. Margins are labelled as a share of the price (cost plus 20% is 16.7%). A free workspace's net is red only for what was neither paid nor given. Price versions carry a basis: the agent rate and security activation are rates g1t sets, so no cost is shown for them, and the token weights show as multipliers. Version and in-force dates show their time. A rise replaced by a newer measurement before its date marks its proposal superseded (it never took effect), and migration 0049 marks the two from 2026-10-07 that showed as applied with no in-force date. No rise skipped its notice: v2 (due 10-21) was replaced by v3 (due 10-22) and sandbox seconds are still charged at v1.

syntaqxcommitted Parentfafe41bBrowse files
10 files+346−600/10 viewed
+50−0
66 import {
77 daySeries,
88 daysBetween,
9+ marginOnPrice,
910 marginPercent,
1011 marginTone,
1112 parseBucket,
1314 parseMapping,
1415 parseRange,
1516 percentLabel,
17+ proposalOutcome,
1618 spendBanner,
1719 spendRows,
20+ subscriptionsOver,
1821 unitDollars,
22+ versionCells,
23+ whoPaid,
1924 } from "./costs.ts";
2025
2126 const day = (d: string, bucket: string, cf: number, own: number, value: number, cash: number): CostDay => ({
150155 assert.deepEqual(rows.map((r) => r.key), ["comped", "trial", "free", "fixed"]);
151156 assert.equal(totalMicros, 76_000_000);
152157 });
158+
159+test("charged without real money is what it cost g1t, never its price", () => {
160+ const { rows } = spendRows(caps({ monthBuckets: [{ bucket: "unpaid", title: "Charged without real money", micros: 7_610_000 }] }));
161+ assert.match(rows[0]!.note, /what it cost g1t, not what was charged/);
162+});
163+
164+test("margins are a share of the price: cost plus 20% is 16.7%", () => {
165+ assert.equal(percentLabel(marginOnPrice(20)), "16.7%");
166+ assert.equal(marginOnPrice(0), 0);
167+ // As By product showed it: $2.99 charged for models that cost $2.49.
168+ assert.equal(percentLabel(marginPercent(2_988_000, 2_490_000)), "16.7%");
169+});
170+
171+test("who g1t paid is the statement's all-in cost, subscriptions included", () => {
172+ const subscriptions = subscriptionsOver(30_000_000, 30);
173+ const paid = whoPaid({ costMicros: 2_490_000, cloudflareCostMicros: 0, modelsCostMicros: 2_490_000 }, subscriptions);
174+ assert.deepEqual(paid, { totalMicros: 32_490_000, cloudflareMicros: 0, subscriptionsMicros: 30_000_000, modelsMicros: 2_490_000 });
175+ assert.equal(subscriptionsOver(30_000_000, 7), 7_000_000);
176+ // From older billing, Cloudflare's part is the rest.
177+ assert.equal(whoPaid({ costMicros: 3_000_000, modelsCostMicros: 1_000_000 }, 0).cloudflareMicros, 2_000_000);
178+});
179+
180+test("a rate g1t sets shows no cost, and a weight is no money", () => {
181+ assert.deepEqual(versionCells({ costMicros: 250_000, priceMicros: 250_000, basis: "rate" }), {
182+ cost: "—",
183+ price: "$0.250",
184+ note: "g1t's own rate: no cost behind it",
185+ });
186+ assert.equal(versionCells({ costMicros: 100_000, priceMicros: 100_000, basis: "weight" }).price, "×0.1");
187+ assert.deepEqual(versionCells({ costMicros: 16.44, priceMicros: 19.73, basis: "cost" }), { cost: unitDollars(16.44), price: unitDollars(19.73), note: null });
188+ // Older billing sends no basis: a cost.
189+ assert.equal(versionCells({ costMicros: 1_000_000, priceMicros: 1_200_000 }).cost, "$1.00");
190+});
191+
192+test("a rise replaced before its date says so, rather than missing its date", () => {
193+ assert.equal(
194+ proposalOutcome({ status: "superseded", decidedBy: "guardrail", effectiveAt: null }),
195+ "applied by guardrail, then replaced by a later measurement before it took effect; nothing was charged at it",
196+ );
197+ // From before billing marked them superseded.
198+ assert.match(proposalOutcome({ status: "applied", decidedBy: "guardrail", effectiveAt: null }) ?? "", /replaced by a later measurement/);
199+ assert.equal(proposalOutcome({ status: "applied", decidedBy: "guardrail", effectiveAt: "2026-10-22T04:18:12.571Z" }), "applied by guardrail");
200+ assert.equal(proposalOutcome({ status: "superseded", decidedBy: null, effectiveAt: null }), "replaced by a later measurement");
201+ assert.equal(proposalOutcome({ status: "open", decidedBy: null, effectiveAt: null }), null);
202+});
+59−7
4444 return [...totals.values()];
4545 }
4646
47+/**
48+ * The margin on the price a markup gives, in percent: cost plus 20% is a
49+ * 16.7% margin, since the 20% is of the cost and the margin of the price.
50+ */
51+export function marginOnPrice(markupPercent: number): number {
52+ return markupPercent > -100 ? (markupPercent / (100 + markupPercent)) * 100 : 0;
53+}
54+
55+/**
56+ * Who g1t paid over the range: Cloudflare's usage bill, its subscriptions
57+ * (a month's, over the range) and the model providers. The same total as
58+ * the statement's All in.
59+ */
60+export function whoPaid(
61+ overall: { costMicros: number; cloudflareCostMicros?: number; modelsCostMicros?: number },
62+ subscriptionsMicros: number,
63+): { totalMicros: number; cloudflareMicros: number; subscriptionsMicros: number; modelsMicros: number } {
64+ const modelsMicros = overall.modelsCostMicros ?? 0;
65+ const cloudflareMicros = overall.cloudflareCostMicros ?? overall.costMicros - modelsMicros;
66+ return { totalMicros: overall.costMicros + subscriptionsMicros, cloudflareMicros, subscriptionsMicros, modelsMicros };
67+}
68+
69+/** Cloudflare's subscriptions over a range of days: a month's, pro rata. */
70+export function subscriptionsOver(monthlyMicros: number, days: number): number {
71+ return Math.round((monthlyMicros * days) / 30);
72+}
73+
74+/**
75+ * A price version's cost and price as the table shows them. A rate g1t
76+ * sets has no cost behind it; a weight is a multiplier, not money.
77+ */
78+export function versionCells(v: { costMicros: number; priceMicros: number; basis?: string }): { cost: string; price: string; note: string | null } {
79+ if (v.basis === "weight") {
80+ const weight = Number((v.costMicros / 1_000_000).toFixed(6));
81+ return { cost: "—", price: `×${weight}`, note: "A weight on the agent rate's tokens, not money" };
82+ }
83+ if (v.basis === "rate") return { cost: "—", price: unitDollars(v.priceMicros), note: "g1t's own rate: no cost behind it" };
84+ return { cost: unitDollars(v.costMicros), price: unitDollars(v.priceMicros), note: null };
85+}
86+
87+/**
88+ * What became of a proposal, in words, for its line: who decided it, and
89+ * when one never took effect because a later measurement replaced it.
90+ */
91+export function proposalOutcome(p: { status: string; decidedBy: string | null; effectiveAt: string | null }): string | null {
92+ const by = p.decidedBy ? `${p.status === "superseded" ? "applied" : p.status} by ${p.decidedBy}` : null;
93+ const replaced = "replaced by a later measurement before it took effect; nothing was charged at it";
94+ if (p.status === "superseded") return by ? `${by}, then ${replaced}` : "replaced by a later measurement";
95+ if ((p.status === "applied" || p.status === "approved") && !p.effectiveAt) return by ? `${by}, then ${replaced}` : replaced;
96+ return by;
97+}
98+
4799 /** Margin as a whole percent of revenue; none when there was none. */
48100 export function marginPercent(revenueMicros: number, costMicros: number): number | null {
49101 return revenueMicros > 0 ? ((revenueMicros - costMicros) / revenueMicros) * 100 : null;
192244 /** What g1t paid this month, by bucket, with the free tier and Cloudflare's subscriptions; and the total. */
193245 export function spendRows(caps: SpendCaps): { rows: { key: string; title: string; micros: number; note: string }[]; totalMicros: number } {
194246 const notes: Record<string, string> = {
195− comped: "Work on accounts with a 100% discount, at cost",
196− trial: "Trial credit, at cost",
197− oss: "Checks and workflows on public repositories, at cost",
198− given: "Free workspaces' overruns past their trial",
199− unpaid: "Charged, but no real money yet (test-mode payments)",
247+ comped: "Work on accounts with a 100% discount: what it cost g1t, not its price",
248+ trial: "Trial credit, at what it cost g1t",
249+ oss: "Checks and workflows on public repositories, at what they cost g1t",
250+ given: "Free workspaces' overruns past their trial, at what they cost g1t",
251+ unpaid: "Usage charged while payments are in Stripe's test mode: what it cost g1t, not what was charged",
200252 };
201253 const rows = caps.monthBuckets.map((b) => ({ key: b.bucket, title: b.title, micros: b.micros, note: notes[b.bucket] ?? "" }));
202− rows.push({ key: "free", title: "Free tier", micros: caps.freeTierMicros, note: "Free workspaces' share of git, storage and platform, reconciled through yesterday" });
254+ rows.push({ key: "free", title: "Free tier", micros: caps.freeTierMicros, note: "Free workspaces' share of git, storage and platform, as last reconciled" });
203255 rows.push({
204256 key: "fixed",
205257 title: "Cloudflare subscriptions",
206258 micros: caps.fixedMonthlyMicros,
207− note: caps.fixedSource === "cloudflare" ? "A month, as Cloudflare lists them" : "A month, estimated (CLOUDFLARE_FIXED_MONTHLY_MICROS)",
259+ note: caps.fixedSource === "cloudflare" ? "The whole month, as Cloudflare lists them" : "The whole month, estimated (CLOUDFLARE_FIXED_MONTHLY_MICROS)",
208260 });
209261 return { rows, totalMicros: rows.reduce((sum, row) => sum + row.micros, 0) };
210262 }
+29−24
55 import type { Route } from "./+types/costs-bill";
66 import { CostsHeader } from "~/components/costs-header";
77 import { Badge, Button, EmptyState, Field, Input, Notice, Section, When } from "~/components/ui";
8−import { countLabel, driftLabel, percentLabel, unitDollars } from "~/lib/costs";
8+import { countLabel, driftLabel, percentLabel, proposalOutcome, unitDollars, versionCells } from "~/lib/costs";
99 import { type CostsActionResult, costsAction, costsLoader } from "~/lib/costs-route.server";
1010 import { dollarsField, usd } from "~/lib/money";
1111
111111 </tr>
112112 </thead>
113113 <tbody>
114− {report.versions.map((v) => (
115− <tr key={v.id} className="border-b border-line align-top last:border-0">
116− <td className="px-4 py-2.5 sm:px-5">
117− <span className="font-mono text-xs">{v.meter}</span> <span className="text-faint">v{v.version}</span>
118− {!v.appliedAt && (
119− <span className="ml-2">
120− <Badge tone="info">Coming</Badge>
121− </span>
122− )}
123− </td>
124− <td className="tabular px-4 py-2.5 text-right">{unitDollars(v.costMicros)}</td>
125− <td className="tabular px-4 py-2.5 text-right">{unitDollars(v.priceMicros)}</td>
126− <td className="px-4 py-2.5 text-xs text-muted">
127− <When at={v.effectiveAt} />
128− </td>
129− <td className="px-4 py-2.5 text-xs text-faint sm:pr-5">
130− {v.reason} · {v.createdBy}
131− </td>
132− </tr>
133− ))}
114+ {report.versions.map((v) => {
115+ const cells = versionCells(v);
116+ return (
117+ <tr key={v.id} className="border-b border-line align-top last:border-0">
118+ <td className="px-4 py-2.5 sm:px-5">
119+ <span className="font-mono text-xs">{v.meter}</span> <span className="text-faint">v{v.version}</span>
120+ {!v.appliedAt && (
121+ <span className="ml-2">
122+ <Badge tone="info">Coming</Badge>
123+ </span>
124+ )}
125+ {cells.note && <span className="block text-xs text-faint">{cells.note}</span>}
126+ </td>
127+ <td className="tabular px-4 py-2.5 text-right">{cells.cost === "—" ? <span className="text-faint">—</span> : cells.cost}</td>
128+ <td className="tabular px-4 py-2.5 text-right">{cells.price}</td>
129+ <td className="px-4 py-2.5 text-xs text-muted">
130+ <When at={v.effectiveAt} time />
131+ </td>
132+ <td className="px-4 py-2.5 text-xs text-faint sm:pr-5">
133+ {v.reason} · {v.createdBy}
134+ </td>
135+ </tr>
136+ );
137+ })}
134138 </tbody>
135139 </table>
136140 </div>
278282
279283 function ProposalRow({ proposal: p, error }: { proposal: PriceProposal; error: string | null }) {
280284 const rise = p.proposedCostMicros > p.currentCostMicros;
285+ const outcome = proposalOutcome(p);
281286 const statusTone = p.status === "open" ? "warn" : p.status === "rejected" || p.status === "superseded" ? "plain" : "mint";
282287 return (
283288 <li className="rounded-md border border-line px-4 py-3">
297302 <p className="mt-1 text-xs text-muted">{p.reason}</p>
298303 <p className="mt-1 text-xs text-faint">
299304 From the {p.source}, <When at={p.createdAt} time />
300− {p.decidedBy ? ` · ${p.status} by ${p.decidedBy}` : ""}
301− {p.effectiveAt ? (
305+ {outcome ? ` · ${outcome}` : ""}
306+ {p.effectiveAt && p.status !== "superseded" ? (
302307 <>
303308 {" "}
304− · in force from <When at={p.effectiveAt} />
309+ · in force from <When at={p.effectiveAt} time />
305310 </>
306311 ) : null}
307312 {p.note ? ` · “${p.note}”` : ""}
+43−16
77 import { DaysChart } from "~/components/costs";
88 import { CostsHeader, chip, costsHref } from "~/components/costs-header";
99 import { Badge, Button, Field, Input, Notice, Section, Stat, When } from "~/components/ui";
10−import { capPercent, daySeries, marginTone, parseBucket, percentLabel, spendRows } from "~/lib/costs";
10+import { capPercent, daySeries, marginOnPrice, marginTone, parseBucket, percentLabel, spendRows, subscriptionsOver, whoPaid } from "~/lib/costs";
1111 import { type CostsActionResult, costsAction, costsLoader } from "~/lib/costs-route.server";
1212 import { usd } from "~/lib/money";
1313
1414 export const meta: Route.MetaFunction = () => [{ title: "Costs & margin · sudo" }, { name: "robots", content: "noindex, nofollow" }];
1515
16+/** Billing's MARGIN_PERCENT: what is added to cost. */
17+const MARKUP_PERCENT = 20;
18+
1619 export const loader = ({ request, context }: Route.LoaderArgs) => costsLoader(request, context);
1720 export const action = ({ request, context }: Route.ActionArgs) => costsAction(request, context);
1821
2326 export default function Costs({ loaderData, actionData }: Route.ComponentProps) {
2427 const { range, report, error, done } = loaderData;
2528 const failed = actionData && "error" in actionData ? (actionData as CostsActionResult) : null;
26− const description =
27− "What g1t cost to run (Cloudflare's bill and the model providers') against what workspaces paid, with what g1t gave away on purpose kept apart. Prices are cost plus 20%; the bill itself, drift and the price book are on Bill & pricing.";
29+ const description = `What g1t cost to run (Cloudflare's bill and the model providers') against what workspaces paid, with what g1t gave away on purpose kept apart. Prices are cost plus ${MARKUP_PERCENT}%, a ${percentLabel(marginOnPrice(MARKUP_PERCENT))} margin: every margin here is a share of the price, not of the cost. The bill itself, drift and the price book are on Bill & pricing.`;
2830 if (!report) {
2931 return (
3032 <main className="mx-auto max-w-6xl px-4 py-8 sm:py-10">
4648
4749 <Statement report={report} floor={floor} range={range} proposals={open.length} />
4850
49− <SpendSection caps={report.caps} error={failed?.section === "lift" ? failed.error : null} />
51+ <SpendSection caps={report.caps} range={range} error={failed?.section === "lift" ? failed.error : null} />
5052
5153 <Section
5254 className="mt-6"
7577 />
7678 </Section>
7779
78− <Section className="mt-6" title="By product" description={`Each of g1t's products over the last ${range} days. The floor is ${floor}%.`}>
80+ <Section
81+ className="mt-6"
82+ title="By product"
83+ description={`Each of g1t's products over the last ${range} days: what customers were charged at price, given away or not, against what it cost. The margin is the charge less the cost, as a share of the charge; at cost plus ${MARKUP_PERCENT}% it is ${percentLabel(marginOnPrice(MARKUP_PERCENT))}. The floor is ${floor}%.`}
84+ >
7985 <div className="-mx-4 overflow-x-auto sm:-mx-5">
8086 <table className="w-full min-w-[44rem] text-sm">
8187 <thead>
8591 <th className="px-4 py-2 text-right font-medium">Cost</th>
8692 <th className="px-4 py-2 text-right font-medium">Price book said</th>
8793 <th className="px-4 py-2 text-right font-medium">Margin</th>
88− <th className="px-4 py-2 text-right font-medium sm:pr-5">%</th>
94+ <th className="px-4 py-2 text-right font-medium sm:pr-5">% of price</th>
8995 </tr>
9096 </thead>
9197 <tbody>
142148 <tbody>
143149 {report.topWorkspaces.map((w) => {
144150 const net = w.revenueMicros - w.costMicros;
151+ // What was given away on purpose is a budget, not a loss:
152+ // red only for what neither paid nor was given.
153+ const lost = net + (w.givenMicros ?? 0) < 0;
145154 return (
146155 <tr key={w.workspace} className="border-b border-line last:border-0">
147156 <td className="px-4 py-2.5 sm:px-5">
153162 <td className="tabular px-4 py-2.5 text-right">{usd(w.costMicros)}</td>
154163 <td className="tabular px-4 py-2.5 text-right text-fg-soft">{usd(w.givenMicros ?? 0)}</td>
155164 <td className="tabular px-4 py-2.5 text-right">{usd(w.revenueMicros)}</td>
156− <td className={`tabular px-4 py-2.5 text-right sm:pr-5 ${net < 0 && !w.internal ? "text-danger" : "text-fg-soft"}`}>{usd(net)}</td>
165+ <td className={`tabular px-4 py-2.5 text-right sm:pr-5 ${lost && !w.internal ? "text-danger" : "text-fg-soft"}`}>{usd(net)}</td>
157166 </tr>
158167 );
159168 })}
204213 const usagePercent = soldSomething && o.usageMarginPercent !== undefined ? o.usageMarginPercent : null;
205214 const running = o.runningCostMicros ?? 0;
206215 const unmapped = o.unmappedCostMicros ?? 0;
207− // Cloudflare's subscriptions are not on the usage bill: the estimate, over the range.
208− const subscriptions = Math.round((report.caps.fixedMonthlyMicros * range) / 30);
216+ // Cloudflare's subscriptions are not on the usage bill: a month's, over the range.
217+ const subscriptions = subscriptionsOver(report.caps.fixedMonthlyMicros, range);
218+ const paid = whoPaid(o, subscriptions);
209219 const moneyIn = o.usageMicros + o.plansMicros;
210− const spent = o.costMicros + subscriptions;
220+ const spent = paid.totalMicros;
211221 const net = moneyIn - spent;
212222 const givenParts = [
213223 ["100% discounts", o.givenCompedMicros ?? 0],
272282 <Stat
273283 label="Margin on usage sold"
274284 value={percentLabel(usagePercent)}
275− hint={soldSomething ? `${usd(usageIn, { cents: true })} paid for usage that cost ${usd(usageCost, { cents: true })}` : "No usage sold in this range"}
285+ hint={
286+ soldSomething
287+ ? `Of the price: ${usd(usageIn, { cents: true })} paid for usage that cost ${usd(usageCost, { cents: true })}`
288+ : "No usage sold in this range"
289+ }
276290 tone={marginTone(usagePercent, floor)}
277291 />
278292 <Stat
282296 />
283297 <Stat
284298 label="Who g1t paid"
285− value={usd(o.costMicros, { cents: true })}
286− hint={`Cloudflare ${usd(o.cloudflareCostMicros ?? o.costMicros - (o.modelsCostMicros ?? 0), { cents: true })}, model providers ${usd(o.modelsCostMicros ?? 0, { cents: true })}`}
299+ value={usd(paid.totalMicros, { cents: true })}
300+ hint={`Cloudflare's usage ${usd(paid.cloudflareMicros, { cents: true })} and subscriptions ${usd(paid.subscriptionsMicros, { cents: true })}, model providers ${usd(paid.modelsMicros, { cents: true })}; the cost in All in`}
287301 />
288302 <Stat
289303 label="Proposals waiting"
370384 );
371385 }
372386
373−function SpendSection({ caps, error }: { caps: CostsReport["caps"]; error: string | null }) {
387+function SpendSection({ caps, range, error }: { caps: CostsReport["caps"]; range: number; error: string | null }) {
374388 const { rows, totalMicros } = spendRows(caps);
375389 const net = caps.revenueMicros - totalMicros;
376390 return (
378392 className="mt-6"
379393 id="spend"
380394 title="g1t's own spend"
381− description="What g1t pays for itself, at cost: accounts on a 100% discount, the trial and open-source pools, free workspaces' overruns, and anything charged without real money behind it. Two caps hold it: each 100%-discount account's monthly budget, and a daily breaker on all of it that pauses new hosted-model agent runs g1t would pay for."
395+ description="What g1t pays for itself this calendar month, today included: accounts on a 100% discount, the trial and open-source pools, free workspaces' overruns, and usage charged without real money behind it. Each is what the work cost g1t, never its price, counted as each charge settled: the model providers' cost, and the price book's cost of sandboxes and builds. Two caps hold it: each 100%-discount account's monthly budget, and a daily breaker on all of it that pauses new hosted-model agent runs g1t would pay for."
382396 >
383397 <div className="grid gap-3 lg:grid-cols-2">
384398 <CapMeter
462476 <tr className="border-b border-line">
463477 <td className="px-4 py-2.5 sm:px-5">
464478 Money in
465− <span className="block text-xs text-faint">Usage paid for and the plan, reconciled through yesterday</span>
479+ <span className="block text-xs text-faint">Usage paid for with real money and the plan, as last reconciled</span>
466480 </td>
467481 <td className="tabular px-4 py-2.5 text-right sm:pr-5">{usd(caps.revenueMicros)}</td>
468482 </tr>
473487 </tbody>
474488 </table>
475489 </div>
490+ <p className="mt-3 text-xs text-muted">
491+ Why this differs from the statement above: this is {caps.month} so far, the statement the last {range} days. The statement takes Cloudflare&apos;s bill as the
492+ cost of what Cloudflare runs, so sandboxes and builds inside Cloudflare&apos;s included usage cost nothing there, and here what the price book says they
493+ cost. Here are Cloudflare&apos;s subscriptions for the whole month, there for the range.
494+ {(caps.resetMicros ?? 0) > 0 && (
495+ <>
496+ {" "}
497+ And {usd(caps.resetMicros ?? 0, { cents: true })} of this was spent on {(caps.resetWorkspaces ?? []).join(", ")} before a testing reset wiped
498+ {(caps.resetWorkspaces ?? []).length === 1 ? " its" : " their"} billing: still g1t&apos;s spend, but the statement has it only where the reset kept
499+ it, as given away.
500+ </>
501+ )}
502+ </p>
476503 </Section>
477504 );
478505 }
+16−0
34193419 pub created_by: String,
34203420 /// When the price book took it on; absent while it waits for its date.
34213421 pub applied_at: Option<String>,
3422+ /// What `cost_micros` is: `cost`, what g1t pays for a unit (a
3423+ /// provider's dollar passed on at cost is one); `rate`, a price g1t
3424+ /// sets with no cost behind it (the agent rate, security activation),
3425+ /// so its cost column is its price; `weight`, a multiplier in
3426+ /// millionths, not money (the agent rate's token weights).
3427+ #[serde(default)]
3428+ pub basis: String,
34223429 }
34233430
34243431 /// What a workspace cost g1t over the range, Cloudflare's costs shared
35903597 pub fixed_items: Vec<FixedCost>,
35913598 /// Money in this month, through the last reconciled day.
35923599 pub revenue_micros: i64,
3600+ /// Of this month's buckets, what was spent on workspaces whose billing
3601+ /// a testing reset later wiped: still g1t's spend, but no longer on
3602+ /// their ledger, so the reconciled figures have it only where the
3603+ /// reset kept it (as given away, testing resets).
3604+ #[serde(default)]
3605+ pub reset_micros: i64,
3606+ /// Those workspaces.
3607+ #[serde(default)]
3608+ pub reset_workspaces: Vec<String>,
35933609 }
35943610
35953611 #[derive(Clone, Debug, Default, Serialize, Deserialize)]
+38−9
2121 | --- | --- | --- |
2222 | Billable usage, `GET /accounts/{account}/billable-usage?from=&to=` | One row per service per day in FOCUS columns: `ServiceFamilyName`, `ServiceName`, `ChargePeriodStart`, `PricingQuantity`, `ContractedCost` / `BilledCost` / `ListCost`. Every product g1t uses appears once it is used: Workers, Workers for Platforms, D1, KV, R2, Queues, Containers, Durable Objects, Artifacts, Browser Rendering, Workers AI, Vectorize, Cloudflare for SaaS, Email. Inside an included amount the cost is 0. | `cost_lines`, source `billable_usage` |
2323 | GraphQL `artifactsEventsAdaptiveGroups` | Artifacts' own count by `date`, `eventType` and `repositoryName`. Operations are `create`, `fork`, `push`, `pull`, `delete`; errors (`rateLimited`, `serverError`, …) are kept but not counted. | `cost_lines`, source `artifacts_events`; per workspace (from the store key `<workspace>--<repo>`; a pull request's working copy, `pulls--<id>`, is its repository's workspace's, from repos' `pull_owners`) in `own_counts` as `cloudflare_git` |
24−| GraphQL `aiGatewayRequestsAdaptiveGroups`, filtered to `AI_GATEWAY_ID` | What AI Gateway priced g1t's own provider traffic at, by `date`, `provider`, `model` and `wholesale`: `count`, `sum.cost` (dollars), `sum.tokensIn`/`tokensOut`/`cacheReadTokens`/`cacheWriteTokens`. Field names checked against Cloudflare's schema (introspection of `AccountAiGatewayRequestsAdaptiveGroups{Sum,Dimensions,Filter_InputObject}`). An adaptive (sampled) dataset: an estimate, close at g1t's volumes. Only g1t's hosted models go through this gateway: a workspace's own provider is called at its own address, never here. | `cost_lines`, source `ai_gateway`, product `ai_gateway_requests`: per day and model a line `<provider>_<model>` (requests, at the gateway's cost), and at no cost `…__tokens`, `…__cache_read_tokens`, `…__cache_write_tokens`; Cloudflare-billed (unified billing) requests are prefixed `wholesale__`. Mapped to `models` (migration 0036). A re-read day replaces all its gateway lines. |
24+| GraphQL `aiGatewayRequestsAdaptiveGroups`, filtered to `AI_GATEWAY_ID` | What AI Gateway priced g1t's own provider traffic at, by `date`, `provider` and `model`: `count`, `sum.cost` (dollars), `sum.tokensIn`/`tokensOut`/`cacheReadTokens`/`cacheWriteTokens`; asked twice in one query, filtered `wholesale: 0` and `wholesale: 1`. `wholesale` is never a dimension: grouped by it, Cloudflare answers no rows and no error (until 2026-10-08 that left the gateway's side empty while it had logged $11.11). Read over its own window: the last 31 days until it has answered with a line, then the last few. Field names checked against Cloudflare's schema (introspection of `AccountAiGatewayRequestsAdaptiveGroups{Sum,Dimensions,Filter_InputObject}`). An adaptive (sampled) dataset: an estimate, close at g1t's volumes. Only g1t's hosted models go through this gateway: a workspace's own provider is called at its own address, never here. | `cost_lines`, source `ai_gateway`, product `ai_gateway_requests`: per day and model a line `<provider>_<model>` (requests, at the gateway's cost), and at no cost `…__tokens`, `…__cache_read_tokens`, `…__cache_write_tokens`; Cloudflare-billed (unified billing) requests are prefixed `wholesale__`. Mapped to `models` (migration 0036). A re-read day replaces all its gateway lines. |
2525 | The ledger | Every charge: its cost at the price book's cost, what it was charged at price, what paid for it. | read, never written |
2626 | `pending_usage` | Month-end meters (git, storage, scans, embeddings, the cache) as they stand. | snapshotted daily into `pending_days` |
2727 | `plan_payments` | The plan's $20. | read |
4040 | Secret on g1t-billing | Permissions | Used for |
4141 | --- | --- | --- |
4242 | `CLOUDFLARE_BILLING_TOKEN` (optional) | Account: **Billing Read**, Account: **Account Analytics Read**, for the g1t account only | Reading the bill, the Artifacts events and the subscriptions |
43−| `CLOUDFLARE_USAGE_TOKEN` (exists) | Billing Read, Account Analytics Read, AI Gateway Read | The keeper (settling runs from the gateway's logs); also the bill when `CLOUDFLARE_BILLING_TOKEN` is not set. AI Gateway's analytics are read with this token first and, if that is refused, with the bill's: Cloudflare answers a token without AI Gateway Read with no rows rather than an error, so the bill's token would read as a gateway that priced nothing |
43+| `CLOUDFLARE_USAGE_TOKEN` (exists) | Billing Read, Account Analytics Read, AI Gateway Read | The keeper (settling runs from the gateway's logs); also the bill when `CLOUDFLARE_BILLING_TOKEN` is not set. AI Gateway's analytics are read with this token first and, if that is refused, with the bill's (a token without Account Analytics Read gets an error). When the answer has no rows, billing asks the REST API for the gateway with the same token: 403 means the token lacks AI Gateway Read, 404 that `AI_GATEWAY_ID` names no gateway, 200 that nothing went through it; the `models` drift says which |
4444
4545 With neither, the daily run reconciles only what g1t counted itself, and
4646 the page says the bill cannot be read. Nothing fails. To set the scoped one:
9898 | `embeddings` | Workers AI, Vectorize | `context` |
9999 | `security` | (Workers CPU, under `platform`) | `security` |
100100 | `domains` | Cloudflare for SaaS | `domains` |
101−| `models` | not Cloudflare: AI Gateway's settled cost on the ledger; AI Gateway's own daily total (`ai_gateway_requests`) beside it, to check it | every other task (agent runs) |
101+| `models` | not Cloudflare: AI Gateway's settled cost on the ledger; AI Gateway's own daily total (`ai_gateway_requests`) beside it, to check it | every other task (agent runs). A planning run's ledger task is `plan`, the same as the plan's payments' key, so it is reconciled as `planning` (`margin::PLANNING_KEY`), which has no `revenue_map` row: models. Before 2026-10-08 its model cost landed in `platform`, where Cloudflare's bill is the cost, and was lost |
102102 | `platform` | Workers, D1, KV, Queues, Email, Browser Rendering, other Durable Objects | the plan's price |
103103
104104 For each day and bucket:
152152 each why by name, testing resets included (`givenResetMicros`).
153153 - **Month-end meters**: a day's figure is that day's `pending_days`
154154 snapshot less the day before's, within a month. Their month-end ledger
155− entries are left out, so nothing is counted twice.
156−- **Product margin** = (value − cost) / value.
155+ entries are left out, so nothing is counted twice. On a 100%-discount
156+ workspace the snapshot's charge is valued and given (comped), never cash:
157+ the month's close takes all of it off (`margin::comped_meter`).
158+- **Product margin** = (value − cost) / value: a share of the price, so
159+ cost plus 20% is a 16.7% margin. Sudo labels every margin "of price".
157160 - **Sudo's statement** keeps apart:
158161 - **Usage sold**: cash for usage against the cost of the usage buckets
159162 less what was given. Its margin is the headline; at cost plus 20% it
167170 - **Given away**: by why. A budget, watched under g1t's own spend, never
168171 shown as a loss.
169172 - **All in**: money in against all of it, with the figure without what
170− was given beside it. **Who g1t paid** splits the cost into Cloudflare
171− and the model providers.
173+ was given beside it. **Who g1t paid** is All in's cost, split into
174+ Cloudflare's usage, Cloudflare's subscriptions over the range, and the
175+ model providers.
172176
173177 The overall alert is (Σ cash − (Σ cost − Σ given)) / Σ cash.
174178 - **Quantities**: where a mapping names an `own_meter`, Cloudflare's
190194 | --- | --- | --- |
191195 | Count | g1t's count and Cloudflare's differ by more than the mapping's `drift_percent` (10%) | Find out what Cloudflare counts: compare its events with `own_counts` `artifacts_*` and `cost_operations`. If it counts more (binding reads, `ls-refs`), either change repos' `operation_mapping` so customers are charged for what Cloudflare counts, or leave it and let the per-unit cost rise (below). |
192196 | Cost | Cloudflare charged more than `drift_percent` away from the price book's cost of the same usage, with at least `min_daily_cost` | A price is stale: check the proposals. |
193−| Cost, on `models` | What AI Gateway priced g1t's own provider traffic at over the 7 days, against the ledger's model cost for the same days (billed to g1t: comped, free and trial use included, a workspace's own provider not) plus the model cost testing resets kept for those days (`reset_costs`), more than the `ai_gateway_requests` mapping's `drift_percent` (10%) apart, with at least `min_daily_cost`. A ledger with none of the gateway's cost is drift too, and so is a gateway that priced nothing against a ledger with at least `min_daily_cost` of model cost (no percentage): that is not agreement, it is a token that cannot see AI Gateway, or calls that went around it | The gateway higher: model calls g1t paid for and charged no one: runs not settled yet (they catch up within the hour), runs with no session, a run started without a billing ticket, or something else on g1t's gateway. The ledger higher: runs that reached a provider without the gateway. The detail adds why the gateway's own figure may be off: prompt-cache read and write tokens (the gateway prices them at its rates for cache tokens, which can lag the provider's; check against the provider's invoice), requests Cloudflare billed itself (unified billing: on Cloudflare's bill, not a provider's), and models with no price. A testing reset in the window is named in the detail: one that kept its cost says how much of the ledger's side it is; one from before resets kept their cost (the audit log has it, `reset_costs` does not) says the gateway's figure includes usage the ledger no longer has, so that part is not a leak, and the day it leaves the 7 days; while such a reset is in the window the `models` leak is not raised. Days are UTC by when a request ran (gateway) and when a charge was entered (ledger), so a run across midnight shifts a little between days; the 7-day sum absorbs it. |
197+| Cost, on `models` | What AI Gateway priced g1t's own provider traffic at over the 7 days, against the ledger's model cost for the same days (billed to g1t: comped, free and trial use included, a workspace's own provider not) plus the model cost testing resets kept for those days (`reset_costs`), more than the `ai_gateway_requests` mapping's `drift_percent` (10%) apart, with at least `min_daily_cost`. A ledger with none of the gateway's cost is drift too, and so is a gateway that priced nothing against a ledger with at least `min_daily_cost` of model cost (no percentage): that is not agreement. Its detail says why as far as the run could tell: requests logged with no price (add the models' prices), a token Cloudflare refuses for the gateway (give it AI Gateway Read, or fix `AI_GATEWAY_ID`), a gateway the token sees with no requests (calls went around it), or a read that failed | The gateway higher: model calls g1t paid for and charged no one: runs not settled yet (they catch up within the hour), runs with no session, a run started without a billing ticket, or something else on g1t's gateway. The ledger higher: runs that reached a provider without the gateway. The detail adds why the gateway's own figure may be off: prompt-cache read and write tokens (the gateway prices them at its rates for cache tokens, which can lag the provider's; check against the provider's invoice), requests Cloudflare billed itself (unified billing: on Cloudflare's bill, not a provider's), and models with no price. A testing reset in the window is named in the detail: one that kept its cost says how much of the ledger's side it is; one from before resets kept their cost (the audit log has it, `reset_costs` does not) says the gateway's figure includes usage the ledger no longer has, so that part is not a leak, and the day it leaves the 7 days; while such a reset is in the window the `models` leak is not raised. Days are UTC by when a request ran (gateway) and when a charge was entered (ledger), so a run across midnight shifts a little between days; the 7-day sum absorbs it. |
194198 | Unpriced | Over the 7 days, a model in AI Gateway's analytics with tokens and $0 cost, or runs settled with `runs.gateway_note` (the gateway could not price all of a run) | The gateway has no price for a model g1t runs: add it in the gateway (custom cost) or route away from it. Until then those runs are charged no less than the sandbox reported (Claude Code's own price table), never $0 silently. |
195199 | Leak | Cost of at least `min_daily_cost` and nothing charged for it (never for `platform`), or a meter in `unmapped` | Map the meter (below), or decide it is overhead (`platform`). |
196200
218222 once per rise (`price_notices`), and the pricing page lists it with
219223 "takes effect". Rises are never retroactive; margin protection is for
220224 new usage once notice has run.
225+- A newer measurement before a rise's date replaces its version, with
226+ the new rise's own full notice; the proposal behind the replaced version
227+ is marked `superseded` (it never took effect, and nothing was charged at
228+ it). Sudo says "applied by guardrail, then replaced by a later
229+ measurement". Migration 0049 marks the ones from before: sandbox and
230+ build seconds' rises of 2026-10-07, replaced on 10-08.
221231 - Staff approve or reject in sudo. A rejection needs a note.
232+- **Price versions** in sudo show each version's cost and price, except
233+ where the cost column is not a cost (`PriceVersion.basis`,
234+ `pricing::basis_of`): the agent rate and security activation are rates
235+ g1t sets (`rate`, no cost shown), and the agent rate's token weights are
236+ multipliers (`weight`, shown as ×0.1). Dates show with their time: a
237+ version from 00:00 UTC is the evening before in the Americas.
222238
223239 ## Changing a mapping
224240
798814 | `unpaid` | Charged, but with no real money behind it: Stripe's test key, or `FREE_WHILE_BUILDING` |
799815
800816 The plan's included usage and on-demand charges count as revenue only
801−with live payments; in test mode they are `unpaid`. A workspace's own
817+with live payments; in test mode they are `unpaid`, at what the usage cost
818+g1t, never what was charged. A workspace's own
802819 model provider costs g1t nothing and is not counted. Month-end meters
803820 (git, storage, scans, embeddings, the cache) are not counted here; the
804821 daily reconciliation covers them. Migration `0024_spend_caps.sql`
805822 backfills the current month from the ledger.
806823
824+**Why it differs from the statement.** The section is the calendar month
825+so far, today included, counted as each charge settled: the model
826+providers' cost and the price book's cost of sandboxes and builds. The
827+statement is the range, reconciled against Cloudflare's bill, where
828+sandboxes inside Cloudflare's included usage cost $0. The section shows
829+Cloudflare's subscriptions for the whole month, the statement over the
830+range. And `g1t_spend` is never wiped: spend on a workspace a testing reset
831+wiped later stays here (sudo names it, `SpendCaps.reset_micros`), while the
832+statement has it only where the reset kept it (`reset_costs`, given away as
833+testing resets). syntaqx's $7.41 of 2026-10-02 to 10-05, reset on 10-07
834+before resets kept their cost, is that case.
835+
807836 ### Caps
808837
809838 | Cap | Variable (g1t-billing) | Default | At the cap |
+14−0
15931593 reason: string;
15941594 createdBy: string;
15951595 appliedAt: string | null;
1596+ /**
1597+ * What `costMicros` is: `cost`, what g1t pays for a unit; `rate`, a price
1598+ * g1t sets with no cost behind it (the agent rate), so it is no cost;
1599+ * `weight`, a multiplier in millionths, not money. Absent from older
1600+ * billing: read as `cost`.
1601+ */
1602+ basis?: "cost" | "rate" | "weight" | "";
15961603 };
15971604
15981605 /** What `resetBilling` removed. */
16981705 fixedItems?: { name: string; monthlyMicros: number }[];
16991706 /** Money in this month, through the last reconciled day. */
17001707 revenueMicros: number;
1708+ /**
1709+ * Of this month's buckets, what was spent on workspaces whose billing a
1710+ * testing reset later wiped (still g1t's spend; the reconciled figures
1711+ * have it only where the reset kept it), and those workspaces.
1712+ */
1713+ resetMicros?: number;
1714+ resetWorkspaces?: string[];
17011715 };
17021716
17031717 /** A comped account's monthly budget, at cost. */
+11−0
1+-- A proposal the guardrail applied as a rise waits for its notice as a
2+-- price version; a newer measurement before that date replaces the
3+-- version (src/pricing.rs, schedule_version). The proposal behind the
4+-- replaced version never took effect, but stayed "applied" with no date it
5+-- is in force from: sandbox_second and build_second's rises of 2026-10-07
6+-- (v2, due 2026-10-21) were replaced by v3 (due 2026-10-22) on 2026-10-08.
7+-- Nothing was charged at v2. From now on the replacement marks them
8+-- superseded; these are the ones from before.
9+UPDATE price_proposals SET status = 'superseded'
10+WHERE status = 'applied' AND version_id IS NOT NULL
11+ AND version_id NOT IN (SELECT id FROM price_versions);
+31−0
503503 .await?
504504 .and_then(|s| s.micros)
505505 .unwrap_or(0);
506+ // What a testing reset wiped from the ledger is still here.
507+ #[derive(Deserialize)]
508+ struct Reset {
509+ account: String,
510+ micros: Option<i64>,
511+ }
512+ let reset = self
513+ .db
514+ .prepare(RESET_SPEND_SQL)
515+ .bind(&[month_start.as_str().into()])?
516+ .all()
517+ .await?
518+ .results::<Reset>()?;
519+ let reset_micros = reset.iter().filter_map(|r| r.micros).sum();
520+ let reset_workspaces = reset.into_iter().map(|r| r.account.strip_prefix("ws_").unwrap_or(&r.account).to_owned()).collect();
506521 let fixed = self.fixed_monthly(self.caps.fixed_monthly).await?;
507522 Ok(SpendCaps {
523+ reset_micros,
524+ reset_workspaces,
508525 day,
509526 month,
510527 today_micros,
550567 }
551568 }
552569
570+/// g1t's own spend since `?1` on accounts a testing reset wiped later than
571+/// the day it was spent (`admin_actions`, action `reset`), by account.
572+pub(crate) const RESET_SPEND_SQL: &str = "SELECT s.account, SUM(s.micros) AS micros FROM g1t_spend s
573+ WHERE s.day >= ?1 AND EXISTS (SELECT 1 FROM admin_actions a WHERE a.action = 'reset' AND a.account = s.account AND substr(a.created_at, 1, 10) >= s.day)
574+ GROUP BY s.account HAVING SUM(s.micros) > 0 ORDER BY s.account";
575+
553576 /// How sudo names a bucket of g1t's own spend.
554577 pub(crate) fn bucket_title(bucket: &str) -> &'static str {
555578 match bucket {
675698 }
676699
677700 #[test]
701+ fn spend_a_testing_reset_wiped_is_found_by_the_reset_after_it() {
702+ // syntaqx's $7.41 of 2026-10-02 to 10-05, reset on 10-07: still
703+ // g1t's spend, gone from its ledger.
704+ assert_eq!(crate::rename::parameters(RESET_SPEND_SQL), 1);
705+ assert!(RESET_SPEND_SQL.contains("a.action = 'reset'") && RESET_SPEND_SQL.contains("substr(a.created_at, 1, 10) >= s.day"));
706+ }
707+
708+ #[test]
678709 fn amounts_read_in_cents() {
679710 assert_eq!(cents(150_000_000), "$150.00");
680711 assert_eq!(cents(1_234_567), "$1.23");
+55−4
6262 /// percent or two at a time, and an email for each would be noise.
6363 const EMAIL_RISE_PERCENT: f64 = 5.0;
6464
65+/// What a meter's `cost_micros` is (`PriceVersion::basis`): a weight of the
66+/// agent rate's tokens is a multiplier; the agent rate and security
67+/// activation are prices g1t sets, with no per-unit cost behind them (the
68+/// models a run uses are charged apart, at cost); everything else is a
69+/// cost g1t pays.
70+pub(crate) fn basis_of(meter: &str) -> &'static str {
71+ if meter.starts_with("agent_token_weight_") {
72+ "weight"
73+ } else if matches!(meter, "agent_tokens" | "agent_tokens_own") || meter == crate::features::SECURITY_METER {
74+ "rate"
75+ } else {
76+ "cost"
77+ }
78+}
79+
80+/// Marks the proposals behind a meter's versions still waiting for their
81+/// date as superseded, before a newer version replaces them; who decided
82+/// it, and when, stay. Parameter: the meter.
83+pub(crate) const SUPERSEDE_WAITING_SQL: &str = "UPDATE price_proposals SET status = 'superseded'
84+ WHERE version_id IN (SELECT id FROM price_versions WHERE meter = ?1 AND applied_at IS NULL)";
85+
6586 /// Owners told of rises per run, at most; the rest on the next run.
6687 const NOTICES_PER_RUN: usize = 40;
6788
403424 + 1;
404425 let id = format!("pv_{meter}_{next}");
405426 let now = rfc3339(now_ms());
406− // A newer decision replaces a rise still waiting for its date.
427+ // A newer decision replaces a rise still waiting for its date. The
428+ // proposal behind it never took effect: superseded, not "applied"
429+ // with no date it is in force from.
407430 self.db
408− .prepare("DELETE FROM price_versions WHERE meter = ? AND applied_at IS NULL")
409− .bind(&[meter.into()])?
410− .run()
431+ .batch(vec![
432+ self.db.prepare(SUPERSEDE_WAITING_SQL).bind(&[meter.into()])?,
433+ self.db.prepare("DELETE FROM price_versions WHERE meter = ? AND applied_at IS NULL").bind(&[meter.into()])?,
434+ ])
411435 .await?;
412436 self.db
413437 .prepare(
524548 .into_iter()
525549 .map(|v| PriceVersion {
526550 price_micros: Price::price_for(v.cost_micros, v.markup_percent),
551+ basis: basis_of(&v.meter).to_owned(),
527552 id: v.id,
528553 meter: v.meter,
529554 version: v.version,
757782 }
758783
759784 #[test]
785+ fn a_versions_cost_says_what_it_is() {
786+ // The agent rate's $0.25 a million tokens is g1t's price, not what
787+ // tokens cost g1t; the weights are multipliers.
788+ assert_eq!(basis_of("agent_tokens"), "rate");
789+ assert_eq!(basis_of("agent_tokens_own"), "rate");
790+ assert_eq!(basis_of("security_activation"), "rate");
791+ assert_eq!(basis_of("agent_token_weight_cache_read"), "weight");
792+ // A provider's dollar passed on at cost, and Cloudflare's units, are costs.
793+ for meter in ["agent_models", "gateway_models", "sandbox_second", "git_operations", "card_fee_percent"] {
794+ assert_eq!(basis_of(meter), "cost", "{meter}");
795+ }
796+ }
797+
798+ #[test]
799+ fn a_rise_replaced_before_its_date_is_superseded_not_applied() {
800+ // 2026-10-07's sandbox rise (v2, due 10-21) was replaced by
801+ // 10-08's (v3, due 10-22): v2's proposal never took effect.
802+ let sql = SUPERSEDE_WAITING_SQL;
803+ assert_eq!(crate::rename::parameters(sql), 1);
804+ assert!(sql.contains("status = 'superseded'") && sql.contains("applied_at IS NULL") && !sql.contains("decided_"), "{sql}");
805+ // A rise still waits its notice: the replacement is no shortcut.
806+ let Decision::Auto { effective_ms } = decide(18.1817, 19.5181, guard(), now(), false) else { panic!() };
807+ assert_eq!(rfc3339(effective_ms), "2026-10-20T04:17:00.000Z");
808+ }
809+
810+ #[test]
760811 fn past_the_guardrail_or_with_auto_off_staff_decide() {
761812 // Up 50%: staff.
762813 assert_eq!(decide(150_000.0, 225_000.0, guard(), now(), true), Decision::Approve { suspect: false });