Skip to content
3,136 linesCodeBlameRaw

Pick any line to see why it is the way it is: the commit, the pull request and issue it came from, and what the agent was thinking.

Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1//! What g1t earns on each thing it sells, measured against what
2//! Cloudflare actually charged for it.
3//!
4//! Once a day, after `costs` has read Cloudflare's bill, the reconciler
5//! puts three figures side by side for every day and each of g1t's
6//! products (a "bucket": sandboxes, deployments, git, repository storage,
7//! …):
8//!
9//! 1. **What Cloudflare charged**: the day's cost lines `cost_map` gives
10//! the bucket.
11//! 2. **What g1t's meters recorded**: the cost on the ledger's entries for
12//! it (the price book's cost at the time) and, where a mapping names
13//! one, g1t's own count of the same units (git operations).
14//! 3. **What customers were charged**: the entries' value at price, before
15//! the plan's included usage, a trial or a pool paid part of it; and of
16//! that, what workspaces paid. Month-end meters (git, storage, scans,
17//! embeddings, the actions cache) come from daily snapshots of what they
18//! had come to (`pending_days`). The plan's price is the `platform`
19//! bucket's: the plan pays for running g1t.
20//!
21//! From those: margin per product (value against cost) and for all of g1t
22//! (money in against every cost); drift (counts or costs that disagree past
23//! a mapping's threshold, and leaks: cost with no revenue, or a Cloudflare
24//! meter no one mapped); each workspace's cost, Cloudflare's figure shared
25//! out by each workspace's own meters; and price proposals when a unit's
26//! real cost has moved (`pricing`). Alerts go to staff by email and as a
27//! banner in sudo. See docs/BILLING_OPERATIONS.md.
28
29use std::collections::{BTreeMap, BTreeSet};
30
31use g1t_contracts::billing::*;
32use g1t_contracts::{FailureCode, Outcome, new_id};
33use g1t_contracts::time::rfc3339;
34use g1t_kit::now_ms;
35use serde::{Deserialize, Serialize};
36use worker::wasm_bindgen::JsValue;
37use worker::{Env, Result};
38
39use crate::Billing;
40use crate::costs::{self, ARTIFACTS_OPERATIONS, DAY_MS, Rule, SOURCE_ARTIFACTS, SOURCE_BILLABLE, UNMAPPED};
41
42/// Buckets that are the cost of running g1t, paid by the plan rather than
43/// sold by the unit: never a leak for having no revenue of their own.
44pub(crate) const OVERHEAD: [&str; 1] = ["platform"];
45/// Buckets Cloudflare does not bill: their cost is g1t's own figure.
46pub(crate) const NOT_CLOUDFLARE: [&str; 1] = ["models"];
Merge costs and margin review: gateway query, own spend, discount meters, superseded rises47/// The key an agent's planning run is reconciled under. Its ledger task
48/// is `plan`, which is also the plan's payments' key (`revenue_map`: the
49/// platform bucket), so read as `plan` its model cost went to running g1t,
50/// where Cloudflare's bill is the cost, and was lost. No `revenue_map`
51/// row: models, like every agent run.
52pub(crate) const PLANNING_KEY: &str = "planning";
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily53/// The days drift is judged over.
54const DRIFT_DAYS: u64 = 7;
55/// The days a workspace's cost is set against its revenue.
56const ANOMALY_DAYS: u64 = 30;
57/// The days a unit's cost is measured over.
58const MEASURE_DAYS: u64 = 30;
59/// Fewer of g1t's units than this say nothing about cost per unit.
60const MIN_UNITS: f64 = 1_000.0;
61/// An open alert is emailed again after this long.
62const REMIND_MS: u64 = 7 * DAY_MS;
63
64// ---------------------------------------------------------------------
65// The arithmetic, apart from the database so it can be tested.
66// ---------------------------------------------------------------------
67
68/// One of g1t's products on one day.
69#[derive(Clone, Debug, Default, PartialEq)]
70pub(crate) struct ProductDay {
71 pub day: String,
72 pub bucket: String,
73 /// What Cloudflare charged g1t, in millionths of a dollar.
74 pub cf_cost_micros: i64,
75 /// What g1t's meters recorded it cost (the price book's cost).
76 pub own_cost_micros: i64,
77 /// What customers were charged for it at price, before what paid.
78 pub value_micros: i64,
79 /// Of that, what workspaces paid themselves.
80 pub cash_micros: i64,
81 /// Units Cloudflare counted and units g1t counted, where a mapping
82 /// says they are the same units.
83 pub cf_quantity: f64,
84 pub own_quantity: f64,
Costs: margin is measured on what was sold; comped workspaces, free periods, the trial and the pools are given away, a budget shown beside it85 /// Of `cost()`, what went on usage g1t gave away (the workspaces'
86 /// `WorkspaceDay::given`, added up).
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running87 pub given: Given,
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily88}
89
90impl ProductDay {
91 /// What it cost: Cloudflare's figure where Cloudflare bills it, else
92 /// g1t's own (models are billed by their providers, through the gateway).
93 pub fn cost(&self) -> i64 {
94 if NOT_CLOUDFLARE.contains(&self.bucket.as_str()) { self.own_cost_micros } else { self.cf_cost_micros }
95 }
96}
97
98/// A line of Cloudflare's bill, as stored.
99#[derive(Clone, Debug, Deserialize)]
100pub(crate) struct LineRow {
101 pub day: String,
102 pub source: String,
103 pub product: String,
104 pub meter: String,
105 pub quantity: f64,
106 pub cost_usd: f64,
107}
108
109/// A count of g1t's own, as stored.
110#[derive(Clone, Debug, Deserialize)]
111pub(crate) struct OwnRow {
112 pub day: String,
113 pub meter: String,
114 pub workspace: String,
115 pub quantity: f64,
116}
117
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running118/// What g1t gave away, by why: its own comped workspaces, free use (a
119/// free period, free allowances, overruns g1t covered), the trial, and the
Merge branch 'worktree-agent-a633ac0f7f66d419d'120/// open-source pool, and discounts on an account's terms (what they took
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging121/// below cost plus the margin, `ledger.discount_micros`), and credits g1t
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97122/// staff gave, promotional and goodwill, when spent (`grants`), and usage a
Merge Cloudflare's usage over its billing cycle: every page read, included amounts once a cycle, a projection, test-mode charges never money in (billing 0052)123/// testing reset wiped (`reset_costs`): g1t paid for it and nobody will;
124/// and what was charged while payments were not live (Stripe's test mode),
125/// which brought in no real money (`without_real_money`).
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97126/// The Team plan's included usage is paid for by the plan's price, so it is
127/// sold, not given; so is what a refund pays for.
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running128#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
129pub(crate) struct Given {
130 pub comped: i64,
131 pub free: i64,
132 pub trial: i64,
133 pub pool: i64,
Merge branch 'worktree-agent-a633ac0f7f66d419d'134 pub discount: i64,
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging135 pub credit_promotional: i64,
136 pub credit_goodwill: i64,
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97137 pub reset: i64,
Merge Cloudflare's usage over its billing cycle: every page read, included amounts once a cycle, a projection, test-mode charges never money in (billing 0052)138 pub unpaid: i64,
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running139}
140
141impl Given {
142 pub fn total(&self) -> i64 {
Merge Cloudflare's usage over its billing cycle: every page read, included amounts once a cycle, a projection, test-mode charges never money in (billing 0052)143 self.comped + self.free + self.trial + self.pool + self.discount + self.credit() + self.reset + self.unpaid
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging144 }
145
146 /// Credits from g1t, both kinds.
147 pub fn credit(&self) -> i64 {
148 self.credit_promotional + self.credit_goodwill
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running149 }
150
151 fn add(&mut self, other: &Given) {
152 self.comped += other.comped;
153 self.free += other.free;
154 self.trial += other.trial;
155 self.pool += other.pool;
Merge branch 'worktree-agent-a633ac0f7f66d419d'156 self.discount += other.discount;
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging157 self.credit_promotional += other.credit_promotional;
158 self.credit_goodwill += other.credit_goodwill;
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97159 self.reset += other.reset;
Merge Cloudflare's usage over its billing cycle: every page read, included amounts once a cycle, a projection, test-mode charges never money in (billing 0052)160 self.unpaid += other.unpaid;
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running161 }
162
163 /// The same shares of `cost` as these are of `value`, at most all of it.
164 fn of(&self, cost: i64, value: i64) -> Given {
165 let total = self.total();
166 if value <= 0 || cost <= 0 || total <= 0 {
167 return Given::default();
168 }
169 let given = cost as i128 * total.min(value) as i128 / value as i128;
170 let part = |x: i64| (given * x.max(0) as i128 / total as i128) as i64;
Merge branch 'worktree-agent-a633ac0f7f66d419d'171 Given {
172 comped: part(self.comped),
173 free: part(self.free),
174 trial: part(self.trial),
175 pool: part(self.pool),
176 discount: part(self.discount),
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging177 credit_promotional: part(self.credit_promotional),
178 credit_goodwill: part(self.credit_goodwill),
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97179 reset: part(self.reset),
Merge Cloudflare's usage over its billing cycle: every page read, included amounts once a cycle, a projection, test-mode charges never money in (billing 0052)180 unpaid: part(self.unpaid),
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging181 }
182 }
183}
184
185/// Credits from g1t in the reconciliation (`grants`): usage paid for with
186/// promotional or goodwill credit is given, not money in; a refund comes
187/// off money in on the day it refunds, shared over that day's paid usage.
188/// What credit paid for that is not among `rows` (month-end meters, or
189/// what was owed from before) is a row of its own on its day.
190pub(crate) fn apply_credits(rows: &mut Vec<UsageRow>, draws: &[(String, crate::grants::Draw)], refunds: &[crate::grants::Refunded]) {
191 let mut paid: BTreeMap<(String, String, String), Given> = BTreeMap::new();
192 for (workspace, draw) in draws {
193 let given = paid
194 .entry((draw.at[..10].to_owned(), workspace.clone(), crate::grants::usage_key(draw.task.as_deref(), &draw.reference)))
195 .or_default();
196 match draw.kind {
197 CreditKind::Promotional => given.credit_promotional += draw.micros,
198 CreditKind::Goodwill => given.credit_goodwill += draw.micros,
199 // Money already paid: what it pays for is paid for.
200 CreditKind::Refund | CreditKind::Purchased => {}
201 }
202 }
203 for ((day, workspace, key), given) in paid {
204 if given.credit() == 0 {
205 continue;
Merge branch 'worktree-agent-a633ac0f7f66d419d'206 }
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging207 match rows.iter_mut().find(|r| r.day == day && r.workspace == workspace && r.key == key) {
208 Some(row) => {
209 row.cash -= given.credit();
210 row.given.add(&given);
211 }
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97212 None => rows.push(UsageRow { day, workspace, key, bucket: None, value: 0, cash: -given.credit(), cost: 0, given }),
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging213 }
214 }
215 for refund in refunds {
216 let weights: Vec<(String, f64)> = rows
217 .iter()
218 .enumerate()
219 .filter(|(_, r)| r.day == refund.day && r.workspace == refund.workspace && r.cash > 0)
220 .map(|(i, r)| (format!("{i:08}"), r.cash as f64))
221 .collect();
222 let shares = attribute(refund.micros, &weights);
223 if shares.is_empty() {
224 rows.push(UsageRow {
225 day: refund.day.clone(),
226 workspace: refund.workspace.clone(),
227 key: "other".into(),
228 cash: -refund.micros,
229 ..UsageRow::default()
230 });
231 }
232 for (index, micros) in shares {
233 if let Ok(i) = index.parse::<usize>() {
234 rows[i].cash -= micros;
235 }
236 }
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running237 }
238}
239
Merge Cloudflare's usage over its billing cycle: every page read, included amounts once a cycle, a projection, test-mode charges never money in (billing 0052)240/// Money in only where it is real. Charges made while payments were not
241/// live (Stripe's test mode) brought in nothing: a row's cash from before
242/// `live_since` (all of it while payments are not live, `None`) is taken
243/// out of cash and counted as given away (`unpaid`), so it is never money
244/// in, never margin, and never what a workspace paid.
245pub(crate) fn without_real_money(rows: &mut [UsageRow], live_since: Option<&str>) {
246 for row in rows {
247 if live_since.is_some_and(|since| row.day.as_str() >= since) || row.cash == 0 {
248 continue;
249 }
250 // What else gave it away already (a 100% discount) stays that.
251 row.given.unpaid += row.cash.min(row.value - row.given.total()).max(0);
252 row.cash = 0;
253 }
254}
255
256/// The day payments went live, from `cost_settings` (`payments_live_since`)
257/// as a value read there: None while they are not live, the day kept when
258/// they are, else `today` (the first time they are seen live).
259pub(crate) fn live_since(live: bool, kept: Option<&str>, today: &str) -> Option<String> {
260 if !live {
261 return None;
262 }
263 Some(kept.filter(|d| d.len() >= 10).map_or_else(|| today.to_owned(), |d| d[..10].to_owned()))
264}
265
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily266/// What a workspace was charged for one key on one day.
267#[derive(Clone, Debug, Default, PartialEq)]
268pub(crate) struct UsageRow {
269 pub day: String,
270 pub workspace: String,
271 /// A ledger task (or `builds`), a month-end source, or `plan`.
272 pub key: String,
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97273 /// The bucket, where it is known already (what a testing reset kept,
274 /// `reset_costs`); else `key`'s, from `revenue_map`.
275 pub bucket: Option<String>,
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily276 pub value: i64,
277 pub cash: i64,
278 pub cost: i64,
Costs: margin is measured on what was sold; comped workspaces, free periods, the trial and the pools are given away, a budget shown beside it279 /// Of `value`, what g1t gave away: all of it for g1t's own (comped)
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running280 /// workspaces and in a free period, else what the trial and the pool
281 /// paid and the overruns g1t covered.
282 pub given: Given,
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily283}
284
285/// One workspace's share of a product's cost on one day.
286#[derive(Clone, Debug, PartialEq)]
287pub(crate) struct WorkspaceDay {
288 pub day: String,
289 pub workspace: String,
290 pub bucket: String,
291 pub cost: i64,
Margin alerts measure what is sold, and say dollars when a percentage would mislead292 /// What the workspace paid in cash.
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily293 pub revenue: i64,
Margin alerts measure what is sold, and say dollars when a percentage would mislead294 /// What its usage was priced at, whoever paid for it.
295 pub value: i64,
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running296 /// Of `cost`, the part g1t gave away: all of it for a comped workspace
297 /// or one with nothing priced that day (free use), else the cost times
298 /// the shares of its usage that day that g1t paid for.
299 pub given: Given,
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily300}
301
302fn micros(dollars: f64) -> i64 {
303 (dollars * 1_000_000.0).round() as i64
304}
305
306/// Puts the day's bill, g1t's counts and what customers were charged side
307/// by side, a row per day and bucket, and shares each bucket's cost out
308/// to workspaces.
309pub(crate) fn fold(
310 rules: &[Rule],
311 revenue_map: &BTreeMap<String, String>,
312 lines: &[LineRow],
313 own: &[OwnRow],
314 usage: &[UsageRow],
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running315 internal: &BTreeSet<String>,
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily316) -> (Vec<ProductDay>, Vec<WorkspaceDay>) {
317 let mut days: BTreeMap<(String, String), ProductDay> = BTreeMap::new();
318 let entry = |day: &str, bucket: &str| -> ProductDay {
319 ProductDay { day: day.to_owned(), bucket: bucket.to_owned(), ..ProductDay::default() }
320 };
321 // Which of g1t's own meters count each bucket's units.
322 let mut own_meters: BTreeMap<&str, BTreeSet<&str>> = BTreeMap::new();
323 for rule in rules {
324 if let Some(meter) = &rule.own_meter {
325 own_meters.entry(rule.bucket.as_str()).or_default().insert(meter.as_str());
326 }
327 }
328 let mut events: BTreeMap<(String, String), f64> = BTreeMap::new();
329 for line in lines {
330 let rule = costs::classify(rules, &line.product, &line.meter);
331 let bucket = rule.map_or(UNMAPPED, |r| r.bucket.as_str());
332 let key = (line.day.clone(), bucket.to_owned());
333 if line.source == SOURCE_ARTIFACTS {
334 // What Artifacts counted: operations only, and only where the
335 // bill does not count them itself.
336 if ARTIFACTS_OPERATIONS.contains(&line.meter.as_str()) {
337 *events.entry(key).or_default() += line.quantity;
338 }
339 continue;
340 }
341 let row = days.entry(key.clone()).or_insert_with(|| entry(&key.0, &key.1));
342 row.cf_cost_micros += micros(line.cost_usd);
343 if line.source == SOURCE_BILLABLE && rule.is_some_and(|r| r.own_meter.is_some()) {
344 row.cf_quantity += line.quantity;
345 }
346 }
347 for (key, quantity) in events {
348 let row = days.entry(key.clone()).or_insert_with(|| entry(&key.0, &key.1));
349 if row.cf_quantity == 0.0 {
350 row.cf_quantity = quantity;
351 }
352 }
353 // g1t's own counts of the same units, by bucket and by workspace.
354 let mut own_by: BTreeMap<(String, String), Vec<(String, f64)>> = BTreeMap::new();
355 // Cloudflare's own count by workspace, where it gives one
356 // (`cloudflare_<bucket>`): the best way to share its cost.
357 let mut cf_by: BTreeMap<(String, String), Vec<(String, f64)>> = BTreeMap::new();
358 for count in own {
359 if let Some(bucket) = count.meter.strip_prefix("cloudflare_") {
360 cf_by.entry((count.day.clone(), bucket.to_owned())).or_default().push((count.workspace.clone(), count.quantity));
361 continue;
362 }
363 for (bucket, meters) in &own_meters {
364 if meters.contains(count.meter.as_str()) {
365 let key = (count.day.clone(), (*bucket).to_owned());
366 days.entry(key.clone()).or_insert_with(|| entry(&key.0, &key.1)).own_quantity += count.quantity;
367 own_by.entry(key).or_default().push((count.workspace.clone(), count.quantity));
368 }
369 }
370 }
371 // What customers were charged.
372 let bucket_of = |key: &str| revenue_map.get(key).cloned().unwrap_or_else(|| "models".to_owned());
373 let mut value_by: BTreeMap<(String, String), Vec<(String, f64)>> = BTreeMap::new();
374 let mut cost_by: BTreeMap<(String, String), Vec<(String, f64)>> = BTreeMap::new();
375 let mut revenue: BTreeMap<(String, String, String), i64> = BTreeMap::new();
Margin alerts measure what is sold, and say dollars when a percentage would mislead376 let mut valued: BTreeMap<(String, String, String), i64> = BTreeMap::new();
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily377 let mut active: BTreeMap<String, Vec<(String, f64)>> = BTreeMap::new();
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running378 let mut gave: BTreeMap<(String, String), (Given, i64)> = BTreeMap::new();
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily379 for u in usage {
Costs: margin is measured on what was sold; comped workspaces, free periods, the trial and the pools are given away, a budget shown beside it380 let g = gave.entry((u.day.clone(), u.workspace.clone())).or_default();
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running381 g.0.add(&u.given);
Costs: margin is measured on what was sold; comped workspaces, free periods, the trial and the pools are given away, a budget shown beside it382 g.1 += u.value;
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97383 let bucket = u.bucket.clone().unwrap_or_else(|| bucket_of(&u.key));
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily384 let key = (u.day.clone(), bucket.clone());
385 let row = days.entry(key.clone()).or_insert_with(|| entry(&key.0, &key.1));
386 row.own_cost_micros += u.cost;
387 row.value_micros += u.value;
388 row.cash_micros += u.cash;
389 value_by.entry(key.clone()).or_default().push((u.workspace.clone(), u.value as f64));
390 cost_by.entry(key).or_default().push((u.workspace.clone(), u.cost as f64));
Margin alerts measure what is sold, and say dollars when a percentage would mislead391 *revenue.entry((u.day.clone(), u.workspace.clone(), bucket.clone())).or_default() += u.cash;
392 *valued.entry((u.day.clone(), u.workspace.clone(), bucket)).or_default() += u.value;
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily393 active.entry(u.day.clone()).or_default().push((u.workspace.clone(), u.value.max(u.cost) as f64));
394 }
395 // Each bucket's cost shared out: by Cloudflare's own count per
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running396 // workspace, else by g1t's own count of its units, else by what its
397 // usage cost (so free use carries its own cost), else by what it was
398 // charged; running g1t, and what no one mapped, by each workspace's
399 // share of all usage that day.
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily400 let mut shares: BTreeMap<(String, String, String), i64> = BTreeMap::new();
401 for ((day, bucket), row) in &days {
402 let key = (day.clone(), bucket.clone());
403 let weigh = |m: &BTreeMap<(String, String), Vec<(String, f64)>>| m.get(&key).filter(|w| w.iter().any(|(_, v)| *v > 0.0)).cloned();
404 let weights = if OVERHEAD.contains(&bucket.as_str()) || bucket == UNMAPPED {
405 active.get(day).cloned()
406 } else {
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running407 weigh(&cf_by).or_else(|| weigh(&own_by)).or_else(|| weigh(&cost_by)).or_else(|| weigh(&value_by)).or_else(|| active.get(day).cloned())
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily408 };
409 for (workspace, micros) in attribute(row.cost(), &weights.unwrap_or_default()) {
410 *shares.entry((day.clone(), workspace, bucket.clone())).or_default() += micros;
411 }
412 }
413 let keys: BTreeSet<(String, String, String)> = shares.keys().chain(revenue.keys()).cloned().collect();
Costs: margin is measured on what was sold; comped workspaces, free periods, the trial and the pools are given away, a budget shown beside it414 let workspaces: Vec<WorkspaceDay> = keys
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily415 .into_iter()
Costs: margin is measured on what was sold; comped workspaces, free periods, the trial and the pools are given away, a budget shown beside it416 .map(|(day, workspace, bucket)| {
417 let cost = shares.get(&(day.clone(), workspace.clone(), bucket.clone())).copied().unwrap_or(0);
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running418 // The day's shares given away apply to every bucket, so a
419 // comped workspace's part of running g1t is given too. A
420 // workspace with nothing priced that day used g1t for free.
421 let given = if internal.contains(&workspace) {
422 Given { comped: cost, ..Given::default() }
423 } else {
424 match gave.get(&(day.clone(), workspace.clone())) {
425 Some((given, value)) if *value > 0 => given.of(cost, *value),
426 _ => Given { free: cost.max(0), ..Given::default() },
427 }
Costs: margin is measured on what was sold; comped workspaces, free periods, the trial and the pools are given away, a budget shown beside it428 };
429 WorkspaceDay {
430 cost,
431 revenue: revenue.get(&(day.clone(), workspace.clone(), bucket.clone())).copied().unwrap_or(0),
432 value: valued.get(&(day.clone(), workspace.clone(), bucket.clone())).copied().unwrap_or(0),
433 given,
434 day,
435 workspace,
436 bucket,
437 }
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily438 })
439 .collect();
Costs: margin is measured on what was sold; comped workspaces, free periods, the trial and the pools are given away, a budget shown beside it440 for w in &workspaces {
441 if let Some(row) = days.get_mut(&(w.day.clone(), w.bucket.clone())) {
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running442 row.given.add(&w.given);
Costs: margin is measured on what was sold; comped workspaces, free periods, the trial and the pools are given away, a budget shown beside it443 }
444 }
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily445 (days.into_values().collect(), workspaces)
446}
447
448/// A month-end source's day, from the snapshots of what it had come to:
449/// each day's figure less the day before's in the same month (the first
450/// day of a month, or the first snapshot, is its own).
451pub(crate) fn pending_deltas(snapshots: &[(String, String, String, i64, i64)]) -> Vec<UsageRow> {
452 // (day, workspace, source, cost, charge), any order.
453 let mut sorted = snapshots.to_vec();
454 sorted.sort_by(|a, b| (&a.1, &a.2, &a.0).cmp(&(&b.1, &b.2, &b.0)));
455 let mut out = Vec::new();
456 let mut previous: Option<&(String, String, String, i64, i64)> = None;
457 for snap in &sorted {
458 let (day, workspace, source, cost, charge) = snap;
459 let (before_cost, before_charge) = match previous {
460 Some(p) if p.1 == *workspace && p.2 == *source && p.0[..7] == day[..7] => (p.3, p.4),
461 _ => (0, 0),
462 };
463 let (cost, charge) = ((cost - before_cost).max(0), (charge - before_charge).max(0));
464 if cost > 0 || charge > 0 {
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97465 out.push(UsageRow { day: day.clone(), workspace: workspace.clone(), key: source.clone(), bucket: None, value: charge, cash: charge, cost, given: Given::default() });
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily466 }
467 previous = Some(snap);
468 }
469 out
470}
471
Merge costs and margin review: gateway query, own spend, discount meters, superseded rises472/// A month-end meter's day on a 100%-discount workspace: all of it given
473/// (comped) and none of it money in. The snapshot holds what the month
474/// would charge before the discount, which the month's close takes off in
475/// full; counted as paid, flagon-io's cache, embeddings and scans read as
476/// money in ($0.0023 on 2026-10-08).
477pub(crate) fn comped_meter(mut u: UsageRow) -> UsageRow {
478 u.given = Given { comped: u.value, ..Given::default() };
479 u.cash = 0;
480 u
481}
482
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily483/// Margin as a share of what was charged, in percent; None when nothing was.
484pub(crate) fn margin_percent(revenue_micros: i64, cost_micros: i64) -> Option<f64> {
485 (revenue_micros > 0).then(|| (revenue_micros - cost_micros) as f64 * 100.0 / revenue_micros as f64)
486}
487
488/// How far `ours` is from `theirs`, in percent of theirs; None when theirs
489/// is nothing.
490pub(crate) fn delta_percent(ours: f64, theirs: f64) -> Option<f64> {
491 (theirs > 0.0).then(|| (ours - theirs) * 100.0 / theirs)
492}
493
494#[derive(Clone, Copy, Debug, PartialEq, Eq)]
495pub(crate) enum DriftKind {
496 /// g1t counted a different number of units than Cloudflare did.
497 Count,
Merge remote-tracking branch 'origin/main' into workspace-chat498 /// The price book's cost of a bucket's usage differs from what the
499 /// same usage comes to at Cloudflare's list prices, before the included
500 /// amounts (a price may be stale); on `models`, the ledger's model cost
501 /// differs from what AI Gateway priced the same traffic at.
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily502 Cost,
503 /// Cloudflare charged for something nothing charges customers for.
504 Leak,
Merge branch 'worktree-agent-a633ac0f7f66d419d'505 /// Model usage AI Gateway put no price on: its cost is not what the
506 /// provider bills, so neither the ledger nor the gateway total has it.
507 Unpriced,
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily508}
509
510impl DriftKind {
511 pub fn as_str(self) -> &'static str {
512 match self {
513 DriftKind::Count => "count",
514 DriftKind::Cost => "cost",
515 DriftKind::Leak => "leak",
Merge branch 'worktree-agent-a633ac0f7f66d419d'516 DriftKind::Unpriced => "unpriced",
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily517 }
518 }
519}
520
521#[derive(Clone, Debug, PartialEq)]
522pub(crate) struct Drift {
523 pub bucket: String,
524 pub kind: DriftKind,
525 pub ours: f64,
526 pub cloudflare: f64,
527 pub delta_percent: Option<f64>,
528}
529
530/// Drift over a window for one bucket: counts more than `threshold`
Merge remote-tracking branch 'origin/main' into workspace-chat531/// percent apart, the price book's cost of the usage that far from what the
532/// same usage comes to at Cloudflare's list prices (`list_micros`, from
533/// `list_costs`), and cost with nothing charged for it. Under
534/// `min_cost_micros` in all, cost says nothing.
535///
536/// The price book is set against the list cost, never against what
537/// Cloudflare billed: the bill is net of the included amounts and the price
538/// book's cost is of every unit, so a bucket whose usage mostly fits in
539/// them would read as a stale price when nothing changed. Without a list
540/// cost (a meter in the bucket with no list price) the price book is not
541/// checked. A leak is still what Cloudflare billed: money spent.
542pub(crate) fn drifts(bucket: &str, days: &[ProductDay], threshold: f64, counted: bool, min_cost_micros: i64, list_micros: Option<f64>) -> Vec<Drift> {
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily543 let overhead = OVERHEAD.contains(&bucket);
544 let sum = |f: &dyn Fn(&ProductDay) -> f64| days.iter().map(f).sum::<f64>();
545 let cf_cost = sum(&|d| d.cf_cost_micros as f64);
546 let own_cost = sum(&|d| d.own_cost_micros as f64);
547 let value = sum(&|d| d.value_micros as f64);
Costs: counts are compared with Cloudflare's from the day g1t started counting, so days before a meter existed are not drift548 // Counts are compared from the first day g1t counted: before its meter
549 // was deployed there is only Cloudflare's side. A meter that never
550 // counted anything is compared over every day, so it still shows.
551 let first_counted = days.iter().filter(|d| d.own_quantity > 0.0).map(|d| d.day.as_str()).min();
552 let compared = |d: &&ProductDay| first_counted.is_none_or(|from| d.day.as_str() >= from);
553 let (cf_quantity, own_quantity) = days.iter().filter(compared).fold((0.0, 0.0), |(cf, own), d| (cf + d.cf_quantity, own + d.own_quantity));
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily554 let mut out = Vec::new();
555 if counted && cf_quantity > 0.0 {
556 let delta = delta_percent(own_quantity, cf_quantity);
557 if delta.is_some_and(|d| d.abs() > threshold) {
558 out.push(Drift { bucket: bucket.into(), kind: DriftKind::Count, ours: own_quantity, cloudflare: cf_quantity, delta_percent: delta });
559 }
560 }
Merge branch 'worktree-agent-a633ac0f7f66d419d'561 // Models: what AI Gateway priced g1t's own provider traffic at (its
562 // lines, as "Cloudflare's" side) against the ledger's model cost. Only
563 // once the gateway has been read; then the ledger having none of it is
Merge remote-tracking branch 'origin/main' into workspace-chat564 // drift too (traffic no run was charged for). Every other bucket: its
565 // usage at Cloudflare's list prices, before the included amounts.
566 let not_cloudflare = NOT_CLOUDFLARE.contains(&bucket);
567 let theirs = if not_cloudflare { cf_cost } else { list_micros.unwrap_or(0.0) };
568 let enough = theirs.max(own_cost) >= min_cost_micros as f64;
569 let models = not_cloudflare && cf_cost > 0.0;
570 if enough && !overhead && theirs > 0.0 && (own_cost > 0.0 || models) {
571 let delta = delta_percent(own_cost, theirs);
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily572 if delta.is_some_and(|d| d.abs() > threshold) {
Merge remote-tracking branch 'origin/main' into workspace-chat573 out.push(Drift { bucket: bucket.into(), kind: DriftKind::Cost, ours: own_cost, cloudflare: theirs, delta_percent: delta });
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily574 }
575 }
Billing: AI Gateway's analytics are read with the token that can see them, and a gateway that priced nothing is said576 // The ledger has model cost and the gateway priced none of it: a token
577 // that cannot see AI Gateway reads as no rows, never an error, so this
578 // is not agreement. Said, rather than left as no row at all.
579 if NOT_CLOUDFLARE.contains(&bucket) && cf_cost <= 0.0 && own_cost >= min_cost_micros as f64 && own_cost > 0.0 {
580 out.push(Drift { bucket: bucket.into(), kind: DriftKind::Cost, ours: own_cost, cloudflare: 0.0, delta_percent: None });
581 }
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily582 if !overhead && cf_cost >= min_cost_micros as f64 && value <= 0.0 {
583 out.push(Drift { bucket: bucket.into(), kind: DriftKind::Leak, ours: value, cloudflare: cf_cost, delta_percent: None });
584 }
585 out
586}
587
Merge remote-tracking branch 'origin/main' into workspace-chat588/// What each bucket's billable usage over a window comes to at
589/// Cloudflare's list prices, before the included amounts (`cycle::list_cost`
590/// line by line, in micros): what the price book's cost of the same usage
591/// is checked against. None for a bucket with usage on a meter that has no
592/// list price: its usage cannot be priced like for like, so it is not
593/// checked. Other sources (Artifacts events, AI Gateway) are left out.
594pub(crate) fn list_costs(rules: &[Rule], lines: &[LineRow]) -> BTreeMap<String, Option<f64>> {
595 let mut out: BTreeMap<String, Option<f64>> = BTreeMap::new();
596 for line in lines.iter().filter(|l| l.source == SOURCE_BILLABLE) {
597 let bucket = costs::classify(rules, &line.product, &line.meter).map_or(UNMAPPED, |r| r.bucket.as_str());
598 let entry = out.entry(bucket.to_owned()).or_insert(Some(0.0));
599 if line.quantity <= 0.0 {
600 continue;
601 }
602 *entry = match (*entry, crate::cycle::list_cost(&line.product, &line.meter, line.quantity)) {
603 (Some(sum), Some(cost)) => Some(sum + cost * 1_000_000.0),
604 _ => None,
605 };
606 }
607 out
608}
609
Merge branch 'worktree-agent-a633ac0f7f66d419d'610/// What can make AI Gateway's cost differ from what the providers bill,
611/// said for staff: cache tokens (priced by the gateway at its own rates for
612/// them, which may lag the provider's), requests Cloudflare billed itself,
613/// models it has no price for, and runs settled short.
614fn caveat_notes(c: &costs::GatewayCaveats) -> Vec<String> {
615 let mut notes = Vec::new();
616 if c.cache_read_tokens > 0.0 || c.cache_write_tokens > 0.0 {
617 notes.push(format!(
618 "{} prompt-cache read and {} cache write tokens went through it: check its cost against the provider's invoice, since cache reads are billed far below input and writes above it",
619 crate::features::thousands(c.cache_read_tokens.round() as u64),
620 crate::features::thousands(c.cache_write_tokens.round() as u64)
621 ));
622 }
623 if c.wholesale_usd > 0.0 {
624 notes.push(format!(
625 "{} of it Cloudflare billed itself (unified billing): that part is on Cloudflare's bill, not a provider's",
626 dollars(micros(c.wholesale_usd))
627 ));
628 }
629 if !c.unpriced.is_empty() {
630 notes.push(format!("it has no price for {} (tokens used, $0)", c.unpriced.join(", ")));
631 }
632 if c.short_runs > 0 {
633 notes.push(format!("{} runs were settled at no less than the sandbox reported because the gateway could not price all of them", c.short_runs));
634 }
635 notes
636}
637
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97638/// Where a testing reset's history starts: all of a workspace's ledger.
639pub(crate) const RESET_HISTORY_FROM: &str = "2000-01-01";
640
641/// What a testing reset wiped that g1t paid for, on one day for one
642/// bucket (`reset_costs`).
643#[derive(Clone, Debug, PartialEq)]
644pub(crate) struct Wiped {
645 pub day: String,
646 pub bucket: String,
647 pub cost: i64,
648 pub value: i64,
649}
650
651/// A workspace's usage rows, about to be wiped, as what g1t paid for: the
652/// rows with a cost, by day and bucket, valued as the reconciliation
653/// valued them (at price where nothing paid). Plan payments, credits and
654/// a workspace's own model provider cost g1t nothing and are left out.
655pub(crate) fn wiped(rows: &[UsageRow], revenue_map: &BTreeMap<String, String>, margin_percent: u32) -> Vec<Wiped> {
656 let mut by: BTreeMap<(String, String), (i64, i64)> = BTreeMap::new();
657 for u in rows.iter().filter(|u| u.cost > 0) {
658 let bucket = u.bucket.clone().unwrap_or_else(|| revenue_map.get(&u.key).cloned().unwrap_or_else(|| NOT_CLOUDFLARE[0].to_owned()));
659 let sums = by.entry((u.day.clone(), bucket)).or_default();
660 sums.0 += u.cost;
661 sums.1 += u.value.max(0);
662 }
663 by.into_iter()
664 .map(|((day, bucket), (cost, value))| Wiped {
665 day,
666 bucket,
667 cost,
668 value: if value > 0 { value } else { crate::credits::with_margin(cost, margin_percent) },
669 })
670 .collect()
671}
672
673/// What testing resets kept, each (day, workspace, bucket, cost, value),
674/// as usage rows: valued as before, nothing paid, all of it given away
675/// (why "testing resets"). A reset's own row (bucket '') is not usage.
676pub(crate) fn reset_usage(kept: &[(String, String, String, i64, i64)]) -> Vec<UsageRow> {
677 kept.iter()
678 .filter(|(_, _, bucket, cost, value)| !bucket.is_empty() && (*cost != 0 || *value != 0))
679 .map(|(day, workspace, bucket, cost, value)| UsageRow {
680 day: day.clone(),
681 workspace: workspace.clone(),
682 key: "reset".into(),
683 bucket: Some(bucket.clone()),
684 value: *value,
685 cash: 0,
686 cost: *cost,
687 given: Given { reset: *value, ..Given::default() },
688 })
689 .collect()
690}
691
692/// A testing reset inside the drift window.
693#[derive(Clone, Debug, PartialEq)]
694pub(crate) struct ResetNote {
695 pub workspace: String,
696 /// The UTC day it was reset.
697 pub day: String,
698 /// Whether it kept what it wiped (`reset_costs`, migration 0046):
699 /// then the ledger's side has it, given away. A reset from before
700 /// that wiped model usage the gateway still counts.
701 pub recorded: bool,
702 /// Of what it kept, model cost on the window's days.
703 pub models_micros: i64,
704}
705
706/// The resets: each audit entry (account `ws_<slug>`, when) and each kept
707/// reset (workspace, reset_at, its model cost in the window). An audit
708/// entry with no kept reset at the same instant is from before resets kept
709/// what they wiped.
710pub(crate) fn reset_notes(audits: &[(String, String)], kept: &[(String, String, i64)]) -> Vec<ResetNote> {
711 let mut notes: Vec<(String, ResetNote)> = kept
712 .iter()
713 .map(|(workspace, at, models)| {
714 (at.clone(), ResetNote { workspace: workspace.clone(), day: at[..10.min(at.len())].to_owned(), recorded: true, models_micros: *models })
715 })
716 .collect();
717 for (account, at) in audits {
718 let workspace = account.strip_prefix("ws_").unwrap_or(account);
719 if !kept.iter().any(|(w, a, _)| w == workspace && a == at) {
720 notes.push((at.clone(), ResetNote { workspace: workspace.to_owned(), day: at[..10.min(at.len())].to_owned(), recorded: false, models_micros: 0 }));
721 }
722 }
723 notes.sort_by(|a, b| a.0.cmp(&b.0).then(a.1.workspace.cmp(&b.1.workspace)));
724 notes.into_iter().map(|(_, n)| n).collect()
725}
726
727/// Model usage a reset wiped before resets kept it is not a leak: while
728/// such a reset is in the window the models leak is not raised, and the
729/// models cost drift says what the gap is.
730pub(crate) fn wiped_not_leaked(drift: &Drift, resets: &[ResetNote]) -> bool {
731 drift.kind == DriftKind::Leak && NOT_CLOUDFLARE.contains(&drift.bucket.as_str()) && resets.iter().any(|r| !r.recorded)
732}
733
734/// What the models drift says about resets in the window.
735fn reset_sentences(resets: &[ResetNote]) -> Vec<String> {
736 resets
737 .iter()
738 .filter_map(|r| {
739 if !r.recorded {
740 Some(format!(
741 "AI Gateway's figure includes model usage wiped by a testing reset of {} on {}, from before resets kept what they wiped: the ledger no longer has it, so that part of the gap is the reset, not a leak. It leaves the {DRIFT_DAYS} days on {}.",
742 r.workspace,
743 r.day,
744 day_after(&r.day, DRIFT_DAYS)
745 ))
746 } else if r.models_micros > 0 {
747 Some(format!(
748 "The ledger's figure includes {} of model cost wiped by a testing reset of {} on {}, counted as given away (testing resets).",
749 dollars(r.models_micros),
750 r.workspace,
751 r.day
752 ))
753 } else {
754 None
755 }
756 })
757 .collect()
758}
759
760/// The models drift's detail: the gateway's total against the ledger's,
761/// and any testing reset in the window.
Merge costs and margin review: gateway query, own spend, discount meters, superseded rises762pub(crate) fn models_detail(drift: &Drift, caveats: &costs::GatewayCaveats, resets: &[ResetNote], read: &costs::GatewayRead) -> String {
Billing: AI Gateway's analytics are read with the token that can see them, and a gateway that priced nothing is said763 if drift.cloudflare <= 0.0 {
Merge costs and margin review: gateway query, own spend, discount meters, superseded rises764 let head = format!(
765 "Models: the ledger's model cost is {} over the last {DRIFT_DAYS} days and AI Gateway priced nothing, so the two were not compared.",
Billing: AI Gateway's analytics are read with the token that can see them, and a gateway that priced nothing is said766 dollars(drift.ours as i64)
767 );
Merge costs and margin review: gateway query, own spend, discount meters, superseded rises768 let why = if caveats.requests > 0.0 {
769 format!(
770 "The gateway logged {} requests in those days but put no price on them: it has no price for the models used{}. Add their prices to the gateway, or route those models where they are priced.",
771 crate::features::thousands(caveats.requests.round() as u64),
772 if caveats.unpriced.is_empty() { String::new() } else { format!(" ({})", caveats.unpriced.join(", ")) }
773 )
774 } else {
775 match read {
776 costs::GatewayRead::Empty { visible: Some(false) } => "The token billing reads AI Gateway with (CLOUDFLARE_USAGE_TOKEN, else CLOUDFLARE_BILLING_TOKEN) cannot see the gateway named in AI_GATEWAY_ID: Cloudflare refused it (no Account, AI Gateway, Read on the token, or no gateway by that id). Give the token AI Gateway Read, or fix AI_GATEWAY_ID.".to_owned(),
777 costs::GatewayRead::Empty { visible: Some(true) } => "The token can see the gateway and it logged no requests in those days: model calls went around it. Check that every caller of a hosted model uses the gateway's URL (services/models, the runner's ANTHROPIC_BASE_URL).".to_owned(),
778 costs::GatewayRead::Failed(error) => format!("AI Gateway's analytics could not be read: {error}"),
779 costs::GatewayRead::NotRead => "AI Gateway was not read on this run: no AI_GATEWAY_ID, or no CLOUDFLARE_USAGE_TOKEN or CLOUDFLARE_BILLING_TOKEN.".to_owned(),
780 costs::GatewayRead::Rows | costs::GatewayRead::Empty { visible: None } => "The gateway answered without requests for these days, and whether its token can see the gateway could not be told: either the token lacks AI Gateway Read, or model calls went around the gateway.".to_owned(),
781 }
782 };
783 return format!("{head} {why}");
Billing: AI Gateway's analytics are read with the token that can see them, and a gateway that priced nothing is said784 }
Merge branch 'worktree-agent-a633ac0f7f66d419d'785 let lower = drift.ours < drift.cloudflare;
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97786 let wiped = resets.iter().any(|r| !r.recorded);
Merge branch 'worktree-agent-a633ac0f7f66d419d'787 let mut detail = format!(
788 "Models: AI Gateway priced g1t's own provider traffic at {} over the last {DRIFT_DAYS} days; the ledger's model cost for the same days is {} ({:+.1}%). {}",
789 dollars(drift.cloudflare as i64),
790 dollars(drift.ours as i64),
791 drift.delta_percent.unwrap_or(0.0),
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97792 if lower && wiped {
793 "The gateway counts model calls the ledger no longer has: a testing reset wiped them (below). Beyond that, runs not yet settled, runs with no session, or calls with no run."
794 } else if lower {
Merge branch 'worktree-agent-a633ac0f7f66d419d'795 "Model calls g1t paid for were not charged: runs not yet settled, runs with no session, or calls with no run (the ledger catches up as runs settle; a gap that stays is a leak)."
796 } else {
797 "The ledger counts more than the gateway priced: runs that went to a provider without the gateway, or sandbox reports the gateway could not correct."
798 }
799 );
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97800 for sentence in reset_sentences(resets) {
801 detail.push(' ');
802 detail.push_str(&sentence);
803 }
Merge branch 'worktree-agent-a633ac0f7f66d419d'804 let notes = caveat_notes(caveats);
805 if !notes.is_empty() {
806 detail.push_str(" The gateway's cost may be off: ");
807 detail.push_str(&notes.join("; "));
808 detail.push('.');
809 }
810 detail
811}
812
813/// The unpriced drift's detail.
814pub(crate) fn unpriced_detail(caveats: &costs::GatewayCaveats) -> String {
815 format!(
816 "Models: AI Gateway's cost is not all of what the providers bill over the last {DRIFT_DAYS} days: {}. Runs on a model with no gateway price are charged no less than the sandbox reported; add the model's price to the gateway (or route away from it) so it is charged at cost.",
817 caveat_notes(&costs::GatewayCaveats { cache_read_tokens: 0.0, cache_write_tokens: 0.0, wholesale_usd: 0.0, ..caveats.clone() }).join("; ")
818 )
819}
820
821/// Model usage AI Gateway could not price over the window, as drift on
822/// the models bucket: models with tokens and no cost, or runs settled
823/// short. None when there is none.
824pub(crate) fn unpriced_drift(caveats: &costs::GatewayCaveats) -> Option<(Drift, String)> {
825 if caveats.unpriced.is_empty() && caveats.short_runs == 0 {
826 return None;
827 }
828 let drift = Drift {
829 bucket: NOT_CLOUDFLARE[0].into(),
830 kind: DriftKind::Unpriced,
831 ours: f64::from(caveats.short_runs),
832 cloudflare: caveats.unpriced.len() as f64,
833 delta_percent: None,
834 };
835 Some((drift, unpriced_detail(caveats)))
836}
837
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily838/// When the last `days` in a row (each with enough cost to say something)
839/// were all under the floor: the first of them and the worst margin.
840/// Each item is a day's (day, revenue, cost).
841pub(crate) fn breach(series: &[(String, i64, i64)], floor_percent: f64, days: usize, min_cost_micros: i64) -> Option<(String, f64)> {
842 if days == 0 || series.len() < days {
843 return None;
844 }
845 let tail = &series[series.len() - days..];
846 let mut worst = f64::INFINITY;
847 for (_, revenue, cost) in tail {
848 if *cost < min_cost_micros {
849 return None;
850 }
851 let margin = margin_percent(*revenue, *cost).unwrap_or(-100.0);
852 if margin >= floor_percent {
853 return None;
854 }
855 worst = worst.min(margin);
856 }
857 Some((tail[0].0.clone(), worst))
858}
859
860/// `total` shared out in proportion to `weights`, in whole millionths that
861/// add up to it exactly (largest remainder first). Nothing to share, or no
862/// weight, shares nothing.
863pub(crate) fn attribute(total: i64, weights: &[(String, f64)]) -> Vec<(String, i64)> {
864 let mut merged: BTreeMap<String, f64> = BTreeMap::new();
865 for (key, w) in weights {
866 *merged.entry(key.clone()).or_default() += w.max(0.0);
867 }
868 let sum: f64 = merged.values().sum();
869 if total <= 0 || sum <= 0.0 {
870 return Vec::new();
871 }
872 let mut shares: Vec<(String, i64, f64)> = merged
873 .into_iter()
874 .map(|(key, w)| {
875 let exact = total as f64 * w / sum;
876 (key, exact.floor() as i64, exact - exact.floor())
877 })
878 .collect();
879 let mut left = total - shares.iter().map(|s| s.1).sum::<i64>();
880 let mut order: Vec<usize> = (0..shares.len()).collect();
881 order.sort_by(|a, b| shares[*b].2.total_cmp(&shares[*a].2).then(shares[*a].0.cmp(&shares[*b].0)));
882 for index in order {
883 if left <= 0 {
884 break;
885 }
886 shares[index].1 += 1;
887 left -= 1;
888 }
889 shares.into_iter().filter(|s| s.1 > 0).map(|(key, micros, _)| (key, micros)).collect()
890}
891
892/// Workspaces that cost g1t more than `factor` times what they paid, with
893/// at least `floor_micros` of cost: each (workspace, cost, revenue), the
894/// biggest gap first.
Models' margin read -14%: usage nothing paid for is valued at price, not $0895/// What a day's usage was worth at price. g1t's own workspaces are valued
896/// at price. So is usage nothing paid for, neither charged nor drawn from
897/// the plan, a trial, a pool or a gift (a free period): it was given away at
898/// its price, not sold for nothing. Anything paid keeps what it was paid, so
899/// a discount still shows as one.
900pub(crate) fn usage_value(internal: bool, cost: i64, paid: i64, margin_percent: u32) -> i64 {
901 if internal || (paid == 0 && cost > 0) {
902 return crate::credits::with_margin(cost, margin_percent);
903 }
904 paid
905}
906
Margin alerts measure what is sold, and say dollars when a percentage would mislead907/// What the overall alert says: the money as money, and a percentage only
908/// while there is enough coming in for one to mean something (a few cents
909/// against dollars of cost reads as -8000%).
910pub(crate) fn overall_detail(took: i64, spent: i64, days: usize, floor: f64, worst: f64) -> String {
911 if took < 1_000_000 * days as i64 {
912 return format!(
913 "All of g1t, comped workspaces left out: took in {} against {} of Cloudflare's bill over {days} days.",
914 dollars(took),
915 dollars(spent)
916 );
917 }
918 format!("All of g1t, comped workspaces left out: money in against Cloudflare's bill under {floor:.0}% for {days} days running, as low as {worst:.1}%.")
919}
920
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily921pub(crate) fn anomalies(rows: &[(String, i64, i64)], factor: f64, floor_micros: i64) -> Vec<(String, i64, i64)> {
922 let mut out: Vec<(String, i64, i64)> = rows
923 .iter()
924 .filter(|(_, cost, revenue)| *cost >= floor_micros && *cost as f64 > *revenue as f64 * factor)
925 .cloned()
926 .collect();
927 out.sort_by(|a, b| (b.1 - b.2).cmp(&(a.1 - a.2)).then(a.0.cmp(&b.0)));
928 out
929}
930
Merge remote-tracking branch 'origin/main' into workspace-chat931/// Cloudflare's rate for one of its units: the median over the costed
932/// days of cost over quantity, in dollars. None while nothing is costed.
933/// Each item is a day's (quantity, cost), from `rate_line`.
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily934pub(crate) fn billed_rate(days: &[(f64, f64)]) -> Option<f64> {
935 let mut rates: Vec<f64> = days.iter().filter(|(q, c)| *q > 0.0 && *c > 0.0).map(|(q, c)| c / q).collect();
936 if rates.is_empty() {
937 return None;
938 }
939 rates.sort_by(f64::total_cmp);
940 Some(rates[rates.len() / 2])
941}
942
Merge remote-tracking branch 'origin/main' into workspace-chat943/// A line's (quantity, cost) for `billed_rate`: at its list price for all
944/// of the quantity where the meter has one, never what the cycle billed,
945/// which is net of the included amounts (nothing until the cycle passes
946/// them, part of a day's usage on the day it does, then whole millions) and
947/// so says nothing about the price of each unit. A meter with no list
948/// price has only what Cloudflare billed; the median over the charged days
949/// leaves out the day its included amount ran out.
950pub(crate) fn rate_line(product: &str, meter: &str, quantity: f64, cost_usd: f64) -> (f64, f64) {
951 (quantity, crate::cycle::list_cost(product, meter, quantity).unwrap_or(cost_usd))
952}
953
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily954/// What one of g1t's units costs, from Cloudflare's rate per its own unit
955/// and how many of Cloudflare's units each of g1t's took: if Cloudflare
956/// counts three operations for every git operation g1t counts, a git
957/// operation costs three of Cloudflare's. None without enough of g1t's
958/// units to say.
959pub(crate) fn derived_unit_cost(rate_per_cf_unit: f64, cf_units: f64, own_units: f64) -> Option<f64> {
960 (own_units >= MIN_UNITS && cf_units > 0.0 && rate_per_cf_unit > 0.0).then(|| rate_per_cf_unit * cf_units / own_units)
961}
962
963/// How many units a price is per: `1,000 operations` → 1,000, `million
964/// requests` → 1,000,000, `second` → 1.
965pub(crate) fn unit_size(unit: &str) -> f64 {
966 let first = unit.split_whitespace().next().unwrap_or_default().replace(',', "");
967 match first.as_str() {
968 "million" => 1_000_000.0,
969 "thousand" => 1_000.0,
970 n => n.parse().unwrap_or(1.0),
971 }
972}
973
974fn day_before(day: &str, days: u64) -> String {
975 let ms = g1t_contracts::time::parse_rfc3339(&format!("{day}T00:00:00Z")).unwrap_or(0);
976 rfc3339(ms.saturating_sub(days * DAY_MS))[..10].to_owned()
977}
978
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97979fn day_after(day: &str, days: u64) -> String {
980 let ms = g1t_contracts::time::parse_rfc3339(&format!("{day}T00:00:00Z")).unwrap_or(0);
981 rfc3339(ms + days * DAY_MS)[..10].to_owned()
982}
983
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily984/// Dollars to the cent from a dollar up, finer below: `$17.02`, `$0.063`.
985fn dollars(micros: i64) -> String {
986 if micros.abs() >= 1_000_000 {
987 let cents = (micros as f64 / 10_000.0).round() as i64;
988 format!("{}${}.{:02}", if cents < 0 { "-" } else { "" }, cents.abs() / 100, cents.abs() % 100)
989 } else {
990 crate::features::dollars(micros)
991 }
992}
993
994/// The days a plan payment is spread over.
995const PLAN_DAYS: u64 = 30;
996
997/// `micros` paid on `day` spread evenly over `days` days from it, in
998/// whole micros that add up to it (the first days take the remainder).
999pub(crate) fn spread(day: &str, micros: i64, days: u64) -> Vec<(String, i64)> {
1000 if micros <= 0 || days == 0 {
1001 return Vec::new();
1002 }
1003 let start = g1t_contracts::time::parse_rfc3339(&format!("{}T00:00:00Z", &day[..10.min(day.len())])).unwrap_or(0);
1004 let each = micros / days as i64;
1005 let rest = micros % days as i64;
1006 (0..days)
1007 .map(|n| (rfc3339(start + n * DAY_MS)[..10].to_owned(), each + i64::from((n as i64) < rest)))
1008 .collect()
1009}
1010
1011// ---------------------------------------------------------------------
1012// The daily run, and what sudo reads.
1013// ---------------------------------------------------------------------
1014
1015#[derive(Serialize)]
1016struct Mail<'a> {
1017 to: &'a str,
1018 from: &'a str,
1019 subject: &'a str,
1020 text: String,
1021 html: String,
1022}
1023
1024fn escape(text: &str) -> String {
1025 text.replace('&', "&amp;").replace('<', "&lt;").replace('>', "&gt;").replace('"', "&quot;")
1026}
1027
1028/// Emails staff through Cloudflare Email Sending, the `EMAIL` binding.
Spend caps: a monthly budget for comped workspaces and a daily breaker on what g1t pays1029pub(crate) async fn email_staff(env: &Env, to: &str, subject: &str, lines: &[String]) -> Result<()> {
Merge branch 'main' into actions-toolkit-oidc-artifacts1030 email_staff_page(env, to, subject, lines, ("Costs & margin", "https://sudo.g1t.sh/costs"), "g1t-billing's margin guard").await
1031}
1032
1033/// Emails staff, linking to a page of sudo (`page`: its name and address)
1034/// and saying what sent it.
1035pub(crate) async fn email_staff_page(env: &Env, to: &str, subject: &str, lines: &[String], page: (&str, &str), sender: &str) -> Result<()> {
1036 let (name, link) = page;
1037 let text = format!("{}\n\n{name}: {link}\n\nSent by {sender} (COSTS_ALERT_EMAIL).\n", lines.join("\n\n"));
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1038 let mut html = String::from("<div style=\"font-family:system-ui,sans-serif;max-width:560px;margin:0 auto;padding:24px 16px;color:#16150f\">");
1039 for line in lines {
1040 html.push_str(&format!("<p style=\"font-size:15px;line-height:1.6\">{}</p>", escape(line)));
1041 }
1042 html.push_str(&format!(
Merge branch 'main' into actions-toolkit-oidc-artifacts1043 "<p><a href=\"{link}\">Open {} in sudo</a></p><p style=\"font-size:13px;color:#6e6a5e\">Sent by {} (COSTS_ALERT_EMAIL).</p></div>",
1044 escape(name),
1045 escape(sender)
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1046 ));
1047 let mail = Mail { to, from: "g1t <noreply@g1t.sh>", subject, text, html };
1048 let binding = g1t_kit::js::binding(env, "EMAIL")?;
1049 g1t_kit::js::call(&binding, "send", &[g1t_kit::js::to_js(&mail)?]).await?;
1050 Ok(())
1051}
1052
1053#[derive(Deserialize)]
1054struct AlertRow {
1055 id: String,
1056 kind: String,
1057 subject: String,
1058 detail: String,
1059 since: String,
1060 opened_at: String,
1061 emailed_at: Option<String>,
1062}
1063
1064impl From<AlertRow> for MarginAlert {
1065 fn from(r: AlertRow) -> Self {
1066 MarginAlert { id: r.id, kind: r.kind, subject: r.subject, detail: r.detail, since: r.since, opened_at: r.opened_at, emailed_at: r.emailed_at }
1067 }
1068}
1069
1070#[derive(Deserialize)]
1071struct MarginRow {
1072 day: String,
1073 bucket: String,
1074 cf_cost_micros: i64,
1075 own_cost_micros: i64,
1076 value_micros: i64,
1077 cash_micros: i64,
1078 cf_quantity: f64,
1079 own_quantity: f64,
Costs: margin is measured on what was sold; comped workspaces, free periods, the trial and the pools are given away, a budget shown beside it1080 #[serde(default)]
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running1081 given_comped_micros: Option<i64>,
1082 #[serde(default)]
1083 given_free_micros: Option<i64>,
1084 #[serde(default)]
1085 given_trial_micros: Option<i64>,
1086 #[serde(default)]
1087 given_pool_micros: Option<i64>,
Merge branch 'worktree-agent-a633ac0f7f66d419d'1088 #[serde(default)]
1089 given_discount_micros: Option<i64>,
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging1090 #[serde(default)]
1091 given_credit_promotional_micros: Option<i64>,
1092 #[serde(default)]
1093 given_credit_goodwill_micros: Option<i64>,
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb971094 #[serde(default)]
1095 given_reset_micros: Option<i64>,
Merge Cloudflare's usage over its billing cycle: every page read, included amounts once a cycle, a projection, test-mode charges never money in (billing 0052)1096 #[serde(default)]
1097 given_unpaid_micros: Option<i64>,
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1098}
1099
1100impl From<MarginRow> for ProductDay {
1101 fn from(r: MarginRow) -> Self {
1102 ProductDay {
1103 day: r.day,
1104 bucket: r.bucket,
1105 cf_cost_micros: r.cf_cost_micros,
1106 own_cost_micros: r.own_cost_micros,
1107 value_micros: r.value_micros,
1108 cash_micros: r.cash_micros,
1109 cf_quantity: r.cf_quantity,
1110 own_quantity: r.own_quantity,
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running1111 given: Given {
1112 comped: r.given_comped_micros.unwrap_or(0),
1113 free: r.given_free_micros.unwrap_or(0),
1114 trial: r.given_trial_micros.unwrap_or(0),
1115 pool: r.given_pool_micros.unwrap_or(0),
Merge branch 'worktree-agent-a633ac0f7f66d419d'1116 discount: r.given_discount_micros.unwrap_or(0),
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging1117 credit_promotional: r.given_credit_promotional_micros.unwrap_or(0),
1118 credit_goodwill: r.given_credit_goodwill_micros.unwrap_or(0),
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb971119 reset: r.given_reset_micros.unwrap_or(0),
Merge Cloudflare's usage over its billing cycle: every page read, included amounts once a cycle, a projection, test-mode charges never money in (billing 0052)1120 unpaid: r.given_unpaid_micros.unwrap_or(0),
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running1121 },
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1122 }
1123 }
1124}
1125
1126impl Billing {
1127 /// The day's work: read Cloudflare's bill and g1t's own counts,
1128 /// reconcile, look for drift, measure unit costs, apply prices whose
1129 /// day has come, and raise or clear alerts.
1130 pub(crate) async fn costs_daily(&self, env: &Env, keeper: &crate::keeper::Keeper) -> Result<CostsRun> {
1131 let mut run = CostsRun::default();
Merge costs and margin review: gateway query, own spend, discount meters, superseded rises1132 let mut gateway = costs::GatewayRead::default();
Merge Cloudflare's usage over its billing cycle: every page read, included amounts once a cycle, a projection, test-mode charges never money in (billing 0052)1133 // The subscriptions first: they say when the billing cycle starts,
1134 // which the bill is priced by. Not a problem for the run: the last
1135 // read, or the estimate, stays.
1136 if keeper.can_read_bill()
1137 && let Err(error) = self.read_subscriptions(keeper).await
1138 {
1139 worker::console_error!("Cloudflare's subscriptions were not read: {error}");
1140 }
1141 let (since, until, bill_since) = match self.read_cloudflare(keeper, &mut run.problems, &mut gateway).await? {
1142 Some((since, until, lines, bill_since)) => {
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1143 run.lines = lines;
Merge Cloudflare's usage over its billing cycle: every page read, included amounts once a cycle, a projection, test-mode charges never money in (billing 0052)1144 (since, until, Some(bill_since))
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1145 }
1146 // Without the bill, still reconcile what g1t knows itself, over
1147 // the same days the bill would be read for.
1148 None => {
1149 #[derive(Deserialize)]
1150 struct Last {
1151 day: Option<String>,
1152 }
1153 let last = self.db.prepare("SELECT MAX(day) AS day FROM margin_days").first::<Last>(None).await?.and_then(|l| l.day);
Merge Cloudflare's usage over its billing cycle: every page read, included amounts once a cycle, a projection, test-mode charges never money in (billing 0052)1154 let (since, until) = costs::window(last.as_deref(), now_ms());
1155 (since, until, None)
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1156 }
1157 };
1158 if let Err(error) = self.count_own(&since, &until).await {
1159 run.problems.push(format!("g1t's own counts could not be read: {error}"));
1160 }
1161 self.snapshot_pending(&until).await?;
Models' margin read -14%: usage nothing paid for is valued at price, not $01162 // Reconciled over the whole window sudo shows, not only the days the
1163 // bill was read for: it reads only what is already kept, so a change
1164 // in how a day is valued reaches every day shown at the next run.
1165 let window = day_before(&until, costs::BACKFILL_DAYS - 1);
Merge Cloudflare's usage over its billing cycle: every page read, included amounts once a cycle, a projection, test-mode charges never money in (billing 0052)1166 let reconcile_from = [Some(window), Some(since.clone()), bill_since].into_iter().flatten().min().unwrap_or_default();
Models' margin read -14%: usage nothing paid for is valued at price, not $01167 run.days = self.reconcile_range(&reconcile_from, &until).await?;
Merge costs and margin review: gateway query, own spend, discount meters, superseded rises1168 let drift = self.find_drift(&until, &gateway).await?;
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1169 run.proposals = self.measure_units(&until).await?;
1170 self.apply_due_versions().await?;
1171 run.alerts = self.raise_alerts(env, &until, &drift).await?;
1172 if let Some(identity) = &self.identity
1173 && let Err(error) = self.tell_owners_of_rises(identity).await
1174 {
1175 run.problems.push(format!("owners could not be told of a price rise: {error}"));
1176 }
1177 for problem in &run.problems {
1178 worker::console_warn!("costs: {problem}");
1179 }
1180 Ok(run)
1181 }
1182
1183 /// What each month-end source had come to by the end of `day`.
1184 async fn snapshot_pending(&self, day: &str) -> Result<()> {
1185 self.db
1186 .prepare(
1187 "INSERT INTO pending_days (day, workspace, source, cost_micros, charge_micros)
1188 SELECT ?1, workspace, source, COALESCE(cost_micros, 0), COALESCE(charge_micros, 0) FROM pending_usage WHERE month = ?2
1189 ON CONFLICT (day, workspace, source) DO UPDATE SET cost_micros = excluded.cost_micros, charge_micros = excluded.charge_micros",
1190 )
1191 .bind(&[day.into(), day[..7].into()])?
1192 .run()
1193 .await?;
1194 Ok(())
1195 }
1196
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb971197 /// What customers were charged on the days, by workspace and key: every
1198 /// workspace's, or only `only`'s.
1199 async fn usage_rows(&self, since: &str, until: &str, only: Option<&str>) -> Result<Vec<UsageRow>> {
1200 let only_sql = only.unwrap_or("");
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1201 #[derive(Deserialize)]
1202 struct Row {
1203 day: String,
1204 workspace: String,
1205 key: String,
1206 internal: i64,
1207 own_provider: i64,
1208 cash: Option<i64>,
1209 drawn: Option<i64>,
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running1210 trial: Option<i64>,
1211 oss: Option<i64>,
1212 covered: Option<i64>,
Merge branch 'worktree-agent-a633ac0f7f66d419d'1213 discount: Option<i64>,
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1214 cost: Option<i64>,
1215 }
1216 let charged_here = crate::storage::CHARGED_HERE.iter().map(|s| format!("'{s}'")).collect::<Vec<_>>().join(", ");
1217 let end = format!("{until}T23:59:59.999Z");
1218 let rows = self
1219 .db
1220 .prepare(format!(
1221 "SELECT substr(created_at, 1, 10) AS day, workspace,
Merge costs and margin review: gateway query, own spend, discount meters, superseded rises1222 CASE WHEN task = 'deployments' AND reference LIKE 'deploy/%' THEN 'builds'
1223 WHEN task = 'plan' THEN '{planning}' ELSE COALESCE(task, 'other') END AS key,
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1224 CASE WHEN workspace IN ({internal}) THEN 1 ELSE 0 END AS internal,
1225 CASE WHEN billed_to = 'workspace' THEN 1 ELSE 0 END AS own_provider,
1226 -SUM(amount_micros) AS cash,
1227 SUM(COALESCE(credit_micros, 0) + COALESCE(trial_micros, 0) + COALESCE(oss_micros, 0) + COALESCE(given_micros, 0)) AS drawn,
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running1228 SUM(COALESCE(trial_micros, 0)) AS trial,
1229 SUM(COALESCE(oss_micros, 0)) AS oss,
1230 SUM(COALESCE(given_micros, 0)) AS covered,
Merge branch 'worktree-agent-a633ac0f7f66d419d'1231 SUM(COALESCE(discount_micros, 0)) AS discount,
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1232 SUM(COALESCE(cost_micros, 0)) AS cost
1233 FROM ledger
1234 WHERE kind = 'usage' AND created_at >= ?1 AND created_at <= ?2 AND COALESCE(task, '') NOT IN ({charged_here})
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb971235 AND (?3 = '' OR workspace = ?3)
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1236 GROUP BY 1, 2, 3, 4, 5",
Merge costs and margin review: gateway query, own spend, discount meters, superseded rises1237 internal = crate::sales::INTERNAL_SQL,
1238 planning = PLANNING_KEY
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1239 ))
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb971240 .bind(&[since.into(), end.as_str().into(), only_sql.into()])?
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1241 .all()
1242 .await?
1243 .results::<Row>()?;
Costs: margin is measured on what was sold; comped workspaces, free periods, the trial and the pools are given away, a budget shown beside it1244 let mut internal = BTreeSet::new();
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1245 let mut out: Vec<UsageRow> = rows
1246 .into_iter()
1247 .map(|r| {
1248 // A workspace's own model provider was paid there: no cost
1249 // to g1t. g1t's own workspaces are valued at price.
1250 let cost = if r.own_provider == 1 { 0 } else { r.cost.unwrap_or(0) };
1251 let cash = r.cash.unwrap_or(0);
Merge branch 'worktree-agent-a633ac0f7f66d419d'1252 // A discount took its part below cost plus the margin: it is
1253 // valued at price and that part counted as given, so a
1254 // discounted sale never reads as margin lost.
1255 let discount = r.discount.unwrap_or(0).max(0);
1256 let paid = cash + r.drawn.unwrap_or(0) + discount;
Models' margin read -14%: usage nothing paid for is valued at price, not $01257 let value = usage_value(r.internal == 1, cost, paid, self.margin_percent);
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running1258 let given = if r.internal == 1 {
1259 Given { comped: value, ..Given::default() }
1260 } else if paid == 0 && cost > 0 {
1261 Given { free: value, ..Given::default() }
1262 } else {
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging1263 Given { free: r.covered.unwrap_or(0), trial: r.trial.unwrap_or(0), pool: r.oss.unwrap_or(0), discount, ..Given::default() }
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running1264 };
Costs: margin is measured on what was sold; comped workspaces, free periods, the trial and the pools are given away, a budget shown beside it1265 if r.internal == 1 {
1266 internal.insert(r.workspace.clone());
1267 }
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb971268 UsageRow { day: r.day, workspace: r.workspace, key: r.key, bucket: None, value, cash, cost, given }
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1269 })
1270 .collect();
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging1271 // Credits from g1t: what promotional and goodwill credit paid for
1272 // is given, not money in; a refund gives money back on its day.
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb971273 let (mut draws, mut refunds) = self.credit_effects(since, until).await?;
1274 if let Some(only) = only {
1275 draws.retain(|(workspace, _)| workspace == only);
1276 refunds.retain(|r| r.workspace == only);
1277 }
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging1278 apply_credits(&mut out, &draws, &refunds);
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1279 // Month-end sources, from their daily snapshots.
1280 #[derive(Deserialize)]
1281 struct Snap {
1282 day: String,
1283 workspace: String,
1284 source: String,
1285 cost_micros: i64,
1286 charge_micros: i64,
1287 }
1288 let snaps = self
1289 .db
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb971290 .prepare("SELECT day, workspace, source, cost_micros, charge_micros FROM pending_days WHERE day >= ?1 AND day <= ?2 AND (?3 = '' OR workspace = ?3)")
1291 .bind(&[day_before(since, 1).into(), until.into(), only_sql.into()])?
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1292 .all()
1293 .await?
1294 .results::<Snap>()?
1295 .into_iter()
1296 .filter(|s| crate::storage::CHARGED_HERE.contains(&s.source.as_str()) || s.source == "domains")
1297 .map(|s| (s.day, s.workspace, s.source, s.cost_micros, s.charge_micros))
1298 .collect::<Vec<_>>();
Merge costs and margin review: gateway query, own spend, discount meters, superseded rises1299 out.extend(
1300 pending_deltas(&snaps)
1301 .into_iter()
1302 .filter(|u| u.day.as_str() >= since)
1303 .map(|u| if internal.contains(&u.workspace) { comped_meter(u) } else { u }),
1304 );
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1305 // The plan's price, spread over the 30 days it pays for, so a month's
1306 // payment does not read as one very good day and 29 bad ones.
1307 #[derive(Deserialize)]
1308 struct Plan {
1309 day: String,
1310 workspace: String,
1311 micros: Option<i64>,
1312 }
1313 let plans = self
1314 .db
1315 .prepare(
1316 "SELECT substr(paid_at, 1, 10) AS day, workspace, SUM(amount_micros) AS micros FROM plan_payments
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb971317 WHERE paid_at >= ?1 AND paid_at <= ?2 AND (?3 = '' OR workspace = ?3) GROUP BY 1, 2",
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1318 )
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb971319 .bind(&[day_before(since, PLAN_DAYS - 1).into(), end.as_str().into(), only_sql.into()])?
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1320 .all()
1321 .await?
1322 .results::<Plan>()?;
1323 for p in plans {
1324 for (day, micros) in spread(&p.day, p.micros.unwrap_or(0), PLAN_DAYS) {
1325 if day.as_str() >= since && day.as_str() <= until {
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb971326 out.push(UsageRow { day, workspace: p.workspace.clone(), key: "plan".into(), bucket: None, value: micros, cash: micros, cost: 0, given: Given::default() });
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1327 }
1328 }
1329 }
Merge Cloudflare's usage over its billing cycle: every page read, included amounts once a cycle, a projection, test-mode charges never money in (billing 0052)1330 // Charges without real money behind them are not money in.
1331 without_real_money(&mut out, self.payments_live_since().await?.as_deref());
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1332 Ok(out)
1333 }
1334
Merge Cloudflare's usage over its billing cycle: every page read, included amounts once a cycle, a projection, test-mode charges never money in (billing 0052)1335 /// The day payments went live (`live_since`): kept in `cost_settings`
1336 /// the first time they are seen live, so charges from before it stay
1337 /// test money after the switch.
1338 pub(crate) async fn payments_live_since(&self) -> Result<Option<String>> {
1339 #[derive(Deserialize)]
1340 struct Row {
1341 value: String,
1342 }
1343 let live = self.stripe.as_ref().is_some_and(crate::stripe::Stripe::live);
1344 let kept = self
1345 .db
1346 .prepare("SELECT value FROM cost_settings WHERE key = 'payments_live_since'")
1347 .first::<Row>(None)
1348 .await?
1349 .map(|r| r.value)
1350 .filter(|v| v.len() >= 10);
1351 let today = rfc3339(now_ms())[..10].to_owned();
1352 let since = live_since(live, kept.as_deref(), &today);
1353 if let Some(day) = since.as_deref().filter(|_| kept.is_none()) {
1354 self.db
1355 .prepare(
1356 "INSERT INTO cost_settings (key, value, updated_at, updated_by) VALUES ('payments_live_since', ?1, ?2, 'billing')
1357 ON CONFLICT (key) DO UPDATE SET value = ?1, updated_at = ?2, updated_by = 'billing'",
1358 )
1359 .bind(&[day.into(), rfc3339(now_ms()).into()])?
1360 .run()
1361 .await?;
1362 }
1363 Ok(since)
1364 }
1365
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb971366 /// Which bucket each ledger key (and month-end source) is revenue of.
1367 async fn revenue_map(&self) -> Result<BTreeMap<String, String>> {
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1368 #[derive(Deserialize)]
1369 struct Map {
1370 key: String,
1371 bucket: String,
1372 }
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb971373 Ok(self
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1374 .db
1375 .prepare("SELECT key, bucket FROM revenue_map")
1376 .all()
1377 .await?
1378 .results::<Map>()?
1379 .into_iter()
1380 .map(|m| (m.key, m.bucket))
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb971381 .collect())
1382 }
1383
1384 /// What a testing reset of `workspace` is about to wipe that g1t paid
1385 /// for, a row per day and bucket: its whole ledger and month-end
1386 /// snapshots, valued as the reconciliation values them.
1387 pub(crate) async fn wiped_by_reset(&self, workspace: &str) -> Result<Vec<Wiped>> {
1388 let today = rfc3339(now_ms())[..10].to_owned();
1389 let rows = self.usage_rows(RESET_HISTORY_FROM, &today, Some(workspace)).await?;
1390 Ok(wiped(&rows, &self.revenue_map().await?, self.margin_percent))
1391 }
1392
1393 /// What testing resets kept for the days, as usage rows.
1394 async fn reset_rows(&self, since: &str, until: &str) -> Result<Vec<UsageRow>> {
1395 #[derive(Deserialize)]
1396 struct Kept {
1397 day: String,
1398 workspace: String,
1399 bucket: String,
1400 cost: Option<i64>,
1401 value: Option<i64>,
1402 }
1403 let kept = self
1404 .db
1405 .prepare(
1406 "SELECT day, workspace, bucket, SUM(cost_micros) AS cost, SUM(value_micros) AS value FROM reset_costs
1407 WHERE day >= ?1 AND day <= ?2 AND bucket <> '' GROUP BY day, workspace, bucket",
1408 )
1409 .bind(&[since.into(), until.into()])?
1410 .all()
1411 .await?
1412 .results::<Kept>()?;
1413 Ok(reset_usage(
1414 &kept.into_iter().map(|k| (k.day, k.workspace, k.bucket, k.cost.unwrap_or(0), k.value.unwrap_or(0))).collect::<Vec<_>>(),
1415 ))
1416 }
1417
1418 /// Testing resets on or after `since` (the day they wiped usage up to
1419 /// is their own, so one before it wiped nothing in the days): those
1420 /// that kept what they wiped (`reset_costs`) and those from before
1421 /// resets did, known only from the audit log.
1422 async fn resets_since(&self, since: &str, until: &str) -> Result<Vec<ResetNote>> {
1423 let end = format!("{until}T23:59:59.999Z");
1424 #[derive(Deserialize)]
1425 struct Audit {
1426 account: String,
1427 created_at: String,
1428 }
1429 let audits = self
1430 .db
1431 .prepare("SELECT account, created_at FROM admin_actions WHERE action = 'reset' AND created_at >= ?1 AND created_at <= ?2")
1432 .bind(&[since.into(), end.as_str().into()])?
1433 .all()
1434 .await?
1435 .results::<Audit>()?;
1436 #[derive(Deserialize)]
1437 struct Kept {
1438 workspace: String,
1439 reset_at: String,
1440 models: Option<i64>,
1441 }
1442 let kept = self
1443 .db
1444 .prepare(
1445 "SELECT workspace, reset_at, SUM(CASE WHEN bucket = ?3 AND day >= ?1 THEN cost_micros ELSE 0 END) AS models
1446 FROM reset_costs WHERE reset_at >= ?1 AND reset_at <= ?2 GROUP BY workspace, reset_at",
1447 )
1448 .bind(&[since.into(), end.as_str().into(), NOT_CLOUDFLARE[0].into()])?
1449 .all()
1450 .await?
1451 .results::<Kept>()?;
1452 Ok(reset_notes(
1453 &audits.into_iter().map(|a| (a.account, a.created_at)).collect::<Vec<_>>(),
1454 &kept.into_iter().map(|k| (k.workspace, k.reset_at, k.models.unwrap_or(0))).collect::<Vec<_>>(),
1455 ))
1456 }
1457
1458 /// Reconciles the days and writes `margin_days` and `workspace_costs`.
1459 async fn reconcile_range(&self, since: &str, until: &str) -> Result<u32> {
1460 let rules = self.rules().await?;
1461 let revenue_map = self.revenue_map().await?;
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1462 let lines = self
1463 .db
1464 .prepare("SELECT day, source, product, meter, quantity, cost_usd FROM cost_lines WHERE day >= ?1 AND day <= ?2")
1465 .bind(&[since.into(), until.into()])?
1466 .all()
1467 .await?
1468 .results::<LineRow>()?;
1469 let own = self
1470 .db
1471 .prepare("SELECT day, meter, workspace, quantity FROM own_counts WHERE day >= ?1 AND day <= ?2")
1472 .bind(&[since.into(), until.into()])?
1473 .all()
1474 .await?
1475 .results::<OwnRow>()?;
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb971476 let mut usage = self.usage_rows(since, until, None).await?;
1477 // What testing resets wiped: still paid for, now given away.
1478 usage.extend(self.reset_rows(since, until).await?);
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running1479 #[derive(Deserialize)]
1480 struct Internal {
1481 workspace: String,
1482 }
1483 let internal: BTreeSet<String> = self
1484 .db
1485 .prepare(format!("WITH i(workspace) AS ({}) SELECT DISTINCT workspace FROM i", crate::sales::INTERNAL_SQL))
1486 .all()
1487 .await?
1488 .results::<Internal>()?
1489 .into_iter()
1490 .map(|i| i.workspace)
1491 .collect();
1492 let (days, workspaces) = fold(&rules, &revenue_map, &lines, &own, &usage, &internal);
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1493 let now = rfc3339(now_ms());
1494 self.db
1495 .batch(vec![
1496 self.db.prepare("DELETE FROM margin_days WHERE day >= ?1 AND day <= ?2").bind(&[since.into(), until.into()])?,
1497 self.db.prepare("DELETE FROM workspace_costs WHERE day >= ?1 AND day <= ?2").bind(&[since.into(), until.into()])?,
1498 ])
1499 .await?;
1500 for chunk in days.chunks(50) {
1501 let mut statements = Vec::with_capacity(chunk.len());
1502 for d in chunk {
1503 statements.push(
1504 self.db
1505 .prepare(
Merge Cloudflare's usage over its billing cycle: every page read, included amounts once a cycle, a projection, test-mode charges never money in (billing 0052)1506 "INSERT OR REPLACE INTO margin_days (day, bucket, cf_cost_micros, own_cost_micros, value_micros, cash_micros, cf_quantity, own_quantity, given_micros, given_comped_micros, given_free_micros, given_trial_micros, given_pool_micros, given_discount_micros, given_credit_promotional_micros, given_credit_goodwill_micros, given_reset_micros, given_unpaid_micros, computed_at)
1507 VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1508 )
1509 .bind(&[
1510 d.day.as_str().into(),
1511 d.bucket.as_str().into(),
1512 (d.cf_cost_micros as f64).into(),
1513 (d.own_cost_micros as f64).into(),
1514 (d.value_micros as f64).into(),
1515 (d.cash_micros as f64).into(),
1516 d.cf_quantity.into(),
1517 d.own_quantity.into(),
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running1518 (d.given.total() as f64).into(),
1519 (d.given.comped as f64).into(),
1520 (d.given.free as f64).into(),
1521 (d.given.trial as f64).into(),
1522 (d.given.pool as f64).into(),
Merge branch 'worktree-agent-a633ac0f7f66d419d'1523 (d.given.discount as f64).into(),
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging1524 (d.given.credit_promotional as f64).into(),
1525 (d.given.credit_goodwill as f64).into(),
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb971526 (d.given.reset as f64).into(),
Merge Cloudflare's usage over its billing cycle: every page read, included amounts once a cycle, a projection, test-mode charges never money in (billing 0052)1527 (d.given.unpaid as f64).into(),
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1528 now.as_str().into(),
1529 ])?,
1530 );
1531 }
1532 self.db.batch(statements).await?;
1533 }
1534 for chunk in workspaces.chunks(50) {
1535 let mut statements = Vec::with_capacity(chunk.len());
1536 for w in chunk {
1537 statements.push(
1538 self.db
Costs: margin is measured on what was sold; comped workspaces, free periods, the trial and the pools are given away, a budget shown beside it1539 .prepare("INSERT OR REPLACE INTO workspace_costs (day, workspace, bucket, cost_micros, revenue_micros, value_micros, given_micros) VALUES (?, ?, ?, ?, ?, ?, ?)")
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1540 .bind(&[
1541 w.day.as_str().into(),
1542 w.workspace.as_str().into(),
1543 w.bucket.as_str().into(),
1544 (w.cost as f64).into(),
1545 (w.revenue as f64).into(),
Margin alerts measure what is sold, and say dollars when a percentage would mislead1546 (w.value as f64).into(),
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running1547 (w.given.total() as f64).into(),
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1548 ])?,
1549 );
1550 }
1551 self.db.batch(statements).await?;
1552 }
1553 Ok(costs::days_between(since, until).len() as u32)
1554 }
1555
1556 async fn margin_days(&self, since: &str, until: &str) -> Result<Vec<ProductDay>> {
1557 Ok(self
1558 .db
1559 .prepare("SELECT * FROM margin_days WHERE day >= ?1 AND day <= ?2 ORDER BY day, bucket")
1560 .bind(&[since.into(), until.into()])?
1561 .all()
1562 .await?
1563 .results::<MarginRow>()?
1564 .into_iter()
1565 .map(ProductDay::from)
1566 .collect())
1567 }
1568
Merge branch 'worktree-agent-a633ac0f7f66d419d'1569 /// What AI Gateway's lines over the days, and the runs settled in them,
1570 /// say about whether its cost is what the providers bill.
1571 async fn gateway_caveats(&self, since: &str, until: &str) -> Result<costs::GatewayCaveats> {
1572 #[derive(Deserialize)]
1573 struct Line {
1574 meter: String,
1575 quantity: f64,
1576 cost_usd: f64,
1577 }
1578 let lines: Vec<(String, f64, f64)> = self
1579 .db
1580 .prepare("SELECT meter, quantity, cost_usd FROM cost_lines WHERE source = ?1 AND day >= ?2 AND day <= ?3")
1581 .bind(&[costs::SOURCE_GATEWAY.into(), since.into(), until.into()])?
1582 .all()
1583 .await?
1584 .results::<Line>()?
1585 .into_iter()
1586 .map(|l| (l.meter, l.quantity, l.cost_usd))
1587 .collect();
1588 let mut caveats = costs::gateway_caveats(&lines);
1589 #[derive(Deserialize)]
1590 struct Short {
1591 n: Option<f64>,
1592 }
1593 caveats.short_runs = self
1594 .db
1595 .prepare("SELECT COUNT(*) AS n FROM runs WHERE gateway_note IS NOT NULL AND settled_at >= ?1 AND settled_at <= ?2")
1596 .bind(&[since.into(), format!("{until}T23:59:59.999Z").into()])?
1597 .first::<Short>(None)
1598 .await?
1599 .and_then(|s| s.n)
1600 .unwrap_or(0.0) as u32;
1601 Ok(caveats)
1602 }
1603
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1604 /// Drift over the last week, written to `cost_drift` (replacing the
1605 /// last run's), with unmapped Cloudflare meters as leaks.
Merge costs and margin review: gateway query, own spend, discount meters, superseded rises1606 async fn find_drift(&self, until: &str, read: &costs::GatewayRead) -> Result<Vec<(Drift, String)>> {
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1607 let since = day_before(until, DRIFT_DAYS - 1);
1608 let settings = self.cost_settings().await?;
1609 let rules = self.rules().await?;
1610 let days = self.margin_days(&since, until).await?;
1611 let mut by: BTreeMap<String, Vec<ProductDay>> = BTreeMap::new();
1612 for d in days {
1613 by.entry(d.bucket.clone()).or_default().push(d);
1614 }
Merge branch 'worktree-agent-a633ac0f7f66d419d'1615 let caveats = self.gateway_caveats(&since, until).await?;
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb971616 let resets = self.resets_since(&since, until).await?;
Merge remote-tracking branch 'origin/main' into workspace-chat1617 // The same days' usage at list prices, before the included amounts:
1618 // what the price book is checked against (never the bill, which is
1619 // net of them).
1620 let lines = self
1621 .db
1622 .prepare("SELECT day, source, product, meter, quantity, cost_usd FROM cost_lines WHERE source = ?1 AND day >= ?2 AND day <= ?3")
1623 .bind(&[SOURCE_BILLABLE.into(), since.as_str().into(), until.into()])?
1624 .all()
1625 .await?
1626 .results::<LineRow>()?;
1627 let list = list_costs(&rules, &lines);
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1628 let mut found = Vec::new();
Merge branch 'worktree-agent-a633ac0f7f66d419d'1629 if let Some(drift) = unpriced_drift(&caveats) {
1630 found.push(drift);
1631 }
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1632 for (bucket, days) in &by {
1633 let bucket_rules: Vec<&Rule> = rules.iter().filter(|r| &r.bucket == bucket).collect();
1634 let threshold = bucket_rules.iter().map(|r| r.drift_percent).fold(f64::INFINITY, f64::min);
1635 let threshold = if threshold.is_finite() { threshold } else { 10.0 };
1636 let counted = bucket_rules.iter().any(|r| r.own_meter.is_some());
Merge remote-tracking branch 'origin/main' into workspace-chat1637 let list_micros = list.get(bucket).copied().flatten();
1638 for drift in drifts(bucket, days, threshold, counted, settings.min_daily_cost_micros, list_micros) {
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb971639 if wiped_not_leaked(&drift, &resets) {
1640 continue;
1641 }
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1642 let title = costs::bucket_title(bucket);
1643 let detail = match drift.kind {
Merge costs and margin review: gateway query, own spend, discount meters, superseded rises1644 DriftKind::Cost if NOT_CLOUDFLARE.contains(&bucket.as_str()) => models_detail(&drift, &caveats, &resets, read),
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1645 DriftKind::Count => format!(
One operation mapping, owned by repos; billing reads it instead of keeping its own1646 "{title}: g1t counted {}, Cloudflare {} over the last {DRIFT_DAYS} days ({:+.1}%). Customers are charged for what g1t counts; check what Cloudflare counts as a unit and change the repos service's operation_mapping (set_operation_mapping).",
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1647 crate::features::thousands(drift.ours.max(0.0).round() as u64),
1648 crate::features::thousands(drift.cloudflare.max(0.0).round() as u64),
1649 drift.delta_percent.unwrap_or(0.0)
1650 ),
1651 DriftKind::Cost => format!(
Merge remote-tracking branch 'origin/main' into workspace-chat1652 "{title}: the last {DRIFT_DAYS} days' usage comes to {} at Cloudflare's list prices, before the included amounts; the price book's cost of the same usage is {} ({:+.1}%). A price may be stale: see the proposals.",
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1653 dollars(drift.cloudflare as i64),
1654 dollars(drift.ours as i64),
1655 drift.delta_percent.unwrap_or(0.0)
1656 ),
1657 DriftKind::Leak if bucket == UNMAPPED => {
1658 format!("Cloudflare charged {} for meters no mapping claims. Map them on Costs & margin.", dollars(drift.cloudflare as i64))
1659 }
Merge branch 'worktree-agent-a633ac0f7f66d419d'1660 DriftKind::Leak if NOT_CLOUDFLARE.contains(&bucket.as_str()) => format!(
1661 "{title}: AI Gateway priced g1t's own provider traffic at {} over the last {DRIFT_DAYS} days and the ledger has no model charge for it, not even a comped or free one: model calls with no billing run behind them (a run started without a ticket, or something else using g1t's gateway).",
1662 dollars(drift.cloudflare as i64)
1663 ),
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1664 DriftKind::Leak => format!(
1665 "{title}: Cloudflare charged {} over the last {DRIFT_DAYS} days and customers were charged nothing for it.",
1666 dollars(drift.cloudflare as i64)
1667 ),
Merge branch 'worktree-agent-a633ac0f7f66d419d'1668 // Raised from the gateway's lines, not per bucket.
1669 DriftKind::Unpriced => unpriced_detail(&caveats),
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1670 };
1671 found.push((drift, detail));
1672 }
1673 }
1674 let now = rfc3339(now_ms());
1675 let mut statements = vec![self.db.prepare("DELETE FROM cost_drift")];
1676 for (drift, detail) in &found {
1677 statements.push(
1678 self.db
1679 .prepare("INSERT OR REPLACE INTO cost_drift (bucket, kind, ours, cloudflare, delta_percent, detail, found_at) VALUES (?, ?, ?, ?, ?, ?, ?)")
1680 .bind(&[
1681 drift.bucket.as_str().into(),
1682 drift.kind.as_str().into(),
1683 drift.ours.into(),
1684 drift.cloudflare.into(),
1685 drift.delta_percent.map_or(JsValue::NULL, JsValue::from),
1686 detail.as_str().into(),
1687 now.as_str().into(),
1688 ])?,
1689 );
1690 }
1691 self.db.batch(statements).await?;
1692 Ok(found)
1693 }
1694
1695 /// Unit costs from the bill for mappings that scale to g1t's own count
1696 /// (git operations), proposed to the price book.
1697 async fn measure_units(&self, until: &str) -> Result<u32> {
1698 #[derive(Deserialize)]
1699 struct Scaled {
1700 product: String,
1701 meter: String,
1702 price_meter: String,
1703 own_meter: String,
1704 unit: Option<String>,
1705 }
1706 let scaled = self
1707 .db
1708 .prepare(
1709 "SELECT m.product, m.meter, m.price_meter, m.own_meter, p.unit FROM cost_map m LEFT JOIN prices p ON p.meter = m.price_meter
1710 WHERE m.scale_to_own = 1 AND m.price_meter IS NOT NULL AND m.own_meter IS NOT NULL",
1711 )
1712 .all()
1713 .await?
1714 .results::<Scaled>()?;
1715 let since = day_before(until, MEASURE_DAYS - 1);
1716 let rules = self.rules().await?;
1717 let mut proposed = 0;
1718 for s in scaled {
1719 #[derive(Deserialize)]
1720 struct Day {
1721 product: String,
1722 meter: String,
1723 quantity: f64,
1724 cost_usd: f64,
1725 }
1726 let lines = self
1727 .db
1728 .prepare("SELECT product, meter, quantity, cost_usd FROM cost_lines WHERE source = ?1 AND product = ?2 AND day >= ?3 AND day <= ?4")
1729 .bind(&[SOURCE_BILLABLE.into(), s.product.as_str().into(), since.as_str().into(), until.into()])?
1730 .all()
1731 .await?
1732 .results::<Day>()?;
1733 // Only the lines this very mapping claims.
1734 let mine: Vec<(f64, f64)> = lines
1735 .iter()
1736 .filter(|l| costs::classify(&rules, &l.product, &l.meter).is_some_and(|r| r.product == s.product && r.meter == s.meter))
Merge remote-tracking branch 'origin/main' into workspace-chat1737 .map(|l| rate_line(&l.product, &l.meter, l.quantity, l.cost_usd))
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1738 .collect();
1739 let Some(rate) = billed_rate(&mine) else { continue };
1740 let cf_units: f64 = mine.iter().map(|(q, _)| q).sum();
1741 #[derive(Deserialize)]
1742 struct Own {
1743 total: Option<f64>,
1744 }
1745 let own_units = self
1746 .db
1747 .prepare("SELECT SUM(quantity) AS total FROM own_counts WHERE meter = ?1 AND day >= ?2 AND day <= ?3")
1748 .bind(&[s.own_meter.as_str().into(), since.as_str().into(), until.into()])?
1749 .first::<Own>(None)
1750 .await?
1751 .and_then(|o| o.total)
1752 .unwrap_or(0.0);
1753 let Some(per_unit) = derived_unit_cost(rate, cf_units, own_units) else { continue };
1754 let size = unit_size(s.unit.as_deref().unwrap_or("1"));
1755 let measured = per_unit * size * 1_000_000.0;
1756 let reason = format!(
1757 "Cloudflare billed ${:.4} per 1,000 of its units and counted {:.2} of them for each one g1t counted over the last {MEASURE_DAYS} days ({} against {})",
1758 rate * 1000.0,
1759 cf_units / own_units,
1760 crate::features::thousands(cf_units.round() as u64),
1761 crate::features::thousands(own_units.round() as u64)
1762 );
1763 if self.propose(&s.price_meter, measured, &reason, "reconciler").await?.is_some() {
1764 proposed += 1;
1765 }
1766 }
1767 Ok(proposed)
1768 }
1769
1770 /// Opens, updates and closes margin alerts, and emails staff about new
1771 /// ones (and open ones each week).
1772 async fn raise_alerts(&self, env: &Env, until: &str, drift: &[(Drift, String)]) -> Result<u32> {
1773 let settings = self.cost_settings().await?;
1774 let since = day_before(until, u64::from(settings.alert_days.max(1)) - 1);
1775 let days = self.margin_days(&since, until).await?;
1776 let mut conditions: Vec<(String, String, String, String)> = Vec::new();
1777 // Each product under the floor.
1778 let mut by: BTreeMap<String, Vec<(String, i64, i64)>> = BTreeMap::new();
1779 let mut all: BTreeMap<String, (i64, i64)> = BTreeMap::new();
1780 for d in &days {
1781 let overall = all.entry(d.day.clone()).or_default();
Costs: margin is measured on what was sold; comped workspaces, free periods, the trial and the pools are given away, a budget shown beside it1782 // What g1t gave away (comped workspaces, free periods, the
1783 // trial and the pools) is a budget it chose to spend, watched on
1784 // its own (budget.rs): not part of whether what is sold pays.
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1785 overall.0 += d.cash_micros;
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running1786 overall.1 += (d.cost() - d.given.total()).max(0);
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1787 if !OVERHEAD.contains(&d.bucket.as_str()) && d.bucket != UNMAPPED {
1788 by.entry(d.bucket.clone()).or_default().push((d.day.clone(), d.value_micros, d.cost()));
1789 }
1790 }
1791 let floor = settings.margin_floor_percent;
1792 let n = settings.alert_days as usize;
1793 for (bucket, series) in &by {
1794 if let Some((from, worst)) = breach(series, floor, n, settings.min_daily_cost_micros) {
1795 conditions.push((
1796 "margin".into(),
1797 bucket.clone(),
1798 format!("{}: margin under {floor:.0}% for {n} days running, as low as {worst:.1}%.", costs::bucket_title(bucket)),
1799 from,
1800 ));
Margin alerts measure what is sold, and say dollars when a percentage would mislead1801 }
1802 }
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1803 let series: Vec<(String, i64, i64)> = all.into_iter().map(|(day, (revenue, cost))| (day, revenue, cost)).collect();
1804 if let Some((from, worst)) = breach(&series, floor, n, settings.min_daily_cost_micros) {
Margin alerts measure what is sold, and say dollars when a percentage would mislead1805 let tail = &series[series.len().saturating_sub(n)..];
1806 let (took, spent) = tail.iter().fold((0i64, 0i64), |(r, c), (_, revenue, cost)| (r + revenue, c + cost));
1807 conditions.push(("overall".into(), "g1t".into(), overall_detail(took, spent, n, floor, worst), from));
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1808 }
1809 for (d, detail) in drift {
1810 let kind = if d.kind == DriftKind::Leak { "leak" } else { "drift" };
1811 conditions.push((kind.into(), format!("{}:{}", d.bucket, d.kind.as_str()), detail.clone(), until.to_owned()));
1812 }
1813 // Workspaces costing more than they pay.
1814 for (workspace, cost, revenue) in self.workspace_anomalies(until, &settings).await? {
1815 conditions.push((
1816 "workspace".into(),
1817 workspace.clone(),
Margin alerts measure what is sold, and say dollars when a percentage would mislead1818 format!(
1819 "{workspace} cost g1t {} on Cloudflare over {ANOMALY_DAYS} days, and its usage was priced at {}: its prices are below cost.",
1820 dollars(cost),
1821 dollars(revenue)
1822 ),
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1823 day_before(until, ANOMALY_DAYS - 1),
1824 ));
1825 }
1826
1827 let open = self
1828 .db
1829 .prepare("SELECT * FROM margin_alerts WHERE resolved_at IS NULL")
1830 .all()
1831 .await?
1832 .results::<AlertRow>()?;
1833 let now = now_ms();
1834 let stamp = rfc3339(now);
1835 let mut to_email: Vec<String> = Vec::new();
1836 let mut kept: BTreeSet<String> = BTreeSet::new();
1837 for (kind, subject, detail, from) in &conditions {
1838 match open.iter().find(|a| &a.kind == kind && &a.subject == subject) {
1839 Some(alert) => {
1840 kept.insert(alert.id.clone());
1841 self.db
1842 .prepare("UPDATE margin_alerts SET detail = ? WHERE id = ?")
1843 .bind(&[detail.as_str().into(), alert.id.as_str().into()])?
1844 .run()
1845 .await?;
1846 let stale = alert
1847 .emailed_at
1848 .as_deref()
1849 .and_then(g1t_contracts::time::parse_rfc3339)
1850 .is_none_or(|at| now.saturating_sub(at) >= REMIND_MS);
1851 if stale && kind != "workspace" {
1852 to_email.push(format!("Still open: {detail}"));
1853 kept.insert(format!("email:{}", alert.id));
1854 }
1855 }
1856 None => {
1857 let id = new_id("mal", now);
1858 self.db
1859 .prepare("INSERT INTO margin_alerts (id, kind, subject, detail, since, opened_at) VALUES (?, ?, ?, ?, ?, ?)")
1860 .bind(&[id.as_str().into(), kind.as_str().into(), subject.as_str().into(), detail.as_str().into(), from.as_str().into(), stamp.as_str().into()])?
1861 .run()
1862 .await?;
1863 kept.insert(id.clone());
Margin alerts measure what is sold, and say dollars when a percentage would mislead1864 // A workspace's is for Reach out, not the inbox.
1865 if kind != "workspace" {
1866 to_email.push(detail.clone());
1867 }
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1868 kept.insert(format!("email:{id}"));
1869 }
1870 }
1871 }
1872 for alert in &open {
1873 if !kept.contains(&alert.id) {
1874 self.db
1875 .prepare("UPDATE margin_alerts SET resolved_at = ? WHERE id = ?")
1876 .bind(&[stamp.as_str().into(), alert.id.as_str().into()])?
1877 .run()
1878 .await?;
1879 }
1880 }
1881 let to = env.var("COSTS_ALERT_EMAIL").map(|v| v.to_string()).unwrap_or_default();
1882 if !to_email.is_empty() && !to.trim().is_empty() {
1883 let subject = format!("[g1t costs] {} margin alert{}", to_email.len(), if to_email.len() == 1 { "" } else { "s" });
1884 match email_staff(env, to.trim(), &subject, &to_email).await {
1885 Ok(()) => {
1886 for marker in kept.iter().filter_map(|k| k.strip_prefix("email:")) {
1887 self.db
1888 .prepare("UPDATE margin_alerts SET emailed_at = ? WHERE id = ?")
1889 .bind(&[stamp.as_str().into(), marker.into()])?
1890 .run()
1891 .await?;
1892 }
1893 }
1894 Err(error) => worker::console_error!("could not email the margin alerts: {error}"),
1895 }
1896 }
1897 Ok(conditions.len() as u32)
1898 }
1899
1900 /// Workspaces costing g1t more than they pay over 30 days, not g1t's own.
Margin alerts measure what is sold, and say dollars when a percentage would mislead1901 /// Each day's cost shared out to comped workspaces.
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1902 async fn workspace_anomalies(&self, until: &str, settings: &CostSettings) -> Result<Vec<(String, i64, i64)>> {
1903 #[derive(Deserialize)]
1904 struct Row {
1905 workspace: String,
1906 cost: Option<i64>,
1907 revenue: Option<i64>,
1908 }
1909 let rows = self
1910 .db
1911 .prepare(format!(
Margin alerts measure what is sold, and say dollars when a percentage would mislead1912 // Against what its usage was priced at, not the cash it
1913 // paid: a trial or a gift paying for usage is not a price
1914 // below cost.
The workspace cost alert compares only days that carry their value, not the days before it was kept1915 // Days from before value_micros was kept have none: only days
1916 // since the first one that does are compared.
Margin alerts measure what is sold, and say dollars when a percentage would mislead1917 "SELECT workspace, SUM(cost_micros) AS cost, SUM(value_micros) AS revenue FROM workspace_costs
The workspace cost alert compares only days that carry their value, not the days before it was kept1918 WHERE day >= ?1 AND day <= ?2 AND workspace NOT IN ({})
1919 AND day >= (SELECT MIN(day) FROM workspace_costs WHERE value_micros > 0)
1920 GROUP BY workspace",
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1921 crate::sales::INTERNAL_SQL
1922 ))
1923 .bind(&[day_before(until, ANOMALY_DAYS - 1).into(), until.into()])?
1924 .all()
1925 .await?
1926 .results::<Row>()?;
1927 let rows: Vec<(String, i64, i64)> = rows.into_iter().map(|r| (r.workspace, r.cost.unwrap_or(0), r.revenue.unwrap_or(0))).collect();
1928 Ok(anomalies(&rows, settings.anomaly_factor, settings.anomaly_floor_micros))
1929 }
1930
1931 /// For Reach out: workspaces with an open cost-over-revenue alert,
1932 /// each with its detail and cost.
1933 pub(crate) async fn costing_more_than_they_pay(&self) -> Result<Vec<(String, String, i64)>> {
1934 let alerts = self
1935 .db
1936 .prepare("SELECT * FROM margin_alerts WHERE resolved_at IS NULL AND kind = 'workspace' ORDER BY opened_at DESC LIMIT 50")
1937 .all()
1938 .await?
1939 .results::<AlertRow>()?;
1940 let mut out = Vec::new();
1941 for alert in alerts {
1942 #[derive(Deserialize)]
1943 struct Cost {
1944 cost: Option<i64>,
1945 }
1946 let cost = self
1947 .db
1948 .prepare("SELECT SUM(cost_micros) AS cost FROM workspace_costs WHERE workspace = ? AND day >= ?")
1949 .bind(&[alert.subject.as_str().into(), alert.since.as_str().into()])?
1950 .first::<Cost>(None)
1951 .await?
1952 .and_then(|c| c.cost)
1953 .unwrap_or(0);
1954 out.push((alert.subject, alert.detail, cost));
1955 }
1956 Ok(out)
1957 }
1958
1959 /// `admin_cost_alerts`: what sudo's banner says.
1960 pub(crate) async fn admin_cost_alerts(&self, _: AdminCostAlertsArgs) -> Result<Vec<MarginAlert>> {
1961 Ok(self
1962 .db
1963 .prepare("SELECT * FROM margin_alerts WHERE resolved_at IS NULL ORDER BY opened_at DESC LIMIT 50")
1964 .all()
1965 .await?
1966 .results::<AlertRow>()?
1967 .into_iter()
1968 .map(MarginAlert::from)
1969 .collect())
1970 }
1971
1972 /// `admin_run_costs`: the daily run, now.
1973 pub(crate) async fn admin_run_costs(&self, env: &Env, a: AdminRunCostsArgs) -> Result<Outcome<CostsRun>> {
1974 let keeper = crate::keeper::Keeper::from_env(env);
1975 let run = self.costs_daily(env, &keeper).await?;
1976 if !a.by.is_empty() {
1977 self.audit(
1978 "costs",
1979 "costs_run",
1980 &format!("{} lines, {} days, {} proposals, {} alerts", run.lines, run.days, run.proposals, run.alerts),
1981 &a.by,
1982 )
1983 .await?;
1984 }
1985 Ok(Outcome::Ok(run))
1986 }
1987
1988 /// `admin_set_cost_mapping`.
1989 pub(crate) async fn admin_set_cost_mapping(&self, a: AdminSetCostMappingArgs) -> Result<Outcome<CostMapping>> {
1990 let product = costs::slug(&a.product);
1991 let meter = if a.meter.trim() == "*" { "*".to_owned() } else { costs::slug(&a.meter) };
1992 if product.is_empty() || meter.is_empty() {
1993 return Ok(Outcome::fail(FailureCode::Invalid, "Name Cloudflare's product and a meter (or * for all of it)."));
1994 }
1995 let now = rfc3339(now_ms());
1996 if a.remove {
1997 self.db
1998 .prepare("DELETE FROM cost_map WHERE product = ? AND meter = ?")
1999 .bind(&[product.as_str().into(), meter.as_str().into()])?
2000 .run()
2001 .await?;
2002 self.audit("costs", "cost_mapping_removed", &format!("{product}/{meter}"), &a.by).await?;
2003 return Ok(Outcome::Ok(CostMapping {
2004 product,
2005 meter,
2006 bucket: String::new(),
2007 price_meter: None,
2008 own_meter: None,
2009 scale_to_own: false,
2010 drift_percent: 0.0,
2011 note: String::new(),
2012 updated_at: now,
2013 updated_by: a.by,
2014 }));
2015 }
2016 let bucket = costs::slug(&a.bucket);
2017 if bucket.is_empty() {
2018 return Ok(Outcome::fail(FailureCode::Invalid, "Say which of g1t's products it is a cost of."));
2019 }
2020 let clean = |v: Option<String>| v.map(|v| v.trim().to_owned()).filter(|v| !v.is_empty());
2021 let (price_meter, own_meter) = (clean(a.price_meter), clean(a.own_meter));
2022 let drift = a.drift_percent.filter(|d| d.is_finite() && *d > 0.0).unwrap_or(10.0);
2023 self.db
2024 .prepare(
2025 "INSERT INTO cost_map (product, meter, bucket, price_meter, own_meter, scale_to_own, drift_percent, note, updated_at, updated_by)
2026 VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10)
2027 ON CONFLICT (product, meter) DO UPDATE SET bucket = ?3, price_meter = ?4, own_meter = ?5, scale_to_own = ?6,
2028 drift_percent = ?7, note = ?8, updated_at = ?9, updated_by = ?10",
2029 )
2030 .bind(&[
2031 product.as_str().into(),
2032 meter.as_str().into(),
2033 bucket.as_str().into(),
2034 crate::optional(price_meter.as_deref()),
2035 crate::optional(own_meter.as_deref()),
2036 i32::from(a.scale_to_own).into(),
2037 drift.into(),
2038 a.note.trim().into(),
2039 now.as_str().into(),
2040 a.by.as_str().into(),
2041 ])?
2042 .run()
2043 .await?;
2044 self.audit("costs", "cost_mapping", &format!("{product}/{meter} → {bucket}"), &a.by).await?;
2045 Ok(Outcome::Ok(CostMapping {
2046 product,
2047 meter,
2048 bucket,
2049 price_meter,
2050 own_meter,
2051 scale_to_own: a.scale_to_own,
2052 drift_percent: drift,
2053 note: a.note.trim().to_owned(),
2054 updated_at: now,
2055 updated_by: a.by,
2056 }))
2057 }
2058
2059 /// `admin_costs`: the Costs & margin page.
2060 pub(crate) async fn admin_costs(&self, a: AdminCostsArgs, configured: bool) -> Result<CostsReport> {
2061 let until = rfc3339(now_ms())[..10].to_owned();
2062 let span = u64::from(a.days.unwrap_or(30).clamp(7, 90));
2063 let since = day_before(&until, span - 1);
2064 let days = self.margin_days(&since, &until).await?;
2065 let rules = self.rules().await?;
2066
2067 let mut products: BTreeMap<String, ProductMargin> = BTreeMap::new();
2068 let mut overall = OverallMargin::default();
2069 for d in &days {
2070 let p = products.entry(d.bucket.clone()).or_insert_with(|| ProductMargin {
2071 bucket: d.bucket.clone(),
2072 title: costs::bucket_title(&d.bucket),
2073 cost_source: if NOT_CLOUDFLARE.contains(&d.bucket.as_str()) { "ledger" } else { "cloudflare" }.into(),
2074 overhead: OVERHEAD.contains(&d.bucket.as_str()),
2075 ..ProductMargin::default()
2076 });
2077 p.cf_cost_micros += d.cf_cost_micros;
2078 p.own_cost_micros += d.own_cost_micros;
2079 p.value_micros += d.value_micros;
2080 p.cost_micros += d.cost();
2081 overall.cost_micros += d.cost();
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running2082 overall.given_micros += d.given.total();
2083 if NOT_CLOUDFLARE.contains(&d.bucket.as_str()) {
2084 overall.models_cost_micros += d.cost();
2085 } else {
2086 overall.cloudflare_cost_micros += d.cost();
2087 }
2088 overall.given_comped_micros += d.given.comped;
2089 overall.given_free_micros += d.given.free;
2090 overall.given_trial_micros += d.given.trial;
2091 overall.given_pool_micros += d.given.pool;
Merge branch 'worktree-agent-a633ac0f7f66d419d'2092 overall.given_discount_micros += d.given.discount;
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging2093 overall.given_credit_promotional_micros += d.given.credit_promotional;
2094 overall.given_credit_goodwill_micros += d.given.credit_goodwill;
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb972095 overall.given_reset_micros += d.given.reset;
Merge Cloudflare's usage over its billing cycle: every page read, included amounts once a cycle, a projection, test-mode charges never money in (billing 0052)2096 overall.given_unpaid_micros += d.given.unpaid;
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running2097 let sold = (d.cost() - d.given.total()).max(0);
2098 if OVERHEAD.contains(&d.bucket.as_str()) {
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily2099 overall.plans_micros += d.cash_micros;
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running2100 overall.running_cost_micros += sold;
2101 } else if d.bucket == UNMAPPED {
2102 overall.usage_micros += d.cash_micros;
2103 overall.unmapped_cost_micros += sold;
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily2104 } else {
2105 overall.usage_micros += d.cash_micros;
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running2106 overall.usage_cost_micros += sold;
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily2107 }
2108 }
2109 for p in products.values_mut() {
2110 p.margin_micros = p.value_micros - p.cost_micros;
2111 p.margin_percent = margin_percent(p.value_micros, p.cost_micros);
2112 }
2113 let revenue = overall.usage_micros + overall.plans_micros;
2114 overall.margin_micros = revenue - overall.cost_micros;
2115 overall.margin_percent = margin_percent(revenue, overall.cost_micros);
Costs: margin is measured on what was sold; comped workspaces, free periods, the trial and the pools are given away, a budget shown beside it2116 let sold = (overall.cost_micros - overall.given_micros).max(0);
2117 overall.sold_margin_micros = revenue - sold;
2118 overall.sold_margin_percent = margin_percent(revenue, sold);
Costs: the plan's included usage counts as paid for the usage it covered, out of what plans leave for running g1t; the run button shows it is running with CSS alone (sudo ships no JavaScript)2119 // The plan's included usage was paid for by the plan's price: it is
2120 // money in for the usage it covered, and out of what the plans
2121 // leave for running g1t.
2122 #[derive(Deserialize)]
2123 struct Included {
2124 micros: Option<i64>,
2125 }
Merge Cloudflare's usage over its billing cycle: every page read, included amounts once a cycle, a projection, test-mode charges never money in (billing 0052)2126 // Only what a plan paid for with real money (`without_real_money`).
2127 let live_since = self.payments_live_since().await?;
2128 overall.included_micros = match &live_since {
2129 None => 0,
2130 Some(live) => self
2131 .db
2132 .prepare(format!(
2133 "SELECT SUM(COALESCE(credit_micros, 0)) AS micros FROM ledger
2134 WHERE kind = 'usage' AND created_at >= ?1 AND created_at <= ?2 AND workspace NOT IN ({})",
2135 crate::sales::INTERNAL_SQL
2136 ))
2137 .bind(&[std::cmp::max(since.clone(), live.clone()).into(), format!("{until}T23:59:59.999Z").into()])?
2138 .first::<Included>(None)
2139 .await?
2140 .and_then(|r| r.micros)
2141 .unwrap_or(0),
2142 };
Costs: the plan's included usage counts as paid for the usage it covered, out of what plans leave for running g1t; the run button shows it is running with CSS alone (sudo ships no JavaScript)2143 let usage_in = overall.usage_micros + overall.included_micros;
2144 overall.usage_margin_micros = usage_in - overall.usage_cost_micros;
2145 overall.usage_margin_percent = margin_percent(usage_in, overall.usage_cost_micros);
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging2146 // Credits from g1t over the range: given, spent, and refunds' money
2147 // given back.
2148 overall.credits_given_micros = self
2149 .db
2150 .prepare("SELECT SUM(amount_micros) AS micros FROM credit_grants WHERE created_at >= ?1 AND created_at <= ?2")
2151 .bind(&[since.as_str().into(), format!("{until}T23:59:59.999Z").into()])?
2152 .first::<Included>(None)
2153 .await?
2154 .and_then(|r| r.micros)
2155 .unwrap_or(0);
2156 let (draws, refunds) = self.credit_effects(&since, &until).await?;
2157 overall.credits_used_micros = draws.iter().map(|(_, d)| d.micros).sum();
2158 overall.credits_refunded_micros = refunds.iter().map(|r| r.micros).sum();
Merge Stripe Tax, the card fee on card payments, and one free workspace per person2159 // Tax and card fees came in with payments but are neither cash nor
2160 // revenue: balances and plan payments are credited without them
2161 // (tax.rs), so cash above never holds them. Shown apart.
2162 (overall.tax_collected_micros, overall.card_fees_micros) = self.extras_between(&since, &until).await?;
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily2163 let mut products: Vec<ProductMargin> = products.into_values().collect();
2164 products.sort_by_key(|p| std::cmp::Reverse(p.cost_micros.max(p.value_micros)));
2165
2166 #[derive(Deserialize)]
2167 struct DriftRow {
2168 bucket: String,
2169 kind: String,
2170 ours: f64,
2171 cloudflare: f64,
2172 delta_percent: Option<f64>,
2173 detail: String,
2174 found_at: String,
2175 }
2176 let drift = self
2177 .db
2178 .prepare("SELECT * FROM cost_drift ORDER BY kind, bucket")
2179 .all()
2180 .await?
2181 .results::<DriftRow>()?
2182 .into_iter()
2183 .map(|r| CostDrift {
2184 title: costs::bucket_title(&r.bucket),
2185 bucket: r.bucket,
2186 kind: r.kind,
2187 ours: r.ours,
2188 cloudflare: r.cloudflare,
2189 delta_percent: r.delta_percent,
2190 detail: r.detail,
2191 found_at: r.found_at,
2192 })
2193 .collect();
2194
2195 #[derive(Deserialize)]
2196 struct Top {
2197 workspace: String,
2198 cost: Option<i64>,
2199 revenue: Option<i64>,
Costs: margin is measured on what was sold; comped workspaces, free periods, the trial and the pools are given away, a budget shown beside it2200 given: Option<i64>,
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily2201 internal: i64,
2202 }
2203 let top_workspaces = self
2204 .db
2205 .prepare(format!(
Costs: margin is measured on what was sold; comped workspaces, free periods, the trial and the pools are given away, a budget shown beside it2206 "SELECT workspace, SUM(cost_micros) AS cost, SUM(revenue_micros) AS revenue, SUM(given_micros) AS given,
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily2207 CASE WHEN workspace IN ({}) THEN 1 ELSE 0 END AS internal
2208 FROM workspace_costs WHERE day >= ?1 AND day <= ?2 GROUP BY workspace ORDER BY cost DESC LIMIT 15",
2209 crate::sales::INTERNAL_SQL
2210 ))
2211 .bind(&[since.as_str().into(), until.as_str().into()])?
2212 .all()
2213 .await?
2214 .results::<Top>()?
2215 .into_iter()
Costs: margin is measured on what was sold; comped workspaces, free periods, the trial and the pools are given away, a budget shown beside it2216 .map(|t| WorkspaceCost { workspace: t.workspace, cost_micros: t.cost.unwrap_or(0), revenue_micros: t.revenue.unwrap_or(0), given_micros: t.given.unwrap_or(0), internal: t.internal == 1 })
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily2217 .collect();
2218
2219 #[derive(Deserialize)]
2220 struct Summary {
2221 source: String,
2222 product: String,
2223 meter: String,
2224 raw_name: String,
2225 unit: String,
2226 quantity: f64,
2227 cost_usd: f64,
2228 }
2229 let lines = self
2230 .db
2231 .prepare(
2232 "SELECT source, product, meter, MAX(raw_name) AS raw_name, MAX(unit) AS unit, SUM(quantity) AS quantity, SUM(cost_usd) AS cost_usd
2233 FROM cost_lines WHERE day >= ?1 AND day <= ?2 GROUP BY source, product, meter ORDER BY cost_usd DESC, product, meter LIMIT 200",
2234 )
2235 .bind(&[since.as_str().into(), until.as_str().into()])?
2236 .all()
2237 .await?
2238 .results::<Summary>()?
2239 .into_iter()
2240 .map(|l| CostLineSummary {
2241 bucket: costs::classify(&rules, &l.product, &l.meter).map(|r| r.bucket.clone()),
2242 product: l.product,
2243 meter: l.meter,
2244 raw_name: l.raw_name,
2245 unit: l.unit,
2246 source: l.source,
2247 quantity: l.quantity,
2248 cost_micros: micros(l.cost_usd),
2249 })
2250 .collect();
2251
2252 #[derive(Deserialize)]
2253 struct MapRow {
2254 product: String,
2255 meter: String,
2256 bucket: String,
2257 price_meter: Option<String>,
2258 own_meter: Option<String>,
2259 scale_to_own: i64,
2260 drift_percent: f64,
2261 note: String,
2262 updated_at: String,
2263 updated_by: String,
2264 }
2265 let mappings = self
2266 .db
2267 .prepare("SELECT * FROM cost_map ORDER BY product, meter")
2268 .all()
2269 .await?
2270 .results::<MapRow>()?
2271 .into_iter()
2272 .map(|m| CostMapping {
2273 product: m.product,
2274 meter: m.meter,
2275 bucket: m.bucket,
2276 price_meter: m.price_meter,
2277 own_meter: m.own_meter,
2278 scale_to_own: m.scale_to_own == 1,
2279 drift_percent: m.drift_percent,
2280 note: m.note,
2281 updated_at: m.updated_at,
2282 updated_by: m.updated_by,
2283 })
2284 .collect();
2285
2286 #[derive(Deserialize)]
2287 struct Fetched {
2288 at: Option<String>,
2289 }
2290 let fetched_at = self.db.prepare("SELECT MAX(fetched_at) AS at FROM cost_lines").first::<Fetched>(None).await?.and_then(|f| f.at);
2291
Merge Cloudflare's usage over its billing cycle: every page read, included amounts once a cycle, a projection, test-mode charges never money in (billing 0052)2292 // Cloudflare's subscriptions over the range, day by day as each
2293 // day's share of its billing cycle; and the cycle itself.
2294 let anchor = self.cycle_anchor().await?;
2295 let fixed = self.fixed_monthly(self.caps.fixed_monthly).await?;
2296 overall.subscriptions_micros = crate::cycle::accrued(fixed.monthly_micros, &since, &until, anchor);
2297 let cycle = self.cloudflare_cycle(&until, anchor, fixed.monthly_micros).await?;
2298 let bill_read = self.bill_read().await?;
2299 // What AI Gateway priced g1t's own provider traffic at, beside the
2300 // ledger's model cost (Cloudflare-billed requests are Cloudflare's).
2301 overall.gateway_cost_micros = self
2302 .db
2303 .prepare("SELECT SUM(cost_usd) AS cost FROM cost_lines WHERE source = ?1 AND day >= ?2 AND day <= ?3 AND substr(meter, 1, 11) <> ?4")
2304 .bind(&[costs::SOURCE_GATEWAY.into(), since.as_str().into(), until.as_str().into(), costs::GATEWAY_WHOLESALE.into()])?
2305 .first::<CostSum>(None)
2306 .await?
2307 .and_then(|c| c.cost)
2308 .map_or(0, micros);
2309 // The workspaces' shares of the cost, and what no one's usage carried.
2310 #[derive(Deserialize)]
2311 struct Shared {
2312 micros: Option<i64>,
2313 }
2314 let shared = self
2315 .db
2316 .prepare("SELECT SUM(cost_micros) AS micros FROM workspace_costs WHERE day >= ?1 AND day <= ?2")
2317 .bind(&[since.as_str().into(), until.as_str().into()])?
2318 .first::<Shared>(None)
2319 .await?
2320 .and_then(|s| s.micros)
2321 .unwrap_or(0);
2322 let unattributed_micros = unattributed(overall.cost_micros, shared);
2323
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily2324 Ok(CostsReport {
2325 configured,
2326 fetched_at,
2327 days: days
2328 .iter()
2329 .map(|d| CostDay {
2330 day: d.day.clone(),
2331 bucket: d.bucket.clone(),
2332 cf_cost_micros: d.cf_cost_micros,
2333 own_cost_micros: d.own_cost_micros,
2334 value_micros: d.value_micros,
2335 cash_micros: d.cash_micros,
2336 })
2337 .collect(),
2338 since,
2339 until,
2340 products,
2341 overall,
2342 drift,
2343 alerts: self.admin_cost_alerts(AdminCostAlertsArgs {}).await?,
2344 proposals: self.proposals().await?,
2345 versions: self.versions().await?,
2346 top_workspaces,
2347 lines,
2348 mappings,
2349 settings: self.cost_settings().await?,
Spend caps: a monthly budget for comped workspaces and a daily breaker on what g1t pays2350 caps: self.spend_caps().await?,
Merge Cloudflare's usage over its billing cycle: every page read, included amounts once a cycle, a projection, test-mode charges never money in (billing 0052)2351 cycle,
2352 bill_read,
2353 unattributed_micros,
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily2354 })
2355 }
Merge Cloudflare's usage over its billing cycle: every page read, included amounts once a cycle, a projection, test-mode charges never money in (billing 0052)2356
2357 /// Cloudflare's billing cycle that `today` is in: its usage cost so far,
2358 /// by meter, with the included amounts, and where it is heading. None
2359 /// while nothing of it has been read.
2360 async fn cloudflare_cycle(&self, today: &str, anchor: u32, monthly_micros: i64) -> Result<Option<CloudflareCycle>> {
2361 #[derive(Deserialize)]
2362 struct Row {
2363 product: String,
2364 meter: String,
2365 raw_name: String,
2366 unit: String,
2367 quantity: f64,
2368 billable_quantity: Option<f64>,
2369 cost_usd: f64,
2370 basis: Option<String>,
2371 }
2372 let cycle = crate::cycle::cycle_of(today, anchor);
2373 let rows = self
2374 .db
2375 .prepare(
2376 "SELECT product, meter, MAX(raw_name) AS raw_name, MAX(unit) AS unit, SUM(quantity) AS quantity,
2377 SUM(billable_quantity) AS billable_quantity, SUM(cost_usd) AS cost_usd, MAX(basis) AS basis
2378 FROM cost_lines WHERE source = ?1 AND day >= ?2 AND day <= ?3 GROUP BY product, meter",
2379 )
2380 .bind(&[SOURCE_BILLABLE.into(), cycle.start.as_str().into(), today.into()])?
2381 .all()
2382 .await?
2383 .results::<Row>()?;
2384 if rows.is_empty() {
2385 return Ok(None);
2386 }
2387 let elapsed = cycle.days_elapsed(today);
2388 let meters = rows
2389 .into_iter()
2390 .map(|r| {
2391 let list = crate::cycle::list_price(&r.product, &r.meter);
2392 CycleMeter {
2393 included: list.map(|p| if p.daily { p.included * elapsed as f64 } else { p.included }),
2394 billable_quantity: r.billable_quantity.unwrap_or(0.0),
2395 cost_micros: micros(r.cost_usd),
2396 basis: r.basis.filter(|b| !b.is_empty()).unwrap_or_else(|| crate::cycle::BASIS_NONE.to_owned()),
2397 product: r.product,
2398 meter: r.meter,
2399 raw_name: r.raw_name,
2400 unit: r.unit,
2401 quantity: r.quantity,
2402 }
2403 })
2404 .collect();
2405 Ok(Some(cycle_report(&cycle, elapsed, meters, monthly_micros)))
2406 }
2407
2408 /// The last read of billable usage (`cost_reads`).
2409 async fn bill_read(&self) -> Result<Option<BillRead>> {
2410 #[derive(Deserialize)]
2411 struct Row {
2412 read_at: String,
2413 since: String,
2414 until: String,
2415 rows: u32,
2416 pages: u32,
2417 consumed_rows: u32,
2418 pricing_only_rows: u32,
2419 costed_rows: u32,
2420 }
2421 Ok(self
2422 .db
2423 .prepare("SELECT * FROM cost_reads WHERE source = ?1")
2424 .bind(&[SOURCE_BILLABLE.into()])?
2425 .first::<Row>(None)
2426 .await?
2427 .map(|r| BillRead {
2428 read_at: r.read_at,
2429 since: r.since,
2430 until: r.until,
2431 rows: r.rows,
2432 pages: r.pages,
2433 consumed_rows: r.consumed_rows,
2434 pricing_only_rows: r.pricing_only_rows,
2435 costed_rows: r.costed_rows,
2436 }))
2437 }
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily2438}
2439
Merge Cloudflare's usage over its billing cycle: every page read, included amounts once a cycle, a projection, test-mode charges never money in (billing 0052)2440#[derive(Deserialize)]
2441struct CostSum {
2442 cost: Option<f64>,
2443}
2444
2445/// Of `total` cost, what the workspaces' shares did not carry: running g1t
2446/// on days no workspace used anything. Never below zero.
2447pub(crate) fn unattributed(total: i64, shared: i64) -> i64 {
2448 (total - shared).max(0)
2449}
2450
2451/// The cycle as sudo shows it: the meters, most costly first, their total,
2452/// the average day and Cloudflare's projection.
2453pub(crate) fn cycle_report(cycle: &crate::cycle::Cycle, elapsed: u32, mut meters: Vec<CycleMeter>, monthly_micros: i64) -> CloudflareCycle {
2454 meters.sort_by(|a, b| b.cost_micros.cmp(&a.cost_micros).then(b.quantity.total_cmp(&a.quantity)).then(a.meter.cmp(&b.meter)));
2455 let usage: i64 = meters.iter().map(|m| m.cost_micros).sum();
2456 let days = cycle.days();
2457 CloudflareCycle {
2458 start: cycle.start.clone(),
2459 end: cycle.end.clone(),
2460 days,
2461 days_elapsed: elapsed,
2462 usage_micros: usage,
2463 projected_micros: crate::cycle::project(usage, elapsed, days),
2464 average_daily_micros: if elapsed > 0 { usage / elapsed as i64 } else { usage },
2465 subscriptions_micros: monthly_micros,
2466 meters,
2467 }
2468}
2469
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily2470#[cfg(test)]
2471mod tests {
2472 use super::*;
2473
Margin alerts measure what is sold, and say dollars when a percentage would mislead2474 #[test]
Models' margin read -14%: usage nothing paid for is valued at price, not $02475 fn usage_nothing_paid_for_is_valued_at_price_and_paid_usage_at_what_was_paid() {
2476 // A free period: charged nothing, drawn from nothing.
2477 assert_eq!(usage_value(false, 1_000_000, 0, 20), 1_200_000);
2478 // Charged, or drawn from a trial: what was paid.
2479 assert_eq!(usage_value(false, 1_000_000, 1_200_000, 20), 1_200_000);
2480 assert_eq!(usage_value(false, 1_000_000, 900_000, 20), 900_000);
2481 // g1t's own: at price.
2482 assert_eq!(usage_value(true, 1_000_000, 0, 20), 1_200_000);
2483 // No cost, nothing paid: nothing.
2484 assert_eq!(usage_value(false, 0, 0, 20), 0);
2485 }
2486
2487 #[test]
Margin alerts measure what is sold, and say dollars when a percentage would mislead2488 fn the_overall_alert_says_dollars_while_little_comes_in() {
2489 let small = overall_detail(90_000, 7_500_000, 3, 10.0, -8239.7);
2490 assert!(small.contains("took in $0.09 against $7.50"), "{small}");
2491 assert!(!small.contains('%'), "{small}");
2492 let real = overall_detail(30_000_000, 40_000_000, 3, 10.0, -33.3);
2493 assert!(real.contains("as low as -33.3%"), "{real}");
2494 }
2495
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily2496 fn rule(product: &str, meter: &str, bucket: &str, own: Option<&str>) -> Rule {
2497 Rule { product: product.into(), meter: meter.into(), bucket: bucket.into(), price_meter: None, own_meter: own.map(Into::into), drift_percent: 10.0 }
2498 }
2499
2500 fn rules() -> Vec<Rule> {
2501 vec![
2502 rule("containers", "*", "sandboxes", None),
2503 rule("workers", "*", "platform", None),
2504 rule("artifacts", "*", "git", Some("git_operations")),
2505 rule("artifacts", "events_", "git", Some("git_operations")),
2506 ]
2507 }
2508
2509 fn revenue_map() -> BTreeMap<String, String> {
2510 [("sandbox", "sandboxes"), ("git", "git"), ("plan", "platform")].iter().map(|(k, v)| (k.to_string(), v.to_string())).collect()
2511 }
2512
2513 fn line(day: &str, source: &str, product: &str, meter: &str, quantity: f64, cost: f64) -> LineRow {
2514 LineRow { day: day.into(), source: source.into(), product: product.into(), meter: meter.into(), quantity, cost_usd: cost }
2515 }
2516
2517 fn usage(day: &str, workspace: &str, key: &str, value: i64, cash: i64, cost: i64) -> UsageRow {
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb972518 UsageRow { day: day.into(), workspace: workspace.into(), key: key.into(), bucket: None, value, cash, cost, given: Given::default() }
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily2519 }
2520
2521 #[test]
2522 fn a_day_puts_the_bill_g1ts_counts_and_charges_side_by_side() {
2523 let lines = vec![
2524 line("2026-10-15", SOURCE_BILLABLE, "containers", "container_memory", 1000.0, 2.00),
2525 line("2026-10-15", SOURCE_BILLABLE, "artifacts", "artifacts_operations", 30_000.0, 3.00),
2526 // Artifacts' own events: not used while the bill has a count.
2527 line("2026-10-15", SOURCE_ARTIFACTS, "artifacts", "events_pull", 29_000.0, 0.0),
2528 line("2026-10-15", SOURCE_BILLABLE, "workers", "workers_cpu_ms", 1.0, 0.50),
2529 line("2026-10-15", SOURCE_BILLABLE, "browser_rendering", "browser_hours", 2.0, 0.25),
2530 ];
2531 let own = vec![
2532 OwnRow { day: "2026-10-15".into(), meter: "git_operations".into(), workspace: "acme".into(), quantity: 7_500.0 },
2533 OwnRow { day: "2026-10-15".into(), meter: "git_operations".into(), workspace: "beta".into(), quantity: 2_500.0 },
2534 ];
2535 let usage = vec![
2536 usage("2026-10-15", "acme", "sandbox", 2_400_000, 1_000_000, 2_000_000),
2537 usage("2026-10-15", "beta", "sandbox", 1_200_000, 1_200_000, 1_000_000),
2538 usage("2026-10-15", "acme", "git", 600_000, 600_000, 500_000),
2539 usage("2026-10-15", "acme", "implement", 120_000, 120_000, 100_000),
2540 usage("2026-10-15", "beta", "plan", 20_000_000, 20_000_000, 0),
2541 ];
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running2542 let (days, workspaces) = fold(&rules(), &revenue_map(), &lines, &own, &usage, &BTreeSet::new());
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily2543 let get = |bucket: &str| days.iter().find(|d| d.bucket == bucket).unwrap();
2544 let sandboxes = get("sandboxes");
2545 assert_eq!((sandboxes.cf_cost_micros, sandboxes.own_cost_micros, sandboxes.value_micros, sandboxes.cash_micros), (2_000_000, 3_000_000, 3_600_000, 2_200_000));
2546 let git = get("git");
2547 assert_eq!(git.cf_cost_micros, 3_000_000);
2548 assert_eq!((git.cf_quantity, git.own_quantity), (30_000.0, 10_000.0));
2549 assert_eq!(get("platform").value_micros, 20_000_000);
2550 // Not mapped: a leak until someone maps it.
2551 assert_eq!(get(UNMAPPED).cf_cost_micros, 250_000);
2552 // Models: no Cloudflare line, their cost is g1t's own.
2553 assert_eq!(get("models").cost(), 100_000);
2554 // Git's cost shared by g1t's own counts (Cloudflare gave none per
2555 // workspace here): three quarters to acme.
2556 let share = |ws: &str, bucket: &str| workspaces.iter().find(|w| w.workspace == ws && w.bucket == bucket).map(|w| (w.cost, w.revenue));
2557 assert_eq!(share("acme", "git"), Some((2_250_000, 600_000)));
2558 assert_eq!(share("beta", "git"), Some((750_000, 0)));
2559 // Every bucket's cost is shared out exactly.
2560 for d in &days {
2561 let shared: i64 = workspaces.iter().filter(|w| w.bucket == d.bucket).map(|w| w.cost).sum();
2562 assert_eq!(shared, d.cost(), "{}", d.bucket);
2563 }
2564 }
2565
2566 #[test]
Merge costs and margin review: gateway query, own spend, discount meters, superseded rises2567 fn a_planning_run_is_model_cost_not_running_g1t() {
2568 // flagon-io's planning run on 2026-10-07 cost $0.0748 of model
2569 // calls; read under the ledger's task, `plan`, it went to running
2570 // g1t, where Cloudflare's bill is the cost, and the model cost was
2571 // lost from the statement and the drift.
2572 let usage = vec![
2573 usage("2026-10-07", "flagon-io", PLANNING_KEY, 89_741, 0, 74_784),
2574 usage("2026-10-07", "acme", "plan", 666_666, 666_666, 0),
2575 ];
2576 let (days, _) = fold(&rules(), &revenue_map(), &[], &[], &usage, &BTreeSet::new());
2577 let get = |bucket: &str| days.iter().find(|d| d.bucket == bucket).unwrap();
2578 assert_eq!((get("models").cost(), get("models").value_micros), (74_784, 89_741));
2579 assert_eq!((get("platform").own_cost_micros, get("platform").cash_micros), (0, 666_666));
2580 assert!(!revenue_map().contains_key(PLANNING_KEY));
2581 }
2582
2583 #[test]
2584 fn a_comped_workspaces_month_end_meters_are_given_never_money_in() {
2585 let snap = |day: &str, cost: i64, charge: i64| (day.to_string(), "flagon-io".to_string(), "cache".to_string(), cost, charge);
2586 let rows: Vec<UsageRow> = pending_deltas(&[snap("2026-10-07", 1, 2), snap("2026-10-08", 473, 568)]).into_iter().map(comped_meter).collect();
2587 assert_eq!(rows.iter().map(|r| (r.cash, r.value, r.given.comped)).collect::<Vec<_>>(), vec![(0, 2, 2), (0, 566, 566)]);
2588 let internal: BTreeSet<String> = ["flagon-io".to_string()].into();
2589 let (days, workspaces) = fold(&rules(), &revenue_map(), &[], &[], &rows, &internal);
2590 assert!(days.iter().all(|d| d.cash_micros == 0));
2591 assert!(workspaces.iter().all(|w| w.revenue == 0));
2592 }
2593
2594 #[test]
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily2595 fn artifacts_events_count_when_the_bill_does_not() {
2596 let lines = vec![
2597 line("2026-10-05", SOURCE_ARTIFACTS, "artifacts", "events_pull", 120.0, 0.0),
2598 line("2026-10-05", SOURCE_ARTIFACTS, "artifacts", "events_push", 30.0, 0.0),
2599 line("2026-10-05", SOURCE_ARTIFACTS, "artifacts", "events_ratelimited", 9.0, 0.0),
2600 ];
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running2601 let (days, _) = fold(&rules(), &revenue_map(), &lines, &[], &[], &BTreeSet::new());
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily2602 assert_eq!(days[0].cf_quantity, 150.0);
2603 assert_eq!(days[0].cf_cost_micros, 0);
2604 }
2605
2606 #[test]
2607 fn month_end_meters_are_told_by_the_day_from_snapshots() {
2608 let snap = |day: &str, cost: i64, charge: i64| (day.to_string(), "acme".to_string(), "git".to_string(), cost, charge);
2609 let rows = pending_deltas(&[snap("2026-10-30", 100, 120), snap("2026-10-31", 250, 300), snap("2026-11-01", 40, 48), snap("2026-11-02", 40, 48)]);
2610 assert_eq!(
2611 rows.iter().map(|r| (r.day.as_str(), r.cost, r.value)).collect::<Vec<_>>(),
2612 vec![("2026-10-30", 100, 120), ("2026-10-31", 150, 180), ("2026-11-01", 40, 48)]
2613 );
2614 }
2615
2616 #[test]
2617 fn a_plan_payment_is_spread_over_the_month_it_pays_for() {
2618 let days = spread("2026-10-01T00:00:00.000Z", 20_000_000, 30);
2619 assert_eq!(days.len(), 30);
2620 assert_eq!(days[0], ("2026-10-01".to_string(), 666_667));
2621 assert_eq!(days[29], ("2026-10-30".to_string(), 666_666));
2622 assert_eq!(days.iter().map(|d| d.1).sum::<i64>(), 20_000_000);
2623 assert!(spread("2026-10-01", 0, 30).is_empty());
2624 assert_eq!(dollars(17_024_000), "$17.02");
2625 assert_eq!(dollars(-27_668_620), "-$27.67");
2626 assert_eq!(dollars(63_000), "$0.063");
2627 }
2628
2629 #[test]
2630 fn margins_and_deltas() {
2631 assert_eq!(margin_percent(1_200_000, 1_000_000).map(|m| (m * 100.0).round() / 100.0), Some(16.67));
2632 assert_eq!(margin_percent(0, 5), None);
2633 assert_eq!(delta_percent(110.0, 100.0), Some(10.0));
2634 assert_eq!(delta_percent(1.0, 0.0), None);
2635 }
2636
2637 fn day(bucket: &str, cf: i64, own: i64, value: i64, cfq: f64, ownq: f64) -> ProductDay {
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running2638 ProductDay { day: "2026-10-15".into(), bucket: bucket.into(), cf_cost_micros: cf, own_cost_micros: own, value_micros: value, cash_micros: value, cf_quantity: cfq, own_quantity: ownq, given: Given::default() }
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily2639 }
2640
2641 #[test]
2642 fn counts_more_than_the_threshold_apart_are_drift() {
2643 // Cloudflare counted 30,000 operations where g1t counted 10,000:
2644 // binding reads, perhaps. -66.7%.
Merge remote-tracking branch 'origin/main' into workspace-chat2645 let drift = drifts("git", &[day("git", 3_000_000, 1_500_000, 1_800_000, 30_000.0, 10_000.0)], 10.0, true, 100_000, Some(3_000_000.0));
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily2646 assert_eq!(drift.iter().map(|d| d.kind).collect::<Vec<_>>(), vec![DriftKind::Count, DriftKind::Cost]);
2647 assert!((drift[0].delta_percent.unwrap() + 66.666).abs() < 0.01);
2648 // 9% apart: within 10%.
Merge remote-tracking branch 'origin/main' into workspace-chat2649 assert!(drifts("git", &[day("git", 1_000_000, 1_000_000, 1_200_000, 10_000.0, 10_900.0)], 10.0, true, 100_000, Some(1_000_000.0)).is_empty());
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily2650 // Uncounted products have no count drift.
Merge remote-tracking branch 'origin/main' into workspace-chat2651 assert!(drifts("sandboxes", &[day("sandboxes", 1_000_000, 1_050_000, 1_200_000, 5.0, 0.0)], 10.0, false, 100_000, Some(1_000_000.0)).is_empty());
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily2652 }
2653
2654 #[test]
2655 fn cost_with_no_revenue_is_a_leak_but_not_for_running_g1t() {
Merge remote-tracking branch 'origin/main' into workspace-chat2656 let leak = drifts("actions_cache", &[day("actions_cache", 400_000, 0, 0, 0.0, 0.0)], 10.0, false, 100_000, None);
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily2657 assert_eq!(leak.len(), 1);
2658 assert_eq!(leak[0].kind, DriftKind::Leak);
Merge remote-tracking branch 'origin/main' into workspace-chat2659 assert!(drifts("platform", &[day("platform", 5_000_000, 0, 0, 0.0, 0.0)], 10.0, false, 100_000, None).is_empty());
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily2660 // Pennies say nothing.
Merge remote-tracking branch 'origin/main' into workspace-chat2661 assert!(drifts("actions_cache", &[day("actions_cache", 50_000, 0, 0, 0.0, 0.0)], 10.0, false, 100_000, None).is_empty());
2662 assert!(drifts(UNMAPPED, &[day(UNMAPPED, 250_000, 0, 0, 0.0, 0.0)], 10.0, false, 100_000, None)[0].kind == DriftKind::Leak);
2663 }
2664
2665 /// A week of sandboxes mostly inside the cycle's included amounts:
2666 /// Cloudflare billed $0.0922 (the memory past 25 GiB-hours), and the
2667 /// same usage is $1.70 at list prices.
2668 fn sandbox_week() -> (Vec<Rule>, Vec<LineRow>) {
2669 let mut rules = rules();
2670 rules.push(rule("durable_objects", "durable_objects_compute_duration", "sandboxes", None));
2671 let lines = vec![
2672 line("2026-10-07", SOURCE_BILLABLE, "containers", "container_memory_per_gib_second", 126_870.0, 0.092_175),
2673 line("2026-10-07", SOURCE_BILLABLE, "containers", "container_vcpu", 12_000.0, 0.0),
2674 line("2026-10-07", SOURCE_BILLABLE, "containers", "container_disk_per_gb_second", 253_740.0, 0.0),
2675 line("2026-10-07", SOURCE_BILLABLE, "durable_objects", "durable_objects_compute_duration", 90_000.0, 0.0),
2676 // Not billable usage: no part of the list cost.
2677 line("2026-10-07", SOURCE_ARTIFACTS, "artifacts", "events_push", 40.0, 0.0),
2678 ];
2679 (rules, lines)
2680 }
2681
2682 #[test]
2683 fn usage_inside_the_included_amounts_is_not_a_stale_price() {
2684 let (rules, lines) = sandbox_week();
2685 let list = list_costs(&rules, &lines);
2686 let sandboxes = list["sandboxes"].unwrap();
2687 assert!((sandboxes - 1_699_936.8).abs() < 1.0, "{sandboxes}");
2688 // The price book's cost of the same usage is $1.69: within 1% of
2689 // the list, though Cloudflare billed $0.0922 after the included
2690 // amounts. Before, that read as +1733.6%.
2691 let week = day("sandboxes", 92_175, 1_690_000, 2_028_000, 0.0, 0.0);
2692 assert!(drifts("sandboxes", std::slice::from_ref(&week), 10.0, false, 100_000, Some(sandboxes)).is_empty());
2693 let net = drifts("sandboxes", &[week], 10.0, false, 100_000, Some(92_175.0));
2694 assert_eq!(net[0].kind, DriftKind::Cost, "billed against the price book was the false alarm");
2695 }
2696
2697 #[test]
2698 fn a_stale_price_is_still_drift() {
2699 let (rules, lines) = sandbox_week();
2700 let sandboxes = list_costs(&rules, &lines)["sandboxes"].unwrap();
2701 // The price book still costs the same usage at $1.20: a list price
2702 // rose and the book did not follow.
2703 let found = drifts("sandboxes", &[day("sandboxes", 92_175, 1_200_000, 1_440_000, 0.0, 0.0)], 10.0, false, 100_000, Some(sandboxes));
2704 assert_eq!(found.len(), 1);
2705 assert_eq!((found[0].kind, found[0].ours, found[0].cloudflare.round()), (DriftKind::Cost, 1_200_000.0, 1_699_937.0));
2706 assert!((found[0].delta_percent.unwrap() + 29.4).abs() < 0.1);
2707 // Nothing billed at all (the whole week inside the included
2708 // amounts) is checked all the same.
2709 assert_eq!(drifts("sandboxes", &[day("sandboxes", 0, 1_200_000, 1_440_000, 0.0, 0.0)], 10.0, false, 100_000, Some(sandboxes)).len(), 1);
2710 }
2711
2712 #[test]
2713 fn a_bucket_with_a_meter_with_no_list_price_is_not_checked() {
2714 let (rules, mut lines) = sandbox_week();
2715 lines.push(line("2026-10-07", SOURCE_BILLABLE, "artifacts", "storage", 3.0, 0.4));
2716 lines.push(line("2026-10-07", SOURCE_BILLABLE, "artifacts", "operations", 0.0, 0.0));
2717 let list = list_costs(&rules, &lines);
2718 assert_eq!(list["git"], None);
2719 assert!(list["sandboxes"].is_some());
2720 // No list cost: no cost drift, but a leak is still what was billed.
2721 assert!(drifts("git", &[day("git", 3_000_000, 1_000_000, 1_200_000, 0.0, 0.0)], 10.0, false, 100_000, None).is_empty());
2722 assert_eq!(drifts("git", &[day("git", 3_000_000, 0, 0, 0.0, 0.0)], 10.0, false, 100_000, None)[0].kind, DriftKind::Leak);
2723 }
2724
2725 #[test]
2726 fn a_unit_rate_is_the_list_price_where_there_is_one() {
2727 // Billed $0.20 for 11.16M CPU ms (9.16M past the included 30M, in
2728 // whole millions): $0.02 a million at list, whatever was billed.
2729 let (q, c) = rate_line("workers", "workers_cpu_ms", 11_160_000.0, 0.20);
2730 assert!((billed_rate(&[(q, c)]).unwrap() * 1e6 - 0.02).abs() < 1e-12);
2731 // No list price: what Cloudflare billed.
2732 assert_eq!(rate_line("artifacts", "operations", 1_000.0, 0.5), (1_000.0, 0.5));
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily2733 }
2734
2735 #[test]
2736 fn a_margin_alert_needs_n_days_in_a_row_under_the_floor() {
2737 let s = |d: &str, revenue: i64, cost: i64| (d.to_string(), revenue, cost);
2738 // 5%, 0%, -20%: three days under 10%.
2739 let series = vec![s("10-13", 1_200_000, 1_000_000), s("10-14", 1_050_000, 1_000_000), s("10-15", 1_000_000, 1_000_000), s("10-16", 1_000_000, 1_200_000)];
2740 let (from, worst) = breach(&series, 10.0, 3, 100_000).unwrap();
2741 assert_eq!(from, "10-14");
2742 assert!((worst + 20.0).abs() < 1e-9);
2743 // A good day in the window clears it.
2744 let mended = vec![s("10-14", 1_050_000, 1_000_000), s("10-15", 1_300_000, 1_000_000), s("10-16", 1_000_000, 1_200_000)];
2745 assert!(breach(&mended, 10.0, 3, 100_000).is_none());
2746 // Cost with no revenue at all is the worst margin there is.
2747 assert_eq!(breach(&[s("10-16", 0, 500_000)], 10.0, 1, 100_000).unwrap().1, -100.0);
2748 // Too little cost to judge.
2749 assert!(breach(&[s("10-16", 0, 5_000)], 10.0, 1, 100_000).is_none());
2750 assert!(breach(&series, 10.0, 9, 100_000).is_none());
2751 }
2752
2753 #[test]
2754 fn shared_costs_add_up_to_the_bill() {
2755 let w = |k: &str, v: f64| (k.to_string(), v);
2756 assert_eq!(attribute(100, &[w("a", 1.0), w("b", 1.0), w("c", 1.0)]), vec![("a".into(), 34), ("b".into(), 33), ("c".into(), 33)]);
2757 assert_eq!(attribute(10, &[w("a", 3.0), w("b", 1.0), w("a", 0.0)]), vec![("a".into(), 8), ("b".into(), 2)]);
2758 assert!(attribute(10, &[w("a", 0.0)]).is_empty());
2759 assert!(attribute(0, &[w("a", 1.0)]).is_empty());
2760 }
2761
2762 #[test]
Costs: counts are compared with Cloudflare's from the day g1t started counting, so days before a meter existed are not drift2763 fn counts_are_compared_from_the_day_g1t_started_counting() {
2764 let on = |day: &str, cf: f64, own: f64| ProductDay { day: day.into(), bucket: "git".into(), cf_quantity: cf, own_quantity: own, ..ProductDay::default() };
2765 // Five days of Cloudflare's count before g1t's meter, then two that match.
2766 let days = vec![on("2026-10-01", 500.0, 0.0), on("2026-10-05", 300.0, 0.0), on("2026-10-06", 210.0, 231.0), on("2026-10-07", 450.0, 458.0)];
Merge remote-tracking branch 'origin/main' into workspace-chat2767 assert!(drifts("git", &days, 10.0, true, 0, None).iter().all(|d| d.kind != DriftKind::Count));
Costs: counts are compared with Cloudflare's from the day g1t started counting, so days before a meter existed are not drift2768 // A real gap on the days both counted still shows.
2769 let days = vec![on("2026-10-01", 500.0, 0.0), on("2026-10-06", 400.0, 231.0), on("2026-10-07", 600.0, 300.0)];
Merge remote-tracking branch 'origin/main' into workspace-chat2770 let found = drifts("git", &days, 10.0, true, 0, None);
Costs: counts are compared with Cloudflare's from the day g1t started counting, so days before a meter existed are not drift2771 let count = found.iter().find(|d| d.kind == DriftKind::Count).unwrap();
2772 assert_eq!((count.ours, count.cloudflare), (531.0, 1000.0));
2773 // A meter that never counted is compared over every day.
2774 let days = vec![on("2026-10-06", 400.0, 0.0)];
Merge remote-tracking branch 'origin/main' into workspace-chat2775 assert!(drifts("git", &days, 10.0, true, 0, None).iter().any(|d| d.kind == DriftKind::Count));
Costs: counts are compared with Cloudflare's from the day g1t started counting, so days before a meter existed are not drift2776 }
2777
2778 #[test]
Costs: margin is measured on what was sold; comped workspaces, free periods, the trial and the pools are given away, a budget shown beside it2779 fn what_g1t_gives_away_is_kept_apart_from_what_it_sells() {
2780 let map = BTreeMap::new();
2781 // A comped workspace (all of it given), one in its trial (half paid
2782 // by the trial) and one paying in cash, all on models.
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running2783 let comped = usage("2026-10-15", "flagon", "agent", 1_200_000, 0, 1_000_000);
Costs: margin is measured on what was sold; comped workspaces, free periods, the trial and the pools are given away, a budget shown beside it2784 let mut trial = usage("2026-10-15", "acme", "agent", 1_200_000, 600_000, 1_000_000);
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running2785 trial.given = Given { trial: 600_000, ..Given::default() };
Costs: margin is measured on what was sold; comped workspaces, free periods, the trial and the pools are given away, a budget shown beside it2786 let paying = usage("2026-10-15", "beta", "agent", 1_200_000, 1_200_000, 1_000_000);
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running2787 // Nothing priced that day: free use.
2788 let free = usage("2026-10-15", "gamma", "agent", 0, 0, 1_000_000);
2789 let internal = BTreeSet::from(["flagon".to_string()]);
2790 let (days, workspaces) = fold(&[], &map, &[], &[], &[comped, trial, paying, free], &internal);
Costs: margin is measured on what was sold; comped workspaces, free periods, the trial and the pools are given away, a budget shown beside it2791 let models = days.iter().find(|d| d.bucket == "models").unwrap();
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running2792 assert_eq!(models.cost(), 4_000_000);
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging2793 assert_eq!(models.given, Given { comped: 1_000_000, free: 1_000_000, trial: 500_000, ..Given::default() });
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running2794 let given = |w: &str| workspaces.iter().find(|x| x.workspace == w).unwrap().given.total();
2795 assert_eq!((given("flagon"), given("acme"), given("beta"), given("gamma")), (1_000_000, 500_000, 0, 1_000_000));
Costs: margin is measured on what was sold; comped workspaces, free periods, the trial and the pools are given away, a budget shown beside it2796 }
2797
2798 #[test]
Merge branch 'worktree-agent-a633ac0f7f66d419d'2799 fn a_discounted_sale_keeps_its_margin_and_counts_the_discount_as_given() {
2800 // $1 of model cost at 20%, sold to an account with 30% off: charged
2801 // $0.84, and $0.36 below cost plus the margin given (as usage_rows
2802 // reads the ledger: value at price, the discount part given).
2803 let mut sale = usage("2026-10-15", "acme", "agent", 1_200_000, 840_000, 1_000_000);
2804 sale.given = Given { discount: 360_000, ..Given::default() };
2805 let (days, _) = fold(&[], &BTreeMap::new(), &[], &[], &[sale], &BTreeSet::new());
2806 let models = days.iter().find(|d| d.bucket == "models").unwrap();
2807 assert_eq!(models.value_micros, 1_200_000);
2808 assert_eq!(models.given, Given { discount: 300_000, ..Given::default() });
2809 // What was sold (cost less given) still makes the margin.
2810 let sold = models.cost() - models.given.total();
2811 assert_eq!(margin_percent(models.cash_micros, sold).map(|m| m.round()), Some(17.0));
2812 }
2813
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging2814 fn draw(kind: CreditKind, reference: &str, task: Option<&str>, at: &str, micros: i64) -> (String, crate::grants::Draw) {
2815 let draw = crate::grants::Draw { grant: "crd_a".into(), kind, reference: reference.into(), task: task.map(Into::into), at: at.into(), micros };
2816 ("acme".to_owned(), draw)
2817 }
2818
2819 #[test]
2820 fn usage_paid_for_with_credit_is_given_not_money_in() {
2821 // $1.20 of usage on $1 of cost, all of it paid with promotional credit.
2822 let mut rows = vec![usage("2026-10-15", "acme", "implement", 1_200_000, 1_200_000, 1_000_000)];
2823 apply_credits(&mut rows, &[draw(CreditKind::Promotional, "run_1", Some("implement"), "2026-10-15T10:00:00Z", 1_200_000)], &[]);
2824 assert_eq!(rows[0].cash, 0);
2825 assert_eq!(rows[0].given, Given { credit_promotional: 1_200_000, ..Given::default() });
2826 let (days, workspaces) = fold(&[], &BTreeMap::new(), &[], &[], &rows, &BTreeSet::new());
2827 let models = days.iter().find(|d| d.bucket == "models").unwrap();
2828 // Valued at its price, none of it money in, all of its cost given:
2829 // the margin on what was sold is untouched by it.
2830 assert_eq!((models.value_micros, models.cash_micros), (1_200_000, 0));
2831 assert_eq!(models.given, Given { credit_promotional: 1_000_000, ..Given::default() });
2832 assert_eq!(models.cost() - models.given.total(), 0);
2833 assert_eq!(workspaces[0].given.total(), 1_000_000);
2834 // Half paid with goodwill credit: half the cost given, half sold.
2835 let mut rows = vec![usage("2026-10-15", "acme", "implement", 1_200_000, 1_200_000, 1_000_000)];
2836 apply_credits(&mut rows, &[draw(CreditKind::Goodwill, "run_1", Some("implement"), "2026-10-15T10:00:00Z", 600_000)], &[]);
2837 let (days, _) = fold(&[], &BTreeMap::new(), &[], &[], &rows, &BTreeSet::new());
2838 let models = days.iter().find(|d| d.bucket == "models").unwrap();
2839 assert_eq!(models.cash_micros, 600_000);
2840 assert_eq!(models.given, Given { credit_goodwill: 500_000, ..Given::default() });
2841 let sold = models.cost() - models.given.total();
2842 assert_eq!(margin_percent(models.cash_micros, sold).map(|m| m.round()), Some(17.0));
2843 }
2844
2845 #[test]
2846 fn what_a_refund_pays_for_is_paid_for_and_the_refund_comes_off_its_day() {
2847 // A refund's credit pays for usage: still money in, nothing given.
2848 let mut rows = vec![usage("2026-10-15", "acme", "implement", 1_200_000, 1_200_000, 1_000_000)];
2849 apply_credits(&mut rows, &[draw(CreditKind::Refund, "run_9", Some("implement"), "2026-10-15T10:00:00Z", 1_200_000)], &[]);
2850 assert_eq!((rows[0].cash, rows[0].given), (1_200_000, Given::default()));
2851 // The $3 refunded for Oct 2 comes off that day's money in, shared
2852 // over what was paid that day.
2853 let mut rows = vec![
2854 usage("2026-10-02", "acme", "implement", 4_000_000, 4_000_000, 3_000_000),
2855 usage("2026-10-02", "acme", "sandbox", 2_000_000, 2_000_000, 1_500_000),
2856 usage("2026-10-02", "beta", "implement", 9_000_000, 9_000_000, 7_000_000),
2857 ];
2858 let refund = crate::grants::Refunded { workspace: "acme".into(), day: "2026-10-02".into(), micros: 3_000_000 };
2859 apply_credits(&mut rows, &[], std::slice::from_ref(&refund));
2860 assert_eq!((rows[0].cash, rows[1].cash, rows[2].cash), (2_000_000, 1_000_000, 9_000_000));
2861 assert!(rows.iter().all(|r| r.given == Given::default()));
2862 // Nothing paid that day: a line of its own, money in less than nothing.
2863 let mut rows = vec![];
2864 apply_credits(&mut rows, &[], &[refund]);
2865 assert_eq!((rows[0].key.as_str(), rows[0].cash, rows[0].value), ("other", -3_000_000, 0));
2866 }
2867
2868 #[test]
2869 fn credit_spent_on_month_end_meters_is_a_line_of_its_own() {
2870 // Storage is reconciled from snapshots, not its ledger line: what
2871 // credit paid of it is its own row on the day it was charged.
2872 let mut rows = vec![usage("2026-10-01", "acme", "implement", 1_000, 1_000, 800)];
2873 apply_credits(&mut rows, &[draw(CreditKind::Goodwill, "storage/2026-09", Some("storage"), "2026-10-01T00:05:00Z", 2_000_000)], &[]);
2874 assert_eq!(rows.len(), 2);
2875 assert_eq!((rows[1].key.as_str(), rows[1].cash, rows[1].value), ("storage", -2_000_000, 0));
2876 assert_eq!(rows[1].given.credit_goodwill, 2_000_000);
2877 assert_eq!(rows[0].cash, 1_000);
2878 }
2879
Merge branch 'worktree-agent-a633ac0f7f66d419d'2880 #[test]
2881 fn the_gateways_total_against_the_ledgers_model_cost_is_drift() {
2882 // The gateway priced $5 of g1t's own traffic; the ledger has $3.
Merge remote-tracking branch 'origin/main' into workspace-chat2883 let short = drifts("models", &[day("models", 5_000_000, 3_000_000, 3_600_000, 0.0, 0.0)], 10.0, false, 100_000, None);
Merge branch 'worktree-agent-a633ac0f7f66d419d'2884 assert_eq!(short.iter().map(|d| d.kind).collect::<Vec<_>>(), vec![DriftKind::Cost]);
2885 assert!((short[0].delta_percent.unwrap() + 40.0).abs() < 1e-9);
2886 // Gateway traffic with nothing on the ledger at all: cost drift and a leak.
Merge remote-tracking branch 'origin/main' into workspace-chat2887 let none = drifts("models", &[day("models", 2_000_000, 0, 0, 0.0, 0.0)], 10.0, false, 100_000, None);
Merge branch 'worktree-agent-a633ac0f7f66d419d'2888 assert_eq!(none.iter().map(|d| d.kind).collect::<Vec<_>>(), vec![DriftKind::Cost, DriftKind::Leak]);
Billing: AI Gateway's analytics are read with the token that can see them, and a gateway that priced nothing is said2889 // Within the threshold: nothing.
Merge remote-tracking branch 'origin/main' into workspace-chat2890 assert!(drifts("models", &[day("models", 1_050_000, 1_000_000, 1_200_000, 0.0, 0.0)], 10.0, false, 100_000, None).is_empty());
Billing: AI Gateway's analytics are read with the token that can see them, and a gateway that priced nothing is said2891 // The gateway priced nothing against a ledger that has model cost:
2892 // not agreement (a token that cannot see AI Gateway reads as no
2893 // rows), so it is said. Under the minimum, or no model cost: nothing.
Merge remote-tracking branch 'origin/main' into workspace-chat2894 let silent = drifts("models", &[day("models", 0, 1_000_000, 1_200_000, 0.0, 0.0)], 10.0, false, 100_000, None);
Billing: AI Gateway's analytics are read with the token that can see them, and a gateway that priced nothing is said2895 assert_eq!(silent, vec![Drift { bucket: "models".into(), kind: DriftKind::Cost, ours: 1_000_000.0, cloudflare: 0.0, delta_percent: None }]);
Merge costs and margin review: gateway query, own spend, discount meters, superseded rises2896 // Why it is empty, as far as the run could tell.
2897 let why = |caveats: &costs::GatewayCaveats, read: costs::GatewayRead| models_detail(&silent[0], caveats, &[], &read);
2898 let none = costs::GatewayCaveats::default();
2899 let said = why(&none, costs::GatewayRead::Empty { visible: Some(false) });
2900 assert!(said.contains("$1.00") && said.contains("priced nothing") && said.contains("cannot see the gateway") && said.contains("AI Gateway Read"), "{said}");
2901 let said = why(&none, costs::GatewayRead::Empty { visible: Some(true) });
2902 assert!(said.contains("logged no requests") && said.contains("went around it"), "{said}");
2903 let said = why(&none, costs::GatewayRead::Failed("Cloudflare answered 500".into()));
2904 assert!(said.contains("could not be read: Cloudflare answered 500"), "{said}");
2905 assert!(why(&none, costs::GatewayRead::NotRead).contains("not read on this run"));
2906 assert!(why(&none, costs::GatewayRead::Empty { visible: None }).contains("could not be told"));
2907 // Requests with no price: neither the token nor a bypass.
2908 let unpriced = costs::GatewayCaveats { requests: 42.0, unpriced: vec!["anthropic_claude_new_1".into()], ..Default::default() };
2909 let said = why(&unpriced, costs::GatewayRead::Rows);
2910 assert!(said.contains("logged 42 requests") && said.contains("no price for the models used (anthropic_claude_new_1)"), "{said}");
Merge remote-tracking branch 'origin/main' into workspace-chat2911 assert!(drifts("models", &[day("models", 0, 50_000, 60_000, 0.0, 0.0)], 10.0, false, 100_000, None).is_empty());
2912 assert!(drifts("models", &[day("models", 0, 0, 0, 0.0, 0.0)], 10.0, false, 100_000, None).is_empty());
Merge branch 'worktree-agent-a633ac0f7f66d419d'2913 // The detail says which way and why it may be off.
2914 let caveats = costs::GatewayCaveats { cache_read_tokens: 3_000_000.0, unpriced: vec!["anthropic_claude_new_1".into()], ..Default::default() };
Merge costs and margin review: gateway query, own spend, discount meters, superseded rises2915 let detail = models_detail(&short[0], &caveats, &[], &costs::GatewayRead::default());
Merge branch 'worktree-agent-a633ac0f7f66d419d'2916 assert!(detail.contains("$5.00") && detail.contains("$3.00") && detail.contains("were not charged"), "{detail}");
2917 assert!(detail.contains("3,000,000 prompt-cache read") && detail.contains("no price for anthropic_claude_new_1"), "{detail}");
2918 }
2919
2920 #[test]
2921 fn model_usage_the_gateway_cannot_price_is_drift_even_when_the_totals_agree() {
2922 assert!(unpriced_drift(&costs::GatewayCaveats::default()).is_none());
2923 // Cache tokens alone are a note on the cost drift, not drift.
2924 assert!(unpriced_drift(&costs::GatewayCaveats { cache_write_tokens: 10.0, ..Default::default() }).is_none());
2925 let (drift, detail) = unpriced_drift(&costs::GatewayCaveats { unpriced: vec!["anthropic_claude_new_1".into()], short_runs: 2, ..Default::default() }).unwrap();
2926 assert_eq!((drift.bucket.as_str(), drift.kind.as_str()), ("models", "unpriced"));
2927 assert!(detail.contains("no price for anthropic_claude_new_1") && detail.contains("2 runs were settled"), "{detail}");
2928 }
2929
2930 #[test]
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily2931 fn a_workspace_that_costs_more_than_it_pays_is_flagged() {
2932 let rows = vec![("acme".to_string(), 5_000_000, 1_000_000), ("beta".to_string(), 900_000, 0), ("gamma".to_string(), 2_000_000, 3_000_000)];
2933 let found = anomalies(&rows, 1.0, 1_000_000);
2934 assert_eq!(found, vec![("acme".to_string(), 5_000_000, 1_000_000)]);
2935 // At twice its revenue as the threshold, $5 against $3 is fine.
2936 assert!(anomalies(&[("acme".to_string(), 5_000_000, 3_000_000)], 2.0, 1_000_000).is_empty());
2937 }
2938
2939 #[test]
2940 fn a_git_operation_costs_what_cloudflare_counts_for_it() {
2941 // $0.15 per 1,000 of Cloudflare's operations, on the charged days.
2942 let rate = billed_rate(&[(10_000.0, 0.0), (20_000.0, 3.0), (30_000.0, 4.5), (5_000.0, 0.75)]).unwrap();
2943 assert!((rate - 0.000_15).abs() < 1e-12);
2944 // Cloudflare counted 3 for every 1 g1t did: binding reads count.
2945 let per_op = derived_unit_cost(rate, 300_000.0, 100_000.0).unwrap();
2946 let per_thousand_micros = per_op * unit_size("1,000 operations") * 1e6;
2947 assert!((per_thousand_micros - 450_000.0).abs() < 1e-6, "{per_thousand_micros}");
2948 // Too few of g1t's units to say.
2949 assert!(derived_unit_cost(rate, 3_000.0, 500.0).is_none());
2950 assert!(billed_rate(&[(10_000.0, 0.0)]).is_none());
2951 assert_eq!(unit_size("million requests"), 1e6);
2952 assert_eq!(unit_size("second"), 1.0);
2953 }
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb972954
2955 /// The case that started it: syntaqx's ~$8.62 of model usage was wiped
2956 /// by a testing reset, AI Gateway still priced all $11.11, and the
2957 /// ledger had $2.49 left.
2958 fn gateway_and_ledger(kept: bool) -> (Vec<ProductDay>, Vec<WorkspaceDay>) {
2959 let rules = vec![rule("ai_gateway_requests", "*", "models", None), rule("containers", "*", "sandboxes", None)];
2960 let lines = vec![
2961 line("2026-10-05", costs::SOURCE_GATEWAY, "ai_gateway_requests", "anthropic_claude_opus_5_5", 1.0, 11.11),
2962 line("2026-10-05", SOURCE_BILLABLE, "containers", "container_memory", 10.0, 0.30),
2963 ];
2964 let mut usage = vec![usage("2026-10-05", "acme", "implement", 2_988_000, 2_988_000, 2_490_000), usage("2026-10-05", "acme", "sandbox", 120_000, 120_000, 100_000)];
2965 if kept {
2966 // What the reset kept (reset_costs), read back for the day.
2967 usage.extend(reset_usage(&[
2968 ("2026-10-05".into(), "syntaqx".into(), "models".into(), 8_620_000, 10_344_000),
2969 ("2026-10-05".into(), "syntaqx".into(), "sandboxes".into(), 100_000, 120_000),
2970 // The reset's own row is not usage.
2971 ("2026-10-07".into(), "syntaqx".into(), String::new(), 0, 0),
2972 ]));
2973 }
2974 fold(&rules, &revenue_map(), &lines, &[], &usage, &BTreeSet::new())
2975 }
2976
2977 #[test]
2978 fn what_a_reset_kept_is_on_the_ledgers_side_of_the_models_drift() {
2979 let models = |days: &[ProductDay]| days.iter().find(|d| d.bucket == "models").cloned().unwrap();
2980 // Without it: AI Gateway's $11.11 against the ledger's $2.49.
2981 let (days, _) = gateway_and_ledger(false);
Merge remote-tracking branch 'origin/main' into workspace-chat2982 let drift = drifts("models", &[models(&days)], 10.0, false, 100_000, None);
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb972983 assert_eq!(drift.iter().map(|d| d.kind).collect::<Vec<_>>(), vec![DriftKind::Cost]);
2984 assert_eq!((drift[0].ours, drift[0].cloudflare), (2_490_000.0, 11_110_000.0));
2985 // With it: the ledger's model cost and the reset's add up to the gateway's.
2986 let (days, _) = gateway_and_ledger(true);
2987 let m = models(&days);
2988 assert_eq!(m.own_cost_micros, 11_110_000);
Merge remote-tracking branch 'origin/main' into workspace-chat2989 assert!(drifts("models", &[m], 10.0, false, 100_000, None).is_empty());
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb972990 // The reset's own row makes no bucket of its own.
2991 assert!(!days.iter().any(|d| d.bucket.is_empty()));
2992 }
2993
2994 #[test]
2995 fn what_a_reset_kept_is_given_away_as_testing_resets() {
2996 let (days, workspaces) = gateway_and_ledger(true);
2997 let models = days.iter().find(|d| d.bucket == "models").unwrap();
2998 // All of syntaqx's model cost is given, none of it money in.
2999 assert_eq!(models.given, Given { reset: 8_620_000, ..Given::default() });
3000 assert_eq!(models.cash_micros, 2_988_000);
3001 // Cloudflare's sandbox cost is shared by what each workspace's usage
3002 // cost: syntaqx's half is given too.
3003 let sandboxes = days.iter().find(|d| d.bucket == "sandboxes").unwrap();
3004 assert_eq!((sandboxes.cost(), sandboxes.given.reset), (300_000, 150_000));
3005 // Who g1t paid: syntaqx is still on it, all of its cost given.
3006 let syntaqx: Vec<&WorkspaceDay> = workspaces.iter().filter(|w| w.workspace == "syntaqx").collect();
3007 assert_eq!(syntaqx.iter().map(|w| w.cost).sum::<i64>(), 8_770_000);
3008 assert!(syntaqx.iter().all(|w| w.given.reset == w.cost && w.given.total() == w.cost && w.revenue == 0));
3009 // The statement reads it back from margin_days by why.
3010 let row = MarginRow {
3011 day: models.day.clone(),
3012 bucket: models.bucket.clone(),
3013 cf_cost_micros: models.cf_cost_micros,
3014 own_cost_micros: models.own_cost_micros,
3015 value_micros: models.value_micros,
3016 cash_micros: models.cash_micros,
3017 cf_quantity: 0.0,
3018 own_quantity: 0.0,
3019 given_comped_micros: Some(0),
3020 given_free_micros: Some(0),
3021 given_trial_micros: Some(0),
3022 given_pool_micros: Some(0),
3023 given_discount_micros: Some(0),
3024 given_credit_promotional_micros: Some(0),
3025 given_credit_goodwill_micros: Some(0),
3026 given_reset_micros: Some(models.given.reset),
Merge Cloudflare's usage over its billing cycle: every page read, included amounts once a cycle, a projection, test-mode charges never money in (billing 0052)3027 given_unpaid_micros: Some(0),
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb973028 };
3029 assert_eq!(ProductDay::from(row).given, models.given);
3030 }
3031
3032 #[test]
Merge Cloudflare's usage over its billing cycle: every page read, included amounts once a cycle, a projection, test-mode charges never money in (billing 0052)3033 fn test_mode_charges_are_never_money_in() {
3034 // A $12 sandbox charge and the $20 plan, both while payments were in
3035 // Stripe's test mode: valued as before, given as unpaid, no cash.
3036 let mut rows = vec![
3037 usage("2026-10-06", "acme", "sandbox", 12_000_000, 12_000_000, 10_000_000),
3038 UsageRow { day: "2026-10-06".into(), workspace: "acme".into(), key: "plan".into(), value: 666_667, cash: 666_667, ..UsageRow::default() },
3039 ];
3040 without_real_money(&mut rows, None);
3041 assert!(rows.iter().all(|r| r.cash == 0));
3042 // A comped workspace's charge is given once, as comped.
3043 let mut comped = vec![UsageRow { given: Given { comped: 500, ..Given::default() }, ..usage("2026-10-06", "flagon-io", "sandbox", 500, 500, 400) }];
3044 without_real_money(&mut comped, None);
3045 assert_eq!((comped[0].cash, comped[0].given.total()), (0, 500));
3046 assert_eq!(rows[0].given.unpaid, 12_000_000);
3047 assert_eq!(rows[0].value, 12_000_000);
3048 assert_eq!(rows[1].given.unpaid, 666_667);
3049 let (days, workspaces) = fold(&rules(), &revenue_map(), &[], &[], &rows, &BTreeSet::new());
3050 assert_eq!(days.iter().map(|d| d.cash_micros).sum::<i64>(), 0);
3051 let sandboxes = days.iter().find(|d| d.bucket == "sandboxes").unwrap();
3052 assert_eq!(sandboxes.given.unpaid, sandboxes.cost());
3053 assert!(workspaces.iter().all(|w| w.revenue == 0));
3054 // Once live: from that day on, it is money.
3055 let mut rows = vec![
3056 usage("2026-10-06", "acme", "sandbox", 1_000_000, 1_000_000, 800_000),
3057 usage("2026-10-07", "acme", "sandbox", 1_000_000, 1_000_000, 800_000),
3058 ];
3059 without_real_money(&mut rows, Some("2026-10-07"));
3060 assert_eq!((rows[0].cash, rows[0].given.unpaid), (0, 1_000_000));
3061 assert_eq!((rows[1].cash, rows[1].given.unpaid), (1_000_000, 0));
3062 // When payments went live is kept from the first time it is seen.
3063 assert_eq!(live_since(false, Some("2026-10-07"), "2026-10-09"), None);
3064 assert_eq!(live_since(true, None, "2026-10-09").as_deref(), Some("2026-10-09"));
3065 assert_eq!(live_since(true, Some("2026-10-07"), "2026-10-09").as_deref(), Some("2026-10-07"));
3066 }
3067
3068 #[test]
3069 fn workspaces_and_running_g1t_add_up_to_the_bill() {
3070 // Running g1t on a day with usage is shared; on a day with none it
3071 // is no one's, and the report says so.
3072 let lines = vec![
3073 line("2026-10-06", SOURCE_BILLABLE, "workers", "workers_cpu_ms", 1.0, 0.10),
3074 line("2026-10-07", SOURCE_BILLABLE, "workers", "workers_cpu_ms", 1.0, 0.20),
3075 line("2026-10-07", SOURCE_BILLABLE, "containers", "container_memory_per_gib_second", 1.0, 0.09),
3076 ];
3077 let usage = vec![usage("2026-10-07", "acme", "sandbox", 100, 100, 80), usage("2026-10-07", "beta", "sandbox", 300, 300, 240)];
3078 let (days, workspaces) = fold(&rules(), &revenue_map(), &lines, &[], &usage, &BTreeSet::new());
3079 let total: i64 = days.iter().map(ProductDay::cost).sum();
3080 let shared: i64 = workspaces.iter().map(|w| w.cost).sum();
3081 assert_eq!(total, 390_000);
3082 assert_eq!(unattributed(total, shared), 100_000);
3083 assert_eq!(shared + unattributed(total, shared), total);
3084 }
3085
3086 #[test]
3087 fn the_cycle_report_projects_as_cloudflare_does() {
3088 let cycle = crate::cycle::cycle_of("2026-10-09", 28);
3089 let meter = |meter: &str, cost: i64, quantity: f64| CycleMeter { meter: meter.into(), cost_micros: cost, quantity, ..CycleMeter::default() };
3090 let report = cycle_report(&cycle, 12, vec![meter("container_memory", 92_175, 126_870.0), meter("workers_cpu_ms", 200_000, 39_160_000.0), meter("d1_rows_read", 0, 61_820_000.0)], 30_000_000);
3091 assert_eq!((report.start.as_str(), report.end.as_str(), report.days, report.days_elapsed), ("2026-09-28", "2026-10-27", 30, 12));
3092 assert_eq!(report.usage_micros, 292_175);
3093 assert_eq!(report.projected_micros, 730_438);
3094 assert_eq!(report.average_daily_micros, 24_347);
3095 assert_eq!(report.meters[0].meter, "workers_cpu_ms");
3096 assert_eq!(report.meters[2].meter, "d1_rows_read");
3097 }
3098
3099 #[test]
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb973100 fn reconciling_again_gives_the_same_answer() {
3101 assert_eq!(gateway_and_ledger(true), gateway_and_ledger(true));
3102 // A reset's kept rows are read back exactly as kept: running it
3103 // again cannot count them twice.
3104 let kept = [("2026-10-05".to_string(), "syntaqx".to_string(), "models".to_string(), 8_620_000, 10_344_000)];
3105 assert_eq!(reset_usage(&kept), reset_usage(&kept));
3106 assert_eq!(reset_usage(&kept).len(), 1);
3107 }
3108
3109 #[test]
3110 fn a_reset_from_before_resets_kept_their_cost_is_said_not_called_a_leak() {
3111 let notes = reset_notes(
3112 &[("ws_syntaqx".into(), "2026-10-07T09:41:00.000Z".into()), ("ws_acme".into(), "2026-10-08T01:00:00.000Z".into())],
3113 &[("acme".into(), "2026-10-08T01:00:00.000Z".into(), 1_500_000)],
3114 );
3115 assert_eq!(
3116 notes,
3117 vec![
3118 ResetNote { workspace: "syntaqx".into(), day: "2026-10-07".into(), recorded: false, models_micros: 0 },
3119 ResetNote { workspace: "acme".into(), day: "2026-10-08".into(), recorded: true, models_micros: 1_500_000 },
3120 ]
3121 );
3122 let drift = Drift { bucket: "models".into(), kind: DriftKind::Cost, ours: 2_490_000.0, cloudflare: 11_110_000.0, delta_percent: Some(-77.6) };
Merge costs and margin review: gateway query, own spend, discount meters, superseded rises3123 let detail = models_detail(&drift, &costs::GatewayCaveats::default(), &notes, &costs::GatewayRead::default());
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb973124 assert!(detail.contains("includes model usage wiped by a testing reset of syntaqx on 2026-10-07"), "{detail}");
3125 assert!(detail.contains("not a leak") && detail.contains("leaves the 7 days on 2026-10-14"), "{detail}");
3126 assert!(!detail.contains("a gap that stays is a leak"), "{detail}");
3127 assert!(detail.contains("$1.50 of model cost wiped by a testing reset of acme on 2026-10-08, counted as given away (testing resets)"), "{detail}");
3128 // The models leak is not raised while such a reset is in the window.
3129 let leak = Drift { bucket: "models".into(), kind: DriftKind::Leak, ours: 0.0, cloudflare: 11_110_000.0, delta_percent: None };
3130 assert!(wiped_not_leaked(&leak, &notes));
3131 assert!(!wiped_not_leaked(&leak, &notes[1..]));
3132 assert!(!wiped_not_leaked(&Drift { bucket: "actions_cache".into(), ..leak }, &notes));
3133 // No reset: the detail is as before.
Merge costs and margin review: gateway query, own spend, discount meters, superseded rises3134 assert!(models_detail(&drift, &costs::GatewayCaveats::default(), &[], &costs::GatewayRead::default()).contains("a gap that stays is a leak"));
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb973135 }
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily3136}

This file's history is long; its oldest lines are credited to the oldest commit read.