Skip to content

g1t/services/billing/src/margin.rs

2,631 lines126,954 bytesCodeBlame

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"];
47/// The days drift is judged over.
48const DRIFT_DAYS: u64 = 7;
49/// The days a workspace's cost is set against its revenue.
50const ANOMALY_DAYS: u64 = 30;
51/// The days a unit's cost is measured over.
52const MEASURE_DAYS: u64 = 30;
53/// Fewer of g1t's units than this say nothing about cost per unit.
54const MIN_UNITS: f64 = 1_000.0;
55/// An open alert is emailed again after this long.
56const REMIND_MS: u64 = 7 * DAY_MS;
57
58// ---------------------------------------------------------------------
59// The arithmetic, apart from the database so it can be tested.
60// ---------------------------------------------------------------------
61
62/// One of g1t's products on one day.
63#[derive(Clone, Debug, Default, PartialEq)]
64pub(crate) struct ProductDay {
65 pub day: String,
66 pub bucket: String,
67 /// What Cloudflare charged g1t, in millionths of a dollar.
68 pub cf_cost_micros: i64,
69 /// What g1t's meters recorded it cost (the price book's cost).
70 pub own_cost_micros: i64,
71 /// What customers were charged for it at price, before what paid.
72 pub value_micros: i64,
73 /// Of that, what workspaces paid themselves.
74 pub cash_micros: i64,
75 /// Units Cloudflare counted and units g1t counted, where a mapping
76 /// says they are the same units.
77 pub cf_quantity: f64,
78 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 it79 /// Of `cost()`, what went on usage g1t gave away (the workspaces'
80 /// `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 running81 pub given: Given,
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily82}
83
84impl ProductDay {
85 /// What it cost: Cloudflare's figure where Cloudflare bills it, else
86 /// g1t's own (models are billed by their providers, through the gateway).
87 pub fn cost(&self) -> i64 {
88 if NOT_CLOUDFLARE.contains(&self.bucket.as_str()) { self.own_cost_micros } else { self.cf_cost_micros }
89 }
90}
91
92/// A line of Cloudflare's bill, as stored.
93#[derive(Clone, Debug, Deserialize)]
94pub(crate) struct LineRow {
95 pub day: String,
96 pub source: String,
97 pub product: String,
98 pub meter: String,
99 pub quantity: f64,
100 pub cost_usd: f64,
101}
102
103/// A count of g1t's own, as stored.
104#[derive(Clone, Debug, Deserialize)]
105pub(crate) struct OwnRow {
106 pub day: String,
107 pub meter: String,
108 pub workspace: String,
109 pub quantity: f64,
110}
111
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 running112/// What g1t gave away, by why: its own comped workspaces, free use (a
113/// free period, free allowances, overruns g1t covered), the trial, and the
Merge branch 'worktree-agent-a633ac0f7f66d419d'114/// 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 charging115/// below cost plus the margin, `ledger.discount_micros`), and credits g1t
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97116/// staff gave, promotional and goodwill, when spent (`grants`), and usage a
117/// testing reset wiped (`reset_costs`): g1t paid for it and nobody will.
118/// The Team plan's included usage is paid for by the plan's price, so it is
119/// 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 running120#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
121pub(crate) struct Given {
122 pub comped: i64,
123 pub free: i64,
124 pub trial: i64,
125 pub pool: i64,
Merge branch 'worktree-agent-a633ac0f7f66d419d'126 pub discount: i64,
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging127 pub credit_promotional: i64,
128 pub credit_goodwill: i64,
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97129 pub reset: 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 running130}
131
132impl Given {
133 pub fn total(&self) -> i64 {
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97134 self.comped + self.free + self.trial + self.pool + self.discount + self.credit() + self.reset
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging135 }
136
137 /// Credits from g1t, both kinds.
138 pub fn credit(&self) -> i64 {
139 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 running140 }
141
142 fn add(&mut self, other: &Given) {
143 self.comped += other.comped;
144 self.free += other.free;
145 self.trial += other.trial;
146 self.pool += other.pool;
Merge branch 'worktree-agent-a633ac0f7f66d419d'147 self.discount += other.discount;
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging148 self.credit_promotional += other.credit_promotional;
149 self.credit_goodwill += other.credit_goodwill;
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97150 self.reset += other.reset;
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 running151 }
152
153 /// The same shares of `cost` as these are of `value`, at most all of it.
154 fn of(&self, cost: i64, value: i64) -> Given {
155 let total = self.total();
156 if value <= 0 || cost <= 0 || total <= 0 {
157 return Given::default();
158 }
159 let given = cost as i128 * total.min(value) as i128 / value as i128;
160 let part = |x: i64| (given * x.max(0) as i128 / total as i128) as i64;
Merge branch 'worktree-agent-a633ac0f7f66d419d'161 Given {
162 comped: part(self.comped),
163 free: part(self.free),
164 trial: part(self.trial),
165 pool: part(self.pool),
166 discount: part(self.discount),
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging167 credit_promotional: part(self.credit_promotional),
168 credit_goodwill: part(self.credit_goodwill),
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97169 reset: part(self.reset),
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging170 }
171 }
172}
173
174/// Credits from g1t in the reconciliation (`grants`): usage paid for with
175/// promotional or goodwill credit is given, not money in; a refund comes
176/// off money in on the day it refunds, shared over that day's paid usage.
177/// What credit paid for that is not among `rows` (month-end meters, or
178/// what was owed from before) is a row of its own on its day.
179pub(crate) fn apply_credits(rows: &mut Vec<UsageRow>, draws: &[(String, crate::grants::Draw)], refunds: &[crate::grants::Refunded]) {
180 let mut paid: BTreeMap<(String, String, String), Given> = BTreeMap::new();
181 for (workspace, draw) in draws {
182 let given = paid
183 .entry((draw.at[..10].to_owned(), workspace.clone(), crate::grants::usage_key(draw.task.as_deref(), &draw.reference)))
184 .or_default();
185 match draw.kind {
186 CreditKind::Promotional => given.credit_promotional += draw.micros,
187 CreditKind::Goodwill => given.credit_goodwill += draw.micros,
188 // Money already paid: what it pays for is paid for.
189 CreditKind::Refund | CreditKind::Purchased => {}
190 }
191 }
192 for ((day, workspace, key), given) in paid {
193 if given.credit() == 0 {
194 continue;
Merge branch 'worktree-agent-a633ac0f7f66d419d'195 }
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging196 match rows.iter_mut().find(|r| r.day == day && r.workspace == workspace && r.key == key) {
197 Some(row) => {
198 row.cash -= given.credit();
199 row.given.add(&given);
200 }
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97201 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 charging202 }
203 }
204 for refund in refunds {
205 let weights: Vec<(String, f64)> = rows
206 .iter()
207 .enumerate()
208 .filter(|(_, r)| r.day == refund.day && r.workspace == refund.workspace && r.cash > 0)
209 .map(|(i, r)| (format!("{i:08}"), r.cash as f64))
210 .collect();
211 let shares = attribute(refund.micros, &weights);
212 if shares.is_empty() {
213 rows.push(UsageRow {
214 day: refund.day.clone(),
215 workspace: refund.workspace.clone(),
216 key: "other".into(),
217 cash: -refund.micros,
218 ..UsageRow::default()
219 });
220 }
221 for (index, micros) in shares {
222 if let Ok(i) = index.parse::<usize>() {
223 rows[i].cash -= micros;
224 }
225 }
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 running226 }
227}
228
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily229/// What a workspace was charged for one key on one day.
230#[derive(Clone, Debug, Default, PartialEq)]
231pub(crate) struct UsageRow {
232 pub day: String,
233 pub workspace: String,
234 /// A ledger task (or `builds`), a month-end source, or `plan`.
235 pub key: String,
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97236 /// The bucket, where it is known already (what a testing reset kept,
237 /// `reset_costs`); else `key`'s, from `revenue_map`.
238 pub bucket: Option<String>,
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily239 pub value: i64,
240 pub cash: i64,
241 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 it242 /// 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 running243 /// workspaces and in a free period, else what the trial and the pool
244 /// paid and the overruns g1t covered.
245 pub given: Given,
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily246}
247
248/// One workspace's share of a product's cost on one day.
249#[derive(Clone, Debug, PartialEq)]
250pub(crate) struct WorkspaceDay {
251 pub day: String,
252 pub workspace: String,
253 pub bucket: String,
254 pub cost: i64,
Margin alerts measure what is sold, and say dollars when a percentage would mislead255 /// What the workspace paid in cash.
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily256 pub revenue: i64,
Margin alerts measure what is sold, and say dollars when a percentage would mislead257 /// What its usage was priced at, whoever paid for it.
258 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 running259 /// Of `cost`, the part g1t gave away: all of it for a comped workspace
260 /// or one with nothing priced that day (free use), else the cost times
261 /// the shares of its usage that day that g1t paid for.
262 pub given: Given,
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily263}
264
265fn micros(dollars: f64) -> i64 {
266 (dollars * 1_000_000.0).round() as i64
267}
268
269/// Puts the day's bill, g1t's counts and what customers were charged side
270/// by side, a row per day and bucket, and shares each bucket's cost out
271/// to workspaces.
272pub(crate) fn fold(
273 rules: &[Rule],
274 revenue_map: &BTreeMap<String, String>,
275 lines: &[LineRow],
276 own: &[OwnRow],
277 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 running278 internal: &BTreeSet<String>,
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily279) -> (Vec<ProductDay>, Vec<WorkspaceDay>) {
280 let mut days: BTreeMap<(String, String), ProductDay> = BTreeMap::new();
281 let entry = |day: &str, bucket: &str| -> ProductDay {
282 ProductDay { day: day.to_owned(), bucket: bucket.to_owned(), ..ProductDay::default() }
283 };
284 // Which of g1t's own meters count each bucket's units.
285 let mut own_meters: BTreeMap<&str, BTreeSet<&str>> = BTreeMap::new();
286 for rule in rules {
287 if let Some(meter) = &rule.own_meter {
288 own_meters.entry(rule.bucket.as_str()).or_default().insert(meter.as_str());
289 }
290 }
291 let mut events: BTreeMap<(String, String), f64> = BTreeMap::new();
292 for line in lines {
293 let rule = costs::classify(rules, &line.product, &line.meter);
294 let bucket = rule.map_or(UNMAPPED, |r| r.bucket.as_str());
295 let key = (line.day.clone(), bucket.to_owned());
296 if line.source == SOURCE_ARTIFACTS {
297 // What Artifacts counted: operations only, and only where the
298 // bill does not count them itself.
299 if ARTIFACTS_OPERATIONS.contains(&line.meter.as_str()) {
300 *events.entry(key).or_default() += line.quantity;
301 }
302 continue;
303 }
304 let row = days.entry(key.clone()).or_insert_with(|| entry(&key.0, &key.1));
305 row.cf_cost_micros += micros(line.cost_usd);
306 if line.source == SOURCE_BILLABLE && rule.is_some_and(|r| r.own_meter.is_some()) {
307 row.cf_quantity += line.quantity;
308 }
309 }
310 for (key, quantity) in events {
311 let row = days.entry(key.clone()).or_insert_with(|| entry(&key.0, &key.1));
312 if row.cf_quantity == 0.0 {
313 row.cf_quantity = quantity;
314 }
315 }
316 // g1t's own counts of the same units, by bucket and by workspace.
317 let mut own_by: BTreeMap<(String, String), Vec<(String, f64)>> = BTreeMap::new();
318 // Cloudflare's own count by workspace, where it gives one
319 // (`cloudflare_<bucket>`): the best way to share its cost.
320 let mut cf_by: BTreeMap<(String, String), Vec<(String, f64)>> = BTreeMap::new();
321 for count in own {
322 if let Some(bucket) = count.meter.strip_prefix("cloudflare_") {
323 cf_by.entry((count.day.clone(), bucket.to_owned())).or_default().push((count.workspace.clone(), count.quantity));
324 continue;
325 }
326 for (bucket, meters) in &own_meters {
327 if meters.contains(count.meter.as_str()) {
328 let key = (count.day.clone(), (*bucket).to_owned());
329 days.entry(key.clone()).or_insert_with(|| entry(&key.0, &key.1)).own_quantity += count.quantity;
330 own_by.entry(key).or_default().push((count.workspace.clone(), count.quantity));
331 }
332 }
333 }
334 // What customers were charged.
335 let bucket_of = |key: &str| revenue_map.get(key).cloned().unwrap_or_else(|| "models".to_owned());
336 let mut value_by: BTreeMap<(String, String), Vec<(String, f64)>> = BTreeMap::new();
337 let mut cost_by: BTreeMap<(String, String), Vec<(String, f64)>> = BTreeMap::new();
338 let mut revenue: BTreeMap<(String, String, String), i64> = BTreeMap::new();
Margin alerts measure what is sold, and say dollars when a percentage would mislead339 let mut valued: BTreeMap<(String, String, String), i64> = BTreeMap::new();
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily340 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 running341 let mut gave: BTreeMap<(String, String), (Given, i64)> = BTreeMap::new();
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily342 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 it343 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 running344 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 it345 g.1 += u.value;
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97346 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 daily347 let key = (u.day.clone(), bucket.clone());
348 let row = days.entry(key.clone()).or_insert_with(|| entry(&key.0, &key.1));
349 row.own_cost_micros += u.cost;
350 row.value_micros += u.value;
351 row.cash_micros += u.cash;
352 value_by.entry(key.clone()).or_default().push((u.workspace.clone(), u.value as f64));
353 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 mislead354 *revenue.entry((u.day.clone(), u.workspace.clone(), bucket.clone())).or_default() += u.cash;
355 *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 daily356 active.entry(u.day.clone()).or_default().push((u.workspace.clone(), u.value.max(u.cost) as f64));
357 }
358 // 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 running359 // workspace, else by g1t's own count of its units, else by what its
360 // usage cost (so free use carries its own cost), else by what it was
361 // charged; running g1t, and what no one mapped, by each workspace's
362 // share of all usage that day.
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily363 let mut shares: BTreeMap<(String, String, String), i64> = BTreeMap::new();
364 for ((day, bucket), row) in &days {
365 let key = (day.clone(), bucket.clone());
366 let weigh = |m: &BTreeMap<(String, String), Vec<(String, f64)>>| m.get(&key).filter(|w| w.iter().any(|(_, v)| *v > 0.0)).cloned();
367 let weights = if OVERHEAD.contains(&bucket.as_str()) || bucket == UNMAPPED {
368 active.get(day).cloned()
369 } 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 running370 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 daily371 };
372 for (workspace, micros) in attribute(row.cost(), &weights.unwrap_or_default()) {
373 *shares.entry((day.clone(), workspace, bucket.clone())).or_default() += micros;
374 }
375 }
376 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 it377 let workspaces: Vec<WorkspaceDay> = keys
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily378 .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 it379 .map(|(day, workspace, bucket)| {
380 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 running381 // The day's shares given away apply to every bucket, so a
382 // comped workspace's part of running g1t is given too. A
383 // workspace with nothing priced that day used g1t for free.
384 let given = if internal.contains(&workspace) {
385 Given { comped: cost, ..Given::default() }
386 } else {
387 match gave.get(&(day.clone(), workspace.clone())) {
388 Some((given, value)) if *value > 0 => given.of(cost, *value),
389 _ => Given { free: cost.max(0), ..Given::default() },
390 }
Costs: margin is measured on what was sold; comped workspaces, free periods, the trial and the pools are given away, a budget shown beside it391 };
392 WorkspaceDay {
393 cost,
394 revenue: revenue.get(&(day.clone(), workspace.clone(), bucket.clone())).copied().unwrap_or(0),
395 value: valued.get(&(day.clone(), workspace.clone(), bucket.clone())).copied().unwrap_or(0),
396 given,
397 day,
398 workspace,
399 bucket,
400 }
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily401 })
402 .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 it403 for w in &workspaces {
404 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 running405 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 it406 }
407 }
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily408 (days.into_values().collect(), workspaces)
409}
410
411/// A month-end source's day, from the snapshots of what it had come to:
412/// each day's figure less the day before's in the same month (the first
413/// day of a month, or the first snapshot, is its own).
414pub(crate) fn pending_deltas(snapshots: &[(String, String, String, i64, i64)]) -> Vec<UsageRow> {
415 // (day, workspace, source, cost, charge), any order.
416 let mut sorted = snapshots.to_vec();
417 sorted.sort_by(|a, b| (&a.1, &a.2, &a.0).cmp(&(&b.1, &b.2, &b.0)));
418 let mut out = Vec::new();
419 let mut previous: Option<&(String, String, String, i64, i64)> = None;
420 for snap in &sorted {
421 let (day, workspace, source, cost, charge) = snap;
422 let (before_cost, before_charge) = match previous {
423 Some(p) if p.1 == *workspace && p.2 == *source && p.0[..7] == day[..7] => (p.3, p.4),
424 _ => (0, 0),
425 };
426 let (cost, charge) = ((cost - before_cost).max(0), (charge - before_charge).max(0));
427 if cost > 0 || charge > 0 {
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97428 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 daily429 }
430 previous = Some(snap);
431 }
432 out
433}
434
435/// Margin as a share of what was charged, in percent; None when nothing was.
436pub(crate) fn margin_percent(revenue_micros: i64, cost_micros: i64) -> Option<f64> {
437 (revenue_micros > 0).then(|| (revenue_micros - cost_micros) as f64 * 100.0 / revenue_micros as f64)
438}
439
440/// How far `ours` is from `theirs`, in percent of theirs; None when theirs
441/// is nothing.
442pub(crate) fn delta_percent(ours: f64, theirs: f64) -> Option<f64> {
443 (theirs > 0.0).then(|| (ours - theirs) * 100.0 / theirs)
444}
445
446#[derive(Clone, Copy, Debug, PartialEq, Eq)]
447pub(crate) enum DriftKind {
448 /// g1t counted a different number of units than Cloudflare did.
449 Count,
450 /// What Cloudflare charged differs from what the price book says the
451 /// same usage cost.
452 Cost,
453 /// Cloudflare charged for something nothing charges customers for.
454 Leak,
Merge branch 'worktree-agent-a633ac0f7f66d419d'455 /// Model usage AI Gateway put no price on: its cost is not what the
456 /// provider bills, so neither the ledger nor the gateway total has it.
457 Unpriced,
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily458}
459
460impl DriftKind {
461 pub fn as_str(self) -> &'static str {
462 match self {
463 DriftKind::Count => "count",
464 DriftKind::Cost => "cost",
465 DriftKind::Leak => "leak",
Merge branch 'worktree-agent-a633ac0f7f66d419d'466 DriftKind::Unpriced => "unpriced",
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily467 }
468 }
469}
470
471#[derive(Clone, Debug, PartialEq)]
472pub(crate) struct Drift {
473 pub bucket: String,
474 pub kind: DriftKind,
475 pub ours: f64,
476 pub cloudflare: f64,
477 pub delta_percent: Option<f64>,
478}
479
480/// Drift over a window for one bucket: counts more than `threshold`
481/// percent apart, a bill that far from the price book's cost of the same
482/// usage, and cost with nothing charged for it. Under `min_cost_micros`
483/// in all, cost says nothing.
484pub(crate) fn drifts(bucket: &str, days: &[ProductDay], threshold: f64, counted: bool, min_cost_micros: i64) -> Vec<Drift> {
485 let overhead = OVERHEAD.contains(&bucket);
486 let sum = |f: &dyn Fn(&ProductDay) -> f64| days.iter().map(f).sum::<f64>();
487 let cf_cost = sum(&|d| d.cf_cost_micros as f64);
488 let own_cost = sum(&|d| d.own_cost_micros as f64);
489 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 drift490 // Counts are compared from the first day g1t counted: before its meter
491 // was deployed there is only Cloudflare's side. A meter that never
492 // counted anything is compared over every day, so it still shows.
493 let first_counted = days.iter().filter(|d| d.own_quantity > 0.0).map(|d| d.day.as_str()).min();
494 let compared = |d: &&ProductDay| first_counted.is_none_or(|from| d.day.as_str() >= from);
495 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 daily496 let mut out = Vec::new();
497 if counted && cf_quantity > 0.0 {
498 let delta = delta_percent(own_quantity, cf_quantity);
499 if delta.is_some_and(|d| d.abs() > threshold) {
500 out.push(Drift { bucket: bucket.into(), kind: DriftKind::Count, ours: own_quantity, cloudflare: cf_quantity, delta_percent: delta });
501 }
502 }
503 let enough = cf_cost.max(own_cost) >= min_cost_micros as f64;
Merge branch 'worktree-agent-a633ac0f7f66d419d'504 // Models: what AI Gateway priced g1t's own provider traffic at (its
505 // lines, as "Cloudflare's" side) against the ledger's model cost. Only
506 // once the gateway has been read; then the ledger having none of it is
507 // drift too (traffic no run was charged for).
508 let models = NOT_CLOUDFLARE.contains(&bucket) && cf_cost > 0.0;
509 if enough && !overhead && cf_cost > 0.0 && (own_cost > 0.0 || models) {
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily510 let delta = delta_percent(own_cost, cf_cost);
511 if delta.is_some_and(|d| d.abs() > threshold) {
512 out.push(Drift { bucket: bucket.into(), kind: DriftKind::Cost, ours: own_cost, cloudflare: cf_cost, delta_percent: delta });
513 }
514 }
Billing: AI Gateway's analytics are read with the token that can see them, and a gateway that priced nothing is said515 // The ledger has model cost and the gateway priced none of it: a token
516 // that cannot see AI Gateway reads as no rows, never an error, so this
517 // is not agreement. Said, rather than left as no row at all.
518 if NOT_CLOUDFLARE.contains(&bucket) && cf_cost <= 0.0 && own_cost >= min_cost_micros as f64 && own_cost > 0.0 {
519 out.push(Drift { bucket: bucket.into(), kind: DriftKind::Cost, ours: own_cost, cloudflare: 0.0, delta_percent: None });
520 }
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily521 if !overhead && cf_cost >= min_cost_micros as f64 && value <= 0.0 {
522 out.push(Drift { bucket: bucket.into(), kind: DriftKind::Leak, ours: value, cloudflare: cf_cost, delta_percent: None });
523 }
524 out
525}
526
Merge branch 'worktree-agent-a633ac0f7f66d419d'527/// What can make AI Gateway's cost differ from what the providers bill,
528/// said for staff: cache tokens (priced by the gateway at its own rates for
529/// them, which may lag the provider's), requests Cloudflare billed itself,
530/// models it has no price for, and runs settled short.
531fn caveat_notes(c: &costs::GatewayCaveats) -> Vec<String> {
532 let mut notes = Vec::new();
533 if c.cache_read_tokens > 0.0 || c.cache_write_tokens > 0.0 {
534 notes.push(format!(
535 "{} 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",
536 crate::features::thousands(c.cache_read_tokens.round() as u64),
537 crate::features::thousands(c.cache_write_tokens.round() as u64)
538 ));
539 }
540 if c.wholesale_usd > 0.0 {
541 notes.push(format!(
542 "{} of it Cloudflare billed itself (unified billing): that part is on Cloudflare's bill, not a provider's",
543 dollars(micros(c.wholesale_usd))
544 ));
545 }
546 if !c.unpriced.is_empty() {
547 notes.push(format!("it has no price for {} (tokens used, $0)", c.unpriced.join(", ")));
548 }
549 if c.short_runs > 0 {
550 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));
551 }
552 notes
553}
554
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97555/// Where a testing reset's history starts: all of a workspace's ledger.
556pub(crate) const RESET_HISTORY_FROM: &str = "2000-01-01";
557
558/// What a testing reset wiped that g1t paid for, on one day for one
559/// bucket (`reset_costs`).
560#[derive(Clone, Debug, PartialEq)]
561pub(crate) struct Wiped {
562 pub day: String,
563 pub bucket: String,
564 pub cost: i64,
565 pub value: i64,
566}
567
568/// A workspace's usage rows, about to be wiped, as what g1t paid for: the
569/// rows with a cost, by day and bucket, valued as the reconciliation
570/// valued them (at price where nothing paid). Plan payments, credits and
571/// a workspace's own model provider cost g1t nothing and are left out.
572pub(crate) fn wiped(rows: &[UsageRow], revenue_map: &BTreeMap<String, String>, margin_percent: u32) -> Vec<Wiped> {
573 let mut by: BTreeMap<(String, String), (i64, i64)> = BTreeMap::new();
574 for u in rows.iter().filter(|u| u.cost > 0) {
575 let bucket = u.bucket.clone().unwrap_or_else(|| revenue_map.get(&u.key).cloned().unwrap_or_else(|| NOT_CLOUDFLARE[0].to_owned()));
576 let sums = by.entry((u.day.clone(), bucket)).or_default();
577 sums.0 += u.cost;
578 sums.1 += u.value.max(0);
579 }
580 by.into_iter()
581 .map(|((day, bucket), (cost, value))| Wiped {
582 day,
583 bucket,
584 cost,
585 value: if value > 0 { value } else { crate::credits::with_margin(cost, margin_percent) },
586 })
587 .collect()
588}
589
590/// What testing resets kept, each (day, workspace, bucket, cost, value),
591/// as usage rows: valued as before, nothing paid, all of it given away
592/// (why "testing resets"). A reset's own row (bucket '') is not usage.
593pub(crate) fn reset_usage(kept: &[(String, String, String, i64, i64)]) -> Vec<UsageRow> {
594 kept.iter()
595 .filter(|(_, _, bucket, cost, value)| !bucket.is_empty() && (*cost != 0 || *value != 0))
596 .map(|(day, workspace, bucket, cost, value)| UsageRow {
597 day: day.clone(),
598 workspace: workspace.clone(),
599 key: "reset".into(),
600 bucket: Some(bucket.clone()),
601 value: *value,
602 cash: 0,
603 cost: *cost,
604 given: Given { reset: *value, ..Given::default() },
605 })
606 .collect()
607}
608
609/// A testing reset inside the drift window.
610#[derive(Clone, Debug, PartialEq)]
611pub(crate) struct ResetNote {
612 pub workspace: String,
613 /// The UTC day it was reset.
614 pub day: String,
615 /// Whether it kept what it wiped (`reset_costs`, migration 0046):
616 /// then the ledger's side has it, given away. A reset from before
617 /// that wiped model usage the gateway still counts.
618 pub recorded: bool,
619 /// Of what it kept, model cost on the window's days.
620 pub models_micros: i64,
621}
622
623/// The resets: each audit entry (account `ws_<slug>`, when) and each kept
624/// reset (workspace, reset_at, its model cost in the window). An audit
625/// entry with no kept reset at the same instant is from before resets kept
626/// what they wiped.
627pub(crate) fn reset_notes(audits: &[(String, String)], kept: &[(String, String, i64)]) -> Vec<ResetNote> {
628 let mut notes: Vec<(String, ResetNote)> = kept
629 .iter()
630 .map(|(workspace, at, models)| {
631 (at.clone(), ResetNote { workspace: workspace.clone(), day: at[..10.min(at.len())].to_owned(), recorded: true, models_micros: *models })
632 })
633 .collect();
634 for (account, at) in audits {
635 let workspace = account.strip_prefix("ws_").unwrap_or(account);
636 if !kept.iter().any(|(w, a, _)| w == workspace && a == at) {
637 notes.push((at.clone(), ResetNote { workspace: workspace.to_owned(), day: at[..10.min(at.len())].to_owned(), recorded: false, models_micros: 0 }));
638 }
639 }
640 notes.sort_by(|a, b| a.0.cmp(&b.0).then(a.1.workspace.cmp(&b.1.workspace)));
641 notes.into_iter().map(|(_, n)| n).collect()
642}
643
644/// Model usage a reset wiped before resets kept it is not a leak: while
645/// such a reset is in the window the models leak is not raised, and the
646/// models cost drift says what the gap is.
647pub(crate) fn wiped_not_leaked(drift: &Drift, resets: &[ResetNote]) -> bool {
648 drift.kind == DriftKind::Leak && NOT_CLOUDFLARE.contains(&drift.bucket.as_str()) && resets.iter().any(|r| !r.recorded)
649}
650
651/// What the models drift says about resets in the window.
652fn reset_sentences(resets: &[ResetNote]) -> Vec<String> {
653 resets
654 .iter()
655 .filter_map(|r| {
656 if !r.recorded {
657 Some(format!(
658 "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 {}.",
659 r.workspace,
660 r.day,
661 day_after(&r.day, DRIFT_DAYS)
662 ))
663 } else if r.models_micros > 0 {
664 Some(format!(
665 "The ledger's figure includes {} of model cost wiped by a testing reset of {} on {}, counted as given away (testing resets).",
666 dollars(r.models_micros),
667 r.workspace,
668 r.day
669 ))
670 } else {
671 None
672 }
673 })
674 .collect()
675}
676
677/// The models drift's detail: the gateway's total against the ledger's,
678/// and any testing reset in the window.
679pub(crate) fn models_detail(drift: &Drift, caveats: &costs::GatewayCaveats, resets: &[ResetNote]) -> String {
Billing: AI Gateway's analytics are read with the token that can see them, and a gateway that priced nothing is said680 if drift.cloudflare <= 0.0 {
681 return format!(
682 "Models: the ledger's model cost is {} over the last {DRIFT_DAYS} days and AI Gateway priced nothing, so the two were not compared. Either the gateway's analytics cannot be seen (Cloudflare answers a token without AI Gateway: Read with no rows, not an error; billing reads them with CLOUDFLARE_USAGE_TOKEN, then CLOUDFLARE_BILLING_TOKEN), or model calls went around the gateway.",
683 dollars(drift.ours as i64)
684 );
685 }
Merge branch 'worktree-agent-a633ac0f7f66d419d'686 let lower = drift.ours < drift.cloudflare;
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97687 let wiped = resets.iter().any(|r| !r.recorded);
Merge branch 'worktree-agent-a633ac0f7f66d419d'688 let mut detail = format!(
689 "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}%). {}",
690 dollars(drift.cloudflare as i64),
691 dollars(drift.ours as i64),
692 drift.delta_percent.unwrap_or(0.0),
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97693 if lower && wiped {
694 "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."
695 } else if lower {
Merge branch 'worktree-agent-a633ac0f7f66d419d'696 "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)."
697 } else {
698 "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."
699 }
700 );
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97701 for sentence in reset_sentences(resets) {
702 detail.push(' ');
703 detail.push_str(&sentence);
704 }
Merge branch 'worktree-agent-a633ac0f7f66d419d'705 let notes = caveat_notes(caveats);
706 if !notes.is_empty() {
707 detail.push_str(" The gateway's cost may be off: ");
708 detail.push_str(&notes.join("; "));
709 detail.push('.');
710 }
711 detail
712}
713
714/// The unpriced drift's detail.
715pub(crate) fn unpriced_detail(caveats: &costs::GatewayCaveats) -> String {
716 format!(
717 "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.",
718 caveat_notes(&costs::GatewayCaveats { cache_read_tokens: 0.0, cache_write_tokens: 0.0, wholesale_usd: 0.0, ..caveats.clone() }).join("; ")
719 )
720}
721
722/// Model usage AI Gateway could not price over the window, as drift on
723/// the models bucket: models with tokens and no cost, or runs settled
724/// short. None when there is none.
725pub(crate) fn unpriced_drift(caveats: &costs::GatewayCaveats) -> Option<(Drift, String)> {
726 if caveats.unpriced.is_empty() && caveats.short_runs == 0 {
727 return None;
728 }
729 let drift = Drift {
730 bucket: NOT_CLOUDFLARE[0].into(),
731 kind: DriftKind::Unpriced,
732 ours: f64::from(caveats.short_runs),
733 cloudflare: caveats.unpriced.len() as f64,
734 delta_percent: None,
735 };
736 Some((drift, unpriced_detail(caveats)))
737}
738
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily739/// When the last `days` in a row (each with enough cost to say something)
740/// were all under the floor: the first of them and the worst margin.
741/// Each item is a day's (day, revenue, cost).
742pub(crate) fn breach(series: &[(String, i64, i64)], floor_percent: f64, days: usize, min_cost_micros: i64) -> Option<(String, f64)> {
743 if days == 0 || series.len() < days {
744 return None;
745 }
746 let tail = &series[series.len() - days..];
747 let mut worst = f64::INFINITY;
748 for (_, revenue, cost) in tail {
749 if *cost < min_cost_micros {
750 return None;
751 }
752 let margin = margin_percent(*revenue, *cost).unwrap_or(-100.0);
753 if margin >= floor_percent {
754 return None;
755 }
756 worst = worst.min(margin);
757 }
758 Some((tail[0].0.clone(), worst))
759}
760
761/// `total` shared out in proportion to `weights`, in whole millionths that
762/// add up to it exactly (largest remainder first). Nothing to share, or no
763/// weight, shares nothing.
764pub(crate) fn attribute(total: i64, weights: &[(String, f64)]) -> Vec<(String, i64)> {
765 let mut merged: BTreeMap<String, f64> = BTreeMap::new();
766 for (key, w) in weights {
767 *merged.entry(key.clone()).or_default() += w.max(0.0);
768 }
769 let sum: f64 = merged.values().sum();
770 if total <= 0 || sum <= 0.0 {
771 return Vec::new();
772 }
773 let mut shares: Vec<(String, i64, f64)> = merged
774 .into_iter()
775 .map(|(key, w)| {
776 let exact = total as f64 * w / sum;
777 (key, exact.floor() as i64, exact - exact.floor())
778 })
779 .collect();
780 let mut left = total - shares.iter().map(|s| s.1).sum::<i64>();
781 let mut order: Vec<usize> = (0..shares.len()).collect();
782 order.sort_by(|a, b| shares[*b].2.total_cmp(&shares[*a].2).then(shares[*a].0.cmp(&shares[*b].0)));
783 for index in order {
784 if left <= 0 {
785 break;
786 }
787 shares[index].1 += 1;
788 left -= 1;
789 }
790 shares.into_iter().filter(|s| s.1 > 0).map(|(key, micros, _)| (key, micros)).collect()
791}
792
793/// Workspaces that cost g1t more than `factor` times what they paid, with
794/// at least `floor_micros` of cost: each (workspace, cost, revenue), the
795/// biggest gap first.
Models' margin read -14%: usage nothing paid for is valued at price, not $0796/// What a day's usage was worth at price. g1t's own workspaces are valued
797/// at price. So is usage nothing paid for, neither charged nor drawn from
798/// the plan, a trial, a pool or a gift (a free period): it was given away at
799/// its price, not sold for nothing. Anything paid keeps what it was paid, so
800/// a discount still shows as one.
801pub(crate) fn usage_value(internal: bool, cost: i64, paid: i64, margin_percent: u32) -> i64 {
802 if internal || (paid == 0 && cost > 0) {
803 return crate::credits::with_margin(cost, margin_percent);
804 }
805 paid
806}
807
Margin alerts measure what is sold, and say dollars when a percentage would mislead808/// What the overall alert says: the money as money, and a percentage only
809/// while there is enough coming in for one to mean something (a few cents
810/// against dollars of cost reads as -8000%).
811pub(crate) fn overall_detail(took: i64, spent: i64, days: usize, floor: f64, worst: f64) -> String {
812 if took < 1_000_000 * days as i64 {
813 return format!(
814 "All of g1t, comped workspaces left out: took in {} against {} of Cloudflare's bill over {days} days.",
815 dollars(took),
816 dollars(spent)
817 );
818 }
819 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}%.")
820}
821
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily822pub(crate) fn anomalies(rows: &[(String, i64, i64)], factor: f64, floor_micros: i64) -> Vec<(String, i64, i64)> {
823 let mut out: Vec<(String, i64, i64)> = rows
824 .iter()
825 .filter(|(_, cost, revenue)| *cost >= floor_micros && *cost as f64 > *revenue as f64 * factor)
826 .cloned()
827 .collect();
828 out.sort_by(|a, b| (b.1 - b.2).cmp(&(a.1 - a.2)).then(a.0.cmp(&b.0)));
829 out
830}
831
832/// Cloudflare's marginal rate for one of its units: the median over the
833/// charged days of cost over quantity, in dollars. None while the included
834/// amounts still cover it. Each item is a day's (quantity, cost).
835pub(crate) fn billed_rate(days: &[(f64, f64)]) -> Option<f64> {
836 let mut rates: Vec<f64> = days.iter().filter(|(q, c)| *q > 0.0 && *c > 0.0).map(|(q, c)| c / q).collect();
837 if rates.is_empty() {
838 return None;
839 }
840 rates.sort_by(f64::total_cmp);
841 Some(rates[rates.len() / 2])
842}
843
844/// What one of g1t's units costs, from Cloudflare's rate per its own unit
845/// and how many of Cloudflare's units each of g1t's took: if Cloudflare
846/// counts three operations for every git operation g1t counts, a git
847/// operation costs three of Cloudflare's. None without enough of g1t's
848/// units to say.
849pub(crate) fn derived_unit_cost(rate_per_cf_unit: f64, cf_units: f64, own_units: f64) -> Option<f64> {
850 (own_units >= MIN_UNITS && cf_units > 0.0 && rate_per_cf_unit > 0.0).then(|| rate_per_cf_unit * cf_units / own_units)
851}
852
853/// How many units a price is per: `1,000 operations` → 1,000, `million
854/// requests` → 1,000,000, `second` → 1.
855pub(crate) fn unit_size(unit: &str) -> f64 {
856 let first = unit.split_whitespace().next().unwrap_or_default().replace(',', "");
857 match first.as_str() {
858 "million" => 1_000_000.0,
859 "thousand" => 1_000.0,
860 n => n.parse().unwrap_or(1.0),
861 }
862}
863
864fn day_before(day: &str, days: u64) -> String {
865 let ms = g1t_contracts::time::parse_rfc3339(&format!("{day}T00:00:00Z")).unwrap_or(0);
866 rfc3339(ms.saturating_sub(days * DAY_MS))[..10].to_owned()
867}
868
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97869fn day_after(day: &str, days: u64) -> String {
870 let ms = g1t_contracts::time::parse_rfc3339(&format!("{day}T00:00:00Z")).unwrap_or(0);
871 rfc3339(ms + days * DAY_MS)[..10].to_owned()
872}
873
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily874/// Dollars to the cent from a dollar up, finer below: `$17.02`, `$0.063`.
875fn dollars(micros: i64) -> String {
876 if micros.abs() >= 1_000_000 {
877 let cents = (micros as f64 / 10_000.0).round() as i64;
878 format!("{}${}.{:02}", if cents < 0 { "-" } else { "" }, cents.abs() / 100, cents.abs() % 100)
879 } else {
880 crate::features::dollars(micros)
881 }
882}
883
884/// The days a plan payment is spread over.
885const PLAN_DAYS: u64 = 30;
886
887/// `micros` paid on `day` spread evenly over `days` days from it, in
888/// whole micros that add up to it (the first days take the remainder).
889pub(crate) fn spread(day: &str, micros: i64, days: u64) -> Vec<(String, i64)> {
890 if micros <= 0 || days == 0 {
891 return Vec::new();
892 }
893 let start = g1t_contracts::time::parse_rfc3339(&format!("{}T00:00:00Z", &day[..10.min(day.len())])).unwrap_or(0);
894 let each = micros / days as i64;
895 let rest = micros % days as i64;
896 (0..days)
897 .map(|n| (rfc3339(start + n * DAY_MS)[..10].to_owned(), each + i64::from((n as i64) < rest)))
898 .collect()
899}
900
901// ---------------------------------------------------------------------
902// The daily run, and what sudo reads.
903// ---------------------------------------------------------------------
904
905#[derive(Serialize)]
906struct Mail<'a> {
907 to: &'a str,
908 from: &'a str,
909 subject: &'a str,
910 text: String,
911 html: String,
912}
913
914fn escape(text: &str) -> String {
915 text.replace('&', "&amp;").replace('<', "&lt;").replace('>', "&gt;").replace('"', "&quot;")
916}
917
918/// Emails staff through Cloudflare Email Sending, the `EMAIL` binding.
Spend caps: a monthly budget for comped workspaces and a daily breaker on what g1t pays919pub(crate) async fn email_staff(env: &Env, to: &str, subject: &str, lines: &[String]) -> Result<()> {
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily920 let link = "https://sudo.g1t.sh/costs";
921 let text = format!("{}\n\nCosts & margin: {link}\n\nSent by g1t-billing's margin guard (COSTS_ALERT_EMAIL).\n", lines.join("\n\n"));
922 let mut html = String::from("<div style=\"font-family:system-ui,sans-serif;max-width:560px;margin:0 auto;padding:24px 16px;color:#16150f\">");
923 for line in lines {
924 html.push_str(&format!("<p style=\"font-size:15px;line-height:1.6\">{}</p>", escape(line)));
925 }
926 html.push_str(&format!(
927 "<p><a href=\"{link}\">Open Costs &amp; margin in sudo</a></p><p style=\"font-size:13px;color:#6e6a5e\">Sent by g1t-billing's margin guard (COSTS_ALERT_EMAIL).</p></div>"
928 ));
929 let mail = Mail { to, from: "g1t <noreply@g1t.sh>", subject, text, html };
930 let binding = g1t_kit::js::binding(env, "EMAIL")?;
931 g1t_kit::js::call(&binding, "send", &[g1t_kit::js::to_js(&mail)?]).await?;
932 Ok(())
933}
934
935#[derive(Deserialize)]
936struct AlertRow {
937 id: String,
938 kind: String,
939 subject: String,
940 detail: String,
941 since: String,
942 opened_at: String,
943 emailed_at: Option<String>,
944}
945
946impl From<AlertRow> for MarginAlert {
947 fn from(r: AlertRow) -> Self {
948 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 }
949 }
950}
951
952#[derive(Deserialize)]
953struct MarginRow {
954 day: String,
955 bucket: String,
956 cf_cost_micros: i64,
957 own_cost_micros: i64,
958 value_micros: i64,
959 cash_micros: i64,
960 cf_quantity: f64,
961 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 it962 #[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 running963 given_comped_micros: Option<i64>,
964 #[serde(default)]
965 given_free_micros: Option<i64>,
966 #[serde(default)]
967 given_trial_micros: Option<i64>,
968 #[serde(default)]
969 given_pool_micros: Option<i64>,
Merge branch 'worktree-agent-a633ac0f7f66d419d'970 #[serde(default)]
971 given_discount_micros: Option<i64>,
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging972 #[serde(default)]
973 given_credit_promotional_micros: Option<i64>,
974 #[serde(default)]
975 given_credit_goodwill_micros: Option<i64>,
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97976 #[serde(default)]
977 given_reset_micros: Option<i64>,
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily978}
979
980impl From<MarginRow> for ProductDay {
981 fn from(r: MarginRow) -> Self {
982 ProductDay {
983 day: r.day,
984 bucket: r.bucket,
985 cf_cost_micros: r.cf_cost_micros,
986 own_cost_micros: r.own_cost_micros,
987 value_micros: r.value_micros,
988 cash_micros: r.cash_micros,
989 cf_quantity: r.cf_quantity,
990 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 running991 given: Given {
992 comped: r.given_comped_micros.unwrap_or(0),
993 free: r.given_free_micros.unwrap_or(0),
994 trial: r.given_trial_micros.unwrap_or(0),
995 pool: r.given_pool_micros.unwrap_or(0),
Merge branch 'worktree-agent-a633ac0f7f66d419d'996 discount: r.given_discount_micros.unwrap_or(0),
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging997 credit_promotional: r.given_credit_promotional_micros.unwrap_or(0),
998 credit_goodwill: r.given_credit_goodwill_micros.unwrap_or(0),
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97999 reset: r.given_reset_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 running1000 },
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1001 }
1002 }
1003}
1004
1005impl Billing {
1006 /// The day's work: read Cloudflare's bill and g1t's own counts,
1007 /// reconcile, look for drift, measure unit costs, apply prices whose
1008 /// day has come, and raise or clear alerts.
1009 pub(crate) async fn costs_daily(&self, env: &Env, keeper: &crate::keeper::Keeper) -> Result<CostsRun> {
1010 let mut run = CostsRun::default();
1011 let (since, until) = match self.read_cloudflare(keeper, &mut run.problems).await? {
1012 Some((since, until, lines)) => {
1013 run.lines = lines;
1014 (since, until)
1015 }
1016 // Without the bill, still reconcile what g1t knows itself, over
1017 // the same days the bill would be read for.
1018 None => {
1019 #[derive(Deserialize)]
1020 struct Last {
1021 day: Option<String>,
1022 }
1023 let last = self.db.prepare("SELECT MAX(day) AS day FROM margin_days").first::<Last>(None).await?.and_then(|l| l.day);
1024 costs::window(last.as_deref(), now_ms())
1025 }
1026 };
Costs: Cloudflare's subscriptions read from Cloudflare each day, the estimate only until then; sudo's costs split into Costs & margin and Bill & pricing1027 // Not a problem for the run: the last read, or the estimate, stays.
1028 if keeper.can_read_bill()
1029 && let Err(error) = self.read_subscriptions(keeper).await
1030 {
1031 worker::console_error!("Cloudflare's subscriptions were not read: {error}");
1032 }
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1033 if let Err(error) = self.count_own(&since, &until).await {
1034 run.problems.push(format!("g1t's own counts could not be read: {error}"));
1035 }
1036 self.snapshot_pending(&until).await?;
Models' margin read -14%: usage nothing paid for is valued at price, not $01037 // Reconciled over the whole window sudo shows, not only the days the
1038 // bill was read for: it reads only what is already kept, so a change
1039 // in how a day is valued reaches every day shown at the next run.
1040 let window = day_before(&until, costs::BACKFILL_DAYS - 1);
1041 let reconcile_from = if window < since { window } else { since.clone() };
1042 run.days = self.reconcile_range(&reconcile_from, &until).await?;
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1043 let drift = self.find_drift(&until).await?;
1044 run.proposals = self.measure_units(&until).await?;
1045 self.apply_due_versions().await?;
1046 run.alerts = self.raise_alerts(env, &until, &drift).await?;
1047 if let Some(identity) = &self.identity
1048 && let Err(error) = self.tell_owners_of_rises(identity).await
1049 {
1050 run.problems.push(format!("owners could not be told of a price rise: {error}"));
1051 }
1052 for problem in &run.problems {
1053 worker::console_warn!("costs: {problem}");
1054 }
1055 Ok(run)
1056 }
1057
1058 /// What each month-end source had come to by the end of `day`.
1059 async fn snapshot_pending(&self, day: &str) -> Result<()> {
1060 self.db
1061 .prepare(
1062 "INSERT INTO pending_days (day, workspace, source, cost_micros, charge_micros)
1063 SELECT ?1, workspace, source, COALESCE(cost_micros, 0), COALESCE(charge_micros, 0) FROM pending_usage WHERE month = ?2
1064 ON CONFLICT (day, workspace, source) DO UPDATE SET cost_micros = excluded.cost_micros, charge_micros = excluded.charge_micros",
1065 )
1066 .bind(&[day.into(), day[..7].into()])?
1067 .run()
1068 .await?;
1069 Ok(())
1070 }
1071
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb971072 /// What customers were charged on the days, by workspace and key: every
1073 /// workspace's, or only `only`'s.
1074 async fn usage_rows(&self, since: &str, until: &str, only: Option<&str>) -> Result<Vec<UsageRow>> {
1075 let only_sql = only.unwrap_or("");
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1076 #[derive(Deserialize)]
1077 struct Row {
1078 day: String,
1079 workspace: String,
1080 key: String,
1081 internal: i64,
1082 own_provider: i64,
1083 cash: Option<i64>,
1084 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 running1085 trial: Option<i64>,
1086 oss: Option<i64>,
1087 covered: Option<i64>,
Merge branch 'worktree-agent-a633ac0f7f66d419d'1088 discount: Option<i64>,
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1089 cost: Option<i64>,
1090 }
1091 let charged_here = crate::storage::CHARGED_HERE.iter().map(|s| format!("'{s}'")).collect::<Vec<_>>().join(", ");
1092 let end = format!("{until}T23:59:59.999Z");
1093 let rows = self
1094 .db
1095 .prepare(format!(
1096 "SELECT substr(created_at, 1, 10) AS day, workspace,
1097 CASE WHEN task = 'deployments' AND reference LIKE 'deploy/%' THEN 'builds' ELSE COALESCE(task, 'other') END AS key,
1098 CASE WHEN workspace IN ({internal}) THEN 1 ELSE 0 END AS internal,
1099 CASE WHEN billed_to = 'workspace' THEN 1 ELSE 0 END AS own_provider,
1100 -SUM(amount_micros) AS cash,
1101 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 running1102 SUM(COALESCE(trial_micros, 0)) AS trial,
1103 SUM(COALESCE(oss_micros, 0)) AS oss,
1104 SUM(COALESCE(given_micros, 0)) AS covered,
Merge branch 'worktree-agent-a633ac0f7f66d419d'1105 SUM(COALESCE(discount_micros, 0)) AS discount,
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1106 SUM(COALESCE(cost_micros, 0)) AS cost
1107 FROM ledger
1108 WHERE kind = 'usage' AND created_at >= ?1 AND created_at <= ?2 AND COALESCE(task, '') NOT IN ({charged_here})
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb971109 AND (?3 = '' OR workspace = ?3)
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1110 GROUP BY 1, 2, 3, 4, 5",
1111 internal = crate::sales::INTERNAL_SQL
1112 ))
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb971113 .bind(&[since.into(), end.as_str().into(), only_sql.into()])?
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1114 .all()
1115 .await?
1116 .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 it1117 let mut internal = BTreeSet::new();
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1118 let mut out: Vec<UsageRow> = rows
1119 .into_iter()
1120 .map(|r| {
1121 // A workspace's own model provider was paid there: no cost
1122 // to g1t. g1t's own workspaces are valued at price.
1123 let cost = if r.own_provider == 1 { 0 } else { r.cost.unwrap_or(0) };
1124 let cash = r.cash.unwrap_or(0);
Merge branch 'worktree-agent-a633ac0f7f66d419d'1125 // A discount took its part below cost plus the margin: it is
1126 // valued at price and that part counted as given, so a
1127 // discounted sale never reads as margin lost.
1128 let discount = r.discount.unwrap_or(0).max(0);
1129 let paid = cash + r.drawn.unwrap_or(0) + discount;
Models' margin read -14%: usage nothing paid for is valued at price, not $01130 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 running1131 let given = if r.internal == 1 {
1132 Given { comped: value, ..Given::default() }
1133 } else if paid == 0 && cost > 0 {
1134 Given { free: value, ..Given::default() }
1135 } else {
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging1136 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 running1137 };
Costs: margin is measured on what was sold; comped workspaces, free periods, the trial and the pools are given away, a budget shown beside it1138 if r.internal == 1 {
1139 internal.insert(r.workspace.clone());
1140 }
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb971141 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 daily1142 })
1143 .collect();
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging1144 // Credits from g1t: what promotional and goodwill credit paid for
1145 // is given, not money in; a refund gives money back on its day.
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb971146 let (mut draws, mut refunds) = self.credit_effects(since, until).await?;
1147 if let Some(only) = only {
1148 draws.retain(|(workspace, _)| workspace == only);
1149 refunds.retain(|r| r.workspace == only);
1150 }
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging1151 apply_credits(&mut out, &draws, &refunds);
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1152 // Month-end sources, from their daily snapshots.
1153 #[derive(Deserialize)]
1154 struct Snap {
1155 day: String,
1156 workspace: String,
1157 source: String,
1158 cost_micros: i64,
1159 charge_micros: i64,
1160 }
1161 let snaps = self
1162 .db
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb971163 .prepare("SELECT day, workspace, source, cost_micros, charge_micros FROM pending_days WHERE day >= ?1 AND day <= ?2 AND (?3 = '' OR workspace = ?3)")
1164 .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 daily1165 .all()
1166 .await?
1167 .results::<Snap>()?
1168 .into_iter()
1169 .filter(|s| crate::storage::CHARGED_HERE.contains(&s.source.as_str()) || s.source == "domains")
1170 .map(|s| (s.day, s.workspace, s.source, s.cost_micros, s.charge_micros))
1171 .collect::<Vec<_>>();
Costs: margin is measured on what was sold; comped workspaces, free periods, the trial and the pools are given away, a budget shown beside it1172 out.extend(pending_deltas(&snaps).into_iter().filter(|u| u.day.as_str() >= since).map(|mut u| {
1173 if internal.contains(&u.workspace) {
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 running1174 u.given = Given { comped: u.value, ..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 it1175 }
1176 u
1177 }));
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1178 // The plan's price, spread over the 30 days it pays for, so a month's
1179 // payment does not read as one very good day and 29 bad ones.
1180 #[derive(Deserialize)]
1181 struct Plan {
1182 day: String,
1183 workspace: String,
1184 micros: Option<i64>,
1185 }
1186 let plans = self
1187 .db
1188 .prepare(
1189 "SELECT substr(paid_at, 1, 10) AS day, workspace, SUM(amount_micros) AS micros FROM plan_payments
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb971190 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 daily1191 )
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb971192 .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 daily1193 .all()
1194 .await?
1195 .results::<Plan>()?;
1196 for p in plans {
1197 for (day, micros) in spread(&p.day, p.micros.unwrap_or(0), PLAN_DAYS) {
1198 if day.as_str() >= since && day.as_str() <= until {
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb971199 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 daily1200 }
1201 }
1202 }
1203 Ok(out)
1204 }
1205
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb971206 /// Which bucket each ledger key (and month-end source) is revenue of.
1207 async fn revenue_map(&self) -> Result<BTreeMap<String, String>> {
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1208 #[derive(Deserialize)]
1209 struct Map {
1210 key: String,
1211 bucket: String,
1212 }
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb971213 Ok(self
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1214 .db
1215 .prepare("SELECT key, bucket FROM revenue_map")
1216 .all()
1217 .await?
1218 .results::<Map>()?
1219 .into_iter()
1220 .map(|m| (m.key, m.bucket))
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb971221 .collect())
1222 }
1223
1224 /// What a testing reset of `workspace` is about to wipe that g1t paid
1225 /// for, a row per day and bucket: its whole ledger and month-end
1226 /// snapshots, valued as the reconciliation values them.
1227 pub(crate) async fn wiped_by_reset(&self, workspace: &str) -> Result<Vec<Wiped>> {
1228 let today = rfc3339(now_ms())[..10].to_owned();
1229 let rows = self.usage_rows(RESET_HISTORY_FROM, &today, Some(workspace)).await?;
1230 Ok(wiped(&rows, &self.revenue_map().await?, self.margin_percent))
1231 }
1232
1233 /// What testing resets kept for the days, as usage rows.
1234 async fn reset_rows(&self, since: &str, until: &str) -> Result<Vec<UsageRow>> {
1235 #[derive(Deserialize)]
1236 struct Kept {
1237 day: String,
1238 workspace: String,
1239 bucket: String,
1240 cost: Option<i64>,
1241 value: Option<i64>,
1242 }
1243 let kept = self
1244 .db
1245 .prepare(
1246 "SELECT day, workspace, bucket, SUM(cost_micros) AS cost, SUM(value_micros) AS value FROM reset_costs
1247 WHERE day >= ?1 AND day <= ?2 AND bucket <> '' GROUP BY day, workspace, bucket",
1248 )
1249 .bind(&[since.into(), until.into()])?
1250 .all()
1251 .await?
1252 .results::<Kept>()?;
1253 Ok(reset_usage(
1254 &kept.into_iter().map(|k| (k.day, k.workspace, k.bucket, k.cost.unwrap_or(0), k.value.unwrap_or(0))).collect::<Vec<_>>(),
1255 ))
1256 }
1257
1258 /// Testing resets on or after `since` (the day they wiped usage up to
1259 /// is their own, so one before it wiped nothing in the days): those
1260 /// that kept what they wiped (`reset_costs`) and those from before
1261 /// resets did, known only from the audit log.
1262 async fn resets_since(&self, since: &str, until: &str) -> Result<Vec<ResetNote>> {
1263 let end = format!("{until}T23:59:59.999Z");
1264 #[derive(Deserialize)]
1265 struct Audit {
1266 account: String,
1267 created_at: String,
1268 }
1269 let audits = self
1270 .db
1271 .prepare("SELECT account, created_at FROM admin_actions WHERE action = 'reset' AND created_at >= ?1 AND created_at <= ?2")
1272 .bind(&[since.into(), end.as_str().into()])?
1273 .all()
1274 .await?
1275 .results::<Audit>()?;
1276 #[derive(Deserialize)]
1277 struct Kept {
1278 workspace: String,
1279 reset_at: String,
1280 models: Option<i64>,
1281 }
1282 let kept = self
1283 .db
1284 .prepare(
1285 "SELECT workspace, reset_at, SUM(CASE WHEN bucket = ?3 AND day >= ?1 THEN cost_micros ELSE 0 END) AS models
1286 FROM reset_costs WHERE reset_at >= ?1 AND reset_at <= ?2 GROUP BY workspace, reset_at",
1287 )
1288 .bind(&[since.into(), end.as_str().into(), NOT_CLOUDFLARE[0].into()])?
1289 .all()
1290 .await?
1291 .results::<Kept>()?;
1292 Ok(reset_notes(
1293 &audits.into_iter().map(|a| (a.account, a.created_at)).collect::<Vec<_>>(),
1294 &kept.into_iter().map(|k| (k.workspace, k.reset_at, k.models.unwrap_or(0))).collect::<Vec<_>>(),
1295 ))
1296 }
1297
1298 /// Reconciles the days and writes `margin_days` and `workspace_costs`.
1299 async fn reconcile_range(&self, since: &str, until: &str) -> Result<u32> {
1300 let rules = self.rules().await?;
1301 let revenue_map = self.revenue_map().await?;
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1302 let lines = self
1303 .db
1304 .prepare("SELECT day, source, product, meter, quantity, cost_usd FROM cost_lines WHERE day >= ?1 AND day <= ?2")
1305 .bind(&[since.into(), until.into()])?
1306 .all()
1307 .await?
1308 .results::<LineRow>()?;
1309 let own = self
1310 .db
1311 .prepare("SELECT day, meter, workspace, quantity FROM own_counts WHERE day >= ?1 AND day <= ?2")
1312 .bind(&[since.into(), until.into()])?
1313 .all()
1314 .await?
1315 .results::<OwnRow>()?;
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb971316 let mut usage = self.usage_rows(since, until, None).await?;
1317 // What testing resets wiped: still paid for, now given away.
1318 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 running1319 #[derive(Deserialize)]
1320 struct Internal {
1321 workspace: String,
1322 }
1323 let internal: BTreeSet<String> = self
1324 .db
1325 .prepare(format!("WITH i(workspace) AS ({}) SELECT DISTINCT workspace FROM i", crate::sales::INTERNAL_SQL))
1326 .all()
1327 .await?
1328 .results::<Internal>()?
1329 .into_iter()
1330 .map(|i| i.workspace)
1331 .collect();
1332 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 daily1333 let now = rfc3339(now_ms());
1334 self.db
1335 .batch(vec![
1336 self.db.prepare("DELETE FROM margin_days WHERE day >= ?1 AND day <= ?2").bind(&[since.into(), until.into()])?,
1337 self.db.prepare("DELETE FROM workspace_costs WHERE day >= ?1 AND day <= ?2").bind(&[since.into(), until.into()])?,
1338 ])
1339 .await?;
1340 for chunk in days.chunks(50) {
1341 let mut statements = Vec::with_capacity(chunk.len());
1342 for d in chunk {
1343 statements.push(
1344 self.db
1345 .prepare(
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb971346 "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, computed_at)
1347 VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1348 )
1349 .bind(&[
1350 d.day.as_str().into(),
1351 d.bucket.as_str().into(),
1352 (d.cf_cost_micros as f64).into(),
1353 (d.own_cost_micros as f64).into(),
1354 (d.value_micros as f64).into(),
1355 (d.cash_micros as f64).into(),
1356 d.cf_quantity.into(),
1357 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 running1358 (d.given.total() as f64).into(),
1359 (d.given.comped as f64).into(),
1360 (d.given.free as f64).into(),
1361 (d.given.trial as f64).into(),
1362 (d.given.pool as f64).into(),
Merge branch 'worktree-agent-a633ac0f7f66d419d'1363 (d.given.discount as f64).into(),
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging1364 (d.given.credit_promotional as f64).into(),
1365 (d.given.credit_goodwill as f64).into(),
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb971366 (d.given.reset as f64).into(),
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1367 now.as_str().into(),
1368 ])?,
1369 );
1370 }
1371 self.db.batch(statements).await?;
1372 }
1373 for chunk in workspaces.chunks(50) {
1374 let mut statements = Vec::with_capacity(chunk.len());
1375 for w in chunk {
1376 statements.push(
1377 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 it1378 .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 daily1379 .bind(&[
1380 w.day.as_str().into(),
1381 w.workspace.as_str().into(),
1382 w.bucket.as_str().into(),
1383 (w.cost as f64).into(),
1384 (w.revenue as f64).into(),
Margin alerts measure what is sold, and say dollars when a percentage would mislead1385 (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 running1386 (w.given.total() as f64).into(),
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1387 ])?,
1388 );
1389 }
1390 self.db.batch(statements).await?;
1391 }
1392 Ok(costs::days_between(since, until).len() as u32)
1393 }
1394
1395 async fn margin_days(&self, since: &str, until: &str) -> Result<Vec<ProductDay>> {
1396 Ok(self
1397 .db
1398 .prepare("SELECT * FROM margin_days WHERE day >= ?1 AND day <= ?2 ORDER BY day, bucket")
1399 .bind(&[since.into(), until.into()])?
1400 .all()
1401 .await?
1402 .results::<MarginRow>()?
1403 .into_iter()
1404 .map(ProductDay::from)
1405 .collect())
1406 }
1407
Merge branch 'worktree-agent-a633ac0f7f66d419d'1408 /// What AI Gateway's lines over the days, and the runs settled in them,
1409 /// say about whether its cost is what the providers bill.
1410 async fn gateway_caveats(&self, since: &str, until: &str) -> Result<costs::GatewayCaveats> {
1411 #[derive(Deserialize)]
1412 struct Line {
1413 meter: String,
1414 quantity: f64,
1415 cost_usd: f64,
1416 }
1417 let lines: Vec<(String, f64, f64)> = self
1418 .db
1419 .prepare("SELECT meter, quantity, cost_usd FROM cost_lines WHERE source = ?1 AND day >= ?2 AND day <= ?3")
1420 .bind(&[costs::SOURCE_GATEWAY.into(), since.into(), until.into()])?
1421 .all()
1422 .await?
1423 .results::<Line>()?
1424 .into_iter()
1425 .map(|l| (l.meter, l.quantity, l.cost_usd))
1426 .collect();
1427 let mut caveats = costs::gateway_caveats(&lines);
1428 #[derive(Deserialize)]
1429 struct Short {
1430 n: Option<f64>,
1431 }
1432 caveats.short_runs = self
1433 .db
1434 .prepare("SELECT COUNT(*) AS n FROM runs WHERE gateway_note IS NOT NULL AND settled_at >= ?1 AND settled_at <= ?2")
1435 .bind(&[since.into(), format!("{until}T23:59:59.999Z").into()])?
1436 .first::<Short>(None)
1437 .await?
1438 .and_then(|s| s.n)
1439 .unwrap_or(0.0) as u32;
1440 Ok(caveats)
1441 }
1442
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1443 /// Drift over the last week, written to `cost_drift` (replacing the
1444 /// last run's), with unmapped Cloudflare meters as leaks.
1445 async fn find_drift(&self, until: &str) -> Result<Vec<(Drift, String)>> {
1446 let since = day_before(until, DRIFT_DAYS - 1);
1447 let settings = self.cost_settings().await?;
1448 let rules = self.rules().await?;
1449 let days = self.margin_days(&since, until).await?;
1450 let mut by: BTreeMap<String, Vec<ProductDay>> = BTreeMap::new();
1451 for d in days {
1452 by.entry(d.bucket.clone()).or_default().push(d);
1453 }
Merge branch 'worktree-agent-a633ac0f7f66d419d'1454 let caveats = self.gateway_caveats(&since, until).await?;
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb971455 let resets = self.resets_since(&since, until).await?;
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1456 let mut found = Vec::new();
Merge branch 'worktree-agent-a633ac0f7f66d419d'1457 if let Some(drift) = unpriced_drift(&caveats) {
1458 found.push(drift);
1459 }
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1460 for (bucket, days) in &by {
1461 let bucket_rules: Vec<&Rule> = rules.iter().filter(|r| &r.bucket == bucket).collect();
1462 let threshold = bucket_rules.iter().map(|r| r.drift_percent).fold(f64::INFINITY, f64::min);
1463 let threshold = if threshold.is_finite() { threshold } else { 10.0 };
1464 let counted = bucket_rules.iter().any(|r| r.own_meter.is_some());
1465 for drift in drifts(bucket, days, threshold, counted, settings.min_daily_cost_micros) {
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb971466 if wiped_not_leaked(&drift, &resets) {
1467 continue;
1468 }
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1469 let title = costs::bucket_title(bucket);
1470 let detail = match drift.kind {
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb971471 DriftKind::Cost if NOT_CLOUDFLARE.contains(&bucket.as_str()) => models_detail(&drift, &caveats, &resets),
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1472 DriftKind::Count => format!(
One operation mapping, owned by repos; billing reads it instead of keeping its own1473 "{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 daily1474 crate::features::thousands(drift.ours.max(0.0).round() as u64),
1475 crate::features::thousands(drift.cloudflare.max(0.0).round() as u64),
1476 drift.delta_percent.unwrap_or(0.0)
1477 ),
1478 DriftKind::Cost => format!(
1479 "{title}: Cloudflare charged {} over the last {DRIFT_DAYS} days; the price book's cost of the same usage is {} ({:+.1}%). A price may be stale: see the proposals.",
1480 dollars(drift.cloudflare as i64),
1481 dollars(drift.ours as i64),
1482 drift.delta_percent.unwrap_or(0.0)
1483 ),
1484 DriftKind::Leak if bucket == UNMAPPED => {
1485 format!("Cloudflare charged {} for meters no mapping claims. Map them on Costs & margin.", dollars(drift.cloudflare as i64))
1486 }
Merge branch 'worktree-agent-a633ac0f7f66d419d'1487 DriftKind::Leak if NOT_CLOUDFLARE.contains(&bucket.as_str()) => format!(
1488 "{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).",
1489 dollars(drift.cloudflare as i64)
1490 ),
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1491 DriftKind::Leak => format!(
1492 "{title}: Cloudflare charged {} over the last {DRIFT_DAYS} days and customers were charged nothing for it.",
1493 dollars(drift.cloudflare as i64)
1494 ),
Merge branch 'worktree-agent-a633ac0f7f66d419d'1495 // Raised from the gateway's lines, not per bucket.
1496 DriftKind::Unpriced => unpriced_detail(&caveats),
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1497 };
1498 found.push((drift, detail));
1499 }
1500 }
1501 let now = rfc3339(now_ms());
1502 let mut statements = vec![self.db.prepare("DELETE FROM cost_drift")];
1503 for (drift, detail) in &found {
1504 statements.push(
1505 self.db
1506 .prepare("INSERT OR REPLACE INTO cost_drift (bucket, kind, ours, cloudflare, delta_percent, detail, found_at) VALUES (?, ?, ?, ?, ?, ?, ?)")
1507 .bind(&[
1508 drift.bucket.as_str().into(),
1509 drift.kind.as_str().into(),
1510 drift.ours.into(),
1511 drift.cloudflare.into(),
1512 drift.delta_percent.map_or(JsValue::NULL, JsValue::from),
1513 detail.as_str().into(),
1514 now.as_str().into(),
1515 ])?,
1516 );
1517 }
1518 self.db.batch(statements).await?;
1519 Ok(found)
1520 }
1521
1522 /// Unit costs from the bill for mappings that scale to g1t's own count
1523 /// (git operations), proposed to the price book.
1524 async fn measure_units(&self, until: &str) -> Result<u32> {
1525 #[derive(Deserialize)]
1526 struct Scaled {
1527 product: String,
1528 meter: String,
1529 price_meter: String,
1530 own_meter: String,
1531 unit: Option<String>,
1532 }
1533 let scaled = self
1534 .db
1535 .prepare(
1536 "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
1537 WHERE m.scale_to_own = 1 AND m.price_meter IS NOT NULL AND m.own_meter IS NOT NULL",
1538 )
1539 .all()
1540 .await?
1541 .results::<Scaled>()?;
1542 let since = day_before(until, MEASURE_DAYS - 1);
1543 let rules = self.rules().await?;
1544 let mut proposed = 0;
1545 for s in scaled {
1546 #[derive(Deserialize)]
1547 struct Day {
1548 product: String,
1549 meter: String,
1550 quantity: f64,
1551 cost_usd: f64,
1552 }
1553 let lines = self
1554 .db
1555 .prepare("SELECT product, meter, quantity, cost_usd FROM cost_lines WHERE source = ?1 AND product = ?2 AND day >= ?3 AND day <= ?4")
1556 .bind(&[SOURCE_BILLABLE.into(), s.product.as_str().into(), since.as_str().into(), until.into()])?
1557 .all()
1558 .await?
1559 .results::<Day>()?;
1560 // Only the lines this very mapping claims.
1561 let mine: Vec<(f64, f64)> = lines
1562 .iter()
1563 .filter(|l| costs::classify(&rules, &l.product, &l.meter).is_some_and(|r| r.product == s.product && r.meter == s.meter))
1564 .map(|l| (l.quantity, l.cost_usd))
1565 .collect();
1566 let Some(rate) = billed_rate(&mine) else { continue };
1567 let cf_units: f64 = mine.iter().map(|(q, _)| q).sum();
1568 #[derive(Deserialize)]
1569 struct Own {
1570 total: Option<f64>,
1571 }
1572 let own_units = self
1573 .db
1574 .prepare("SELECT SUM(quantity) AS total FROM own_counts WHERE meter = ?1 AND day >= ?2 AND day <= ?3")
1575 .bind(&[s.own_meter.as_str().into(), since.as_str().into(), until.into()])?
1576 .first::<Own>(None)
1577 .await?
1578 .and_then(|o| o.total)
1579 .unwrap_or(0.0);
1580 let Some(per_unit) = derived_unit_cost(rate, cf_units, own_units) else { continue };
1581 let size = unit_size(s.unit.as_deref().unwrap_or("1"));
1582 let measured = per_unit * size * 1_000_000.0;
1583 let reason = format!(
1584 "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 {})",
1585 rate * 1000.0,
1586 cf_units / own_units,
1587 crate::features::thousands(cf_units.round() as u64),
1588 crate::features::thousands(own_units.round() as u64)
1589 );
1590 if self.propose(&s.price_meter, measured, &reason, "reconciler").await?.is_some() {
1591 proposed += 1;
1592 }
1593 }
1594 Ok(proposed)
1595 }
1596
1597 /// Opens, updates and closes margin alerts, and emails staff about new
1598 /// ones (and open ones each week).
1599 async fn raise_alerts(&self, env: &Env, until: &str, drift: &[(Drift, String)]) -> Result<u32> {
1600 let settings = self.cost_settings().await?;
1601 let since = day_before(until, u64::from(settings.alert_days.max(1)) - 1);
1602 let days = self.margin_days(&since, until).await?;
1603 let mut conditions: Vec<(String, String, String, String)> = Vec::new();
1604 // Each product under the floor.
1605 let mut by: BTreeMap<String, Vec<(String, i64, i64)>> = BTreeMap::new();
1606 let mut all: BTreeMap<String, (i64, i64)> = BTreeMap::new();
1607 for d in &days {
1608 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 it1609 // What g1t gave away (comped workspaces, free periods, the
1610 // trial and the pools) is a budget it chose to spend, watched on
1611 // 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 daily1612 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 running1613 overall.1 += (d.cost() - d.given.total()).max(0);
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1614 if !OVERHEAD.contains(&d.bucket.as_str()) && d.bucket != UNMAPPED {
1615 by.entry(d.bucket.clone()).or_default().push((d.day.clone(), d.value_micros, d.cost()));
1616 }
1617 }
1618 let floor = settings.margin_floor_percent;
1619 let n = settings.alert_days as usize;
1620 for (bucket, series) in &by {
1621 if let Some((from, worst)) = breach(series, floor, n, settings.min_daily_cost_micros) {
1622 conditions.push((
1623 "margin".into(),
1624 bucket.clone(),
1625 format!("{}: margin under {floor:.0}% for {n} days running, as low as {worst:.1}%.", costs::bucket_title(bucket)),
1626 from,
1627 ));
Margin alerts measure what is sold, and say dollars when a percentage would mislead1628 }
1629 }
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1630 let series: Vec<(String, i64, i64)> = all.into_iter().map(|(day, (revenue, cost))| (day, revenue, cost)).collect();
1631 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 mislead1632 let tail = &series[series.len().saturating_sub(n)..];
1633 let (took, spent) = tail.iter().fold((0i64, 0i64), |(r, c), (_, revenue, cost)| (r + revenue, c + cost));
1634 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 daily1635 }
1636 for (d, detail) in drift {
1637 let kind = if d.kind == DriftKind::Leak { "leak" } else { "drift" };
1638 conditions.push((kind.into(), format!("{}:{}", d.bucket, d.kind.as_str()), detail.clone(), until.to_owned()));
1639 }
1640 // Workspaces costing more than they pay.
1641 for (workspace, cost, revenue) in self.workspace_anomalies(until, &settings).await? {
1642 conditions.push((
1643 "workspace".into(),
1644 workspace.clone(),
Margin alerts measure what is sold, and say dollars when a percentage would mislead1645 format!(
1646 "{workspace} cost g1t {} on Cloudflare over {ANOMALY_DAYS} days, and its usage was priced at {}: its prices are below cost.",
1647 dollars(cost),
1648 dollars(revenue)
1649 ),
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1650 day_before(until, ANOMALY_DAYS - 1),
1651 ));
1652 }
1653
1654 let open = self
1655 .db
1656 .prepare("SELECT * FROM margin_alerts WHERE resolved_at IS NULL")
1657 .all()
1658 .await?
1659 .results::<AlertRow>()?;
1660 let now = now_ms();
1661 let stamp = rfc3339(now);
1662 let mut to_email: Vec<String> = Vec::new();
1663 let mut kept: BTreeSet<String> = BTreeSet::new();
1664 for (kind, subject, detail, from) in &conditions {
1665 match open.iter().find(|a| &a.kind == kind && &a.subject == subject) {
1666 Some(alert) => {
1667 kept.insert(alert.id.clone());
1668 self.db
1669 .prepare("UPDATE margin_alerts SET detail = ? WHERE id = ?")
1670 .bind(&[detail.as_str().into(), alert.id.as_str().into()])?
1671 .run()
1672 .await?;
1673 let stale = alert
1674 .emailed_at
1675 .as_deref()
1676 .and_then(g1t_contracts::time::parse_rfc3339)
1677 .is_none_or(|at| now.saturating_sub(at) >= REMIND_MS);
1678 if stale && kind != "workspace" {
1679 to_email.push(format!("Still open: {detail}"));
1680 kept.insert(format!("email:{}", alert.id));
1681 }
1682 }
1683 None => {
1684 let id = new_id("mal", now);
1685 self.db
1686 .prepare("INSERT INTO margin_alerts (id, kind, subject, detail, since, opened_at) VALUES (?, ?, ?, ?, ?, ?)")
1687 .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()])?
1688 .run()
1689 .await?;
1690 kept.insert(id.clone());
Margin alerts measure what is sold, and say dollars when a percentage would mislead1691 // A workspace's is for Reach out, not the inbox.
1692 if kind != "workspace" {
1693 to_email.push(detail.clone());
1694 }
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1695 kept.insert(format!("email:{id}"));
1696 }
1697 }
1698 }
1699 for alert in &open {
1700 if !kept.contains(&alert.id) {
1701 self.db
1702 .prepare("UPDATE margin_alerts SET resolved_at = ? WHERE id = ?")
1703 .bind(&[stamp.as_str().into(), alert.id.as_str().into()])?
1704 .run()
1705 .await?;
1706 }
1707 }
1708 let to = env.var("COSTS_ALERT_EMAIL").map(|v| v.to_string()).unwrap_or_default();
1709 if !to_email.is_empty() && !to.trim().is_empty() {
1710 let subject = format!("[g1t costs] {} margin alert{}", to_email.len(), if to_email.len() == 1 { "" } else { "s" });
1711 match email_staff(env, to.trim(), &subject, &to_email).await {
1712 Ok(()) => {
1713 for marker in kept.iter().filter_map(|k| k.strip_prefix("email:")) {
1714 self.db
1715 .prepare("UPDATE margin_alerts SET emailed_at = ? WHERE id = ?")
1716 .bind(&[stamp.as_str().into(), marker.into()])?
1717 .run()
1718 .await?;
1719 }
1720 }
1721 Err(error) => worker::console_error!("could not email the margin alerts: {error}"),
1722 }
1723 }
1724 Ok(conditions.len() as u32)
1725 }
1726
1727 /// 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 mislead1728 /// Each day's cost shared out to comped workspaces.
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1729 async fn workspace_anomalies(&self, until: &str, settings: &CostSettings) -> Result<Vec<(String, i64, i64)>> {
1730 #[derive(Deserialize)]
1731 struct Row {
1732 workspace: String,
1733 cost: Option<i64>,
1734 revenue: Option<i64>,
1735 }
1736 let rows = self
1737 .db
1738 .prepare(format!(
Margin alerts measure what is sold, and say dollars when a percentage would mislead1739 // Against what its usage was priced at, not the cash it
1740 // paid: a trial or a gift paying for usage is not a price
1741 // below cost.
The workspace cost alert compares only days that carry their value, not the days before it was kept1742 // Days from before value_micros was kept have none: only days
1743 // since the first one that does are compared.
Margin alerts measure what is sold, and say dollars when a percentage would mislead1744 "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 kept1745 WHERE day >= ?1 AND day <= ?2 AND workspace NOT IN ({})
1746 AND day >= (SELECT MIN(day) FROM workspace_costs WHERE value_micros > 0)
1747 GROUP BY workspace",
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1748 crate::sales::INTERNAL_SQL
1749 ))
1750 .bind(&[day_before(until, ANOMALY_DAYS - 1).into(), until.into()])?
1751 .all()
1752 .await?
1753 .results::<Row>()?;
1754 let rows: Vec<(String, i64, i64)> = rows.into_iter().map(|r| (r.workspace, r.cost.unwrap_or(0), r.revenue.unwrap_or(0))).collect();
1755 Ok(anomalies(&rows, settings.anomaly_factor, settings.anomaly_floor_micros))
1756 }
1757
1758 /// For Reach out: workspaces with an open cost-over-revenue alert,
1759 /// each with its detail and cost.
1760 pub(crate) async fn costing_more_than_they_pay(&self) -> Result<Vec<(String, String, i64)>> {
1761 let alerts = self
1762 .db
1763 .prepare("SELECT * FROM margin_alerts WHERE resolved_at IS NULL AND kind = 'workspace' ORDER BY opened_at DESC LIMIT 50")
1764 .all()
1765 .await?
1766 .results::<AlertRow>()?;
1767 let mut out = Vec::new();
1768 for alert in alerts {
1769 #[derive(Deserialize)]
1770 struct Cost {
1771 cost: Option<i64>,
1772 }
1773 let cost = self
1774 .db
1775 .prepare("SELECT SUM(cost_micros) AS cost FROM workspace_costs WHERE workspace = ? AND day >= ?")
1776 .bind(&[alert.subject.as_str().into(), alert.since.as_str().into()])?
1777 .first::<Cost>(None)
1778 .await?
1779 .and_then(|c| c.cost)
1780 .unwrap_or(0);
1781 out.push((alert.subject, alert.detail, cost));
1782 }
1783 Ok(out)
1784 }
1785
1786 /// `admin_cost_alerts`: what sudo's banner says.
1787 pub(crate) async fn admin_cost_alerts(&self, _: AdminCostAlertsArgs) -> Result<Vec<MarginAlert>> {
1788 Ok(self
1789 .db
1790 .prepare("SELECT * FROM margin_alerts WHERE resolved_at IS NULL ORDER BY opened_at DESC LIMIT 50")
1791 .all()
1792 .await?
1793 .results::<AlertRow>()?
1794 .into_iter()
1795 .map(MarginAlert::from)
1796 .collect())
1797 }
1798
1799 /// `admin_run_costs`: the daily run, now.
1800 pub(crate) async fn admin_run_costs(&self, env: &Env, a: AdminRunCostsArgs) -> Result<Outcome<CostsRun>> {
1801 let keeper = crate::keeper::Keeper::from_env(env);
1802 let run = self.costs_daily(env, &keeper).await?;
1803 if !a.by.is_empty() {
1804 self.audit(
1805 "costs",
1806 "costs_run",
1807 &format!("{} lines, {} days, {} proposals, {} alerts", run.lines, run.days, run.proposals, run.alerts),
1808 &a.by,
1809 )
1810 .await?;
1811 }
1812 Ok(Outcome::Ok(run))
1813 }
1814
1815 /// `admin_set_cost_mapping`.
1816 pub(crate) async fn admin_set_cost_mapping(&self, a: AdminSetCostMappingArgs) -> Result<Outcome<CostMapping>> {
1817 let product = costs::slug(&a.product);
1818 let meter = if a.meter.trim() == "*" { "*".to_owned() } else { costs::slug(&a.meter) };
1819 if product.is_empty() || meter.is_empty() {
1820 return Ok(Outcome::fail(FailureCode::Invalid, "Name Cloudflare's product and a meter (or * for all of it)."));
1821 }
1822 let now = rfc3339(now_ms());
1823 if a.remove {
1824 self.db
1825 .prepare("DELETE FROM cost_map WHERE product = ? AND meter = ?")
1826 .bind(&[product.as_str().into(), meter.as_str().into()])?
1827 .run()
1828 .await?;
1829 self.audit("costs", "cost_mapping_removed", &format!("{product}/{meter}"), &a.by).await?;
1830 return Ok(Outcome::Ok(CostMapping {
1831 product,
1832 meter,
1833 bucket: String::new(),
1834 price_meter: None,
1835 own_meter: None,
1836 scale_to_own: false,
1837 drift_percent: 0.0,
1838 note: String::new(),
1839 updated_at: now,
1840 updated_by: a.by,
1841 }));
1842 }
1843 let bucket = costs::slug(&a.bucket);
1844 if bucket.is_empty() {
1845 return Ok(Outcome::fail(FailureCode::Invalid, "Say which of g1t's products it is a cost of."));
1846 }
1847 let clean = |v: Option<String>| v.map(|v| v.trim().to_owned()).filter(|v| !v.is_empty());
1848 let (price_meter, own_meter) = (clean(a.price_meter), clean(a.own_meter));
1849 let drift = a.drift_percent.filter(|d| d.is_finite() && *d > 0.0).unwrap_or(10.0);
1850 self.db
1851 .prepare(
1852 "INSERT INTO cost_map (product, meter, bucket, price_meter, own_meter, scale_to_own, drift_percent, note, updated_at, updated_by)
1853 VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10)
1854 ON CONFLICT (product, meter) DO UPDATE SET bucket = ?3, price_meter = ?4, own_meter = ?5, scale_to_own = ?6,
1855 drift_percent = ?7, note = ?8, updated_at = ?9, updated_by = ?10",
1856 )
1857 .bind(&[
1858 product.as_str().into(),
1859 meter.as_str().into(),
1860 bucket.as_str().into(),
1861 crate::optional(price_meter.as_deref()),
1862 crate::optional(own_meter.as_deref()),
1863 i32::from(a.scale_to_own).into(),
1864 drift.into(),
1865 a.note.trim().into(),
1866 now.as_str().into(),
1867 a.by.as_str().into(),
1868 ])?
1869 .run()
1870 .await?;
1871 self.audit("costs", "cost_mapping", &format!("{product}/{meter} → {bucket}"), &a.by).await?;
1872 Ok(Outcome::Ok(CostMapping {
1873 product,
1874 meter,
1875 bucket,
1876 price_meter,
1877 own_meter,
1878 scale_to_own: a.scale_to_own,
1879 drift_percent: drift,
1880 note: a.note.trim().to_owned(),
1881 updated_at: now,
1882 updated_by: a.by,
1883 }))
1884 }
1885
1886 /// `admin_costs`: the Costs & margin page.
1887 pub(crate) async fn admin_costs(&self, a: AdminCostsArgs, configured: bool) -> Result<CostsReport> {
1888 let until = rfc3339(now_ms())[..10].to_owned();
1889 let span = u64::from(a.days.unwrap_or(30).clamp(7, 90));
1890 let since = day_before(&until, span - 1);
1891 let days = self.margin_days(&since, &until).await?;
1892 let rules = self.rules().await?;
1893
1894 let mut products: BTreeMap<String, ProductMargin> = BTreeMap::new();
1895 let mut overall = OverallMargin::default();
1896 for d in &days {
1897 let p = products.entry(d.bucket.clone()).or_insert_with(|| ProductMargin {
1898 bucket: d.bucket.clone(),
1899 title: costs::bucket_title(&d.bucket),
1900 cost_source: if NOT_CLOUDFLARE.contains(&d.bucket.as_str()) { "ledger" } else { "cloudflare" }.into(),
1901 overhead: OVERHEAD.contains(&d.bucket.as_str()),
1902 ..ProductMargin::default()
1903 });
1904 p.cf_cost_micros += d.cf_cost_micros;
1905 p.own_cost_micros += d.own_cost_micros;
1906 p.value_micros += d.value_micros;
1907 p.cost_micros += d.cost();
1908 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 running1909 overall.given_micros += d.given.total();
1910 if NOT_CLOUDFLARE.contains(&d.bucket.as_str()) {
1911 overall.models_cost_micros += d.cost();
1912 } else {
1913 overall.cloudflare_cost_micros += d.cost();
1914 }
1915 overall.given_comped_micros += d.given.comped;
1916 overall.given_free_micros += d.given.free;
1917 overall.given_trial_micros += d.given.trial;
1918 overall.given_pool_micros += d.given.pool;
Merge branch 'worktree-agent-a633ac0f7f66d419d'1919 overall.given_discount_micros += d.given.discount;
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging1920 overall.given_credit_promotional_micros += d.given.credit_promotional;
1921 overall.given_credit_goodwill_micros += d.given.credit_goodwill;
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb971922 overall.given_reset_micros += d.given.reset;
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 running1923 let sold = (d.cost() - d.given.total()).max(0);
1924 if OVERHEAD.contains(&d.bucket.as_str()) {
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1925 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 running1926 overall.running_cost_micros += sold;
1927 } else if d.bucket == UNMAPPED {
1928 overall.usage_micros += d.cash_micros;
1929 overall.unmapped_cost_micros += sold;
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1930 } else {
1931 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 running1932 overall.usage_cost_micros += sold;
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1933 }
1934 }
1935 for p in products.values_mut() {
1936 p.margin_micros = p.value_micros - p.cost_micros;
1937 p.margin_percent = margin_percent(p.value_micros, p.cost_micros);
1938 }
1939 let revenue = overall.usage_micros + overall.plans_micros;
1940 overall.margin_micros = revenue - overall.cost_micros;
1941 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 it1942 let sold = (overall.cost_micros - overall.given_micros).max(0);
1943 overall.sold_margin_micros = revenue - sold;
1944 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)1945 // The plan's included usage was paid for by the plan's price: it is
1946 // money in for the usage it covered, and out of what the plans
1947 // leave for running g1t.
1948 #[derive(Deserialize)]
1949 struct Included {
1950 micros: Option<i64>,
1951 }
1952 overall.included_micros = self
1953 .db
1954 .prepare(format!(
1955 "SELECT SUM(COALESCE(credit_micros, 0)) AS micros FROM ledger
1956 WHERE kind = 'usage' AND created_at >= ?1 AND created_at <= ?2 AND workspace NOT IN ({})",
1957 crate::sales::INTERNAL_SQL
1958 ))
1959 .bind(&[since.as_str().into(), format!("{until}T23:59:59.999Z").into()])?
1960 .first::<Included>(None)
1961 .await?
1962 .and_then(|r| r.micros)
1963 .unwrap_or(0);
1964 let usage_in = overall.usage_micros + overall.included_micros;
1965 overall.usage_margin_micros = usage_in - overall.usage_cost_micros;
1966 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 charging1967 // Credits from g1t over the range: given, spent, and refunds' money
1968 // given back.
1969 overall.credits_given_micros = self
1970 .db
1971 .prepare("SELECT SUM(amount_micros) AS micros FROM credit_grants WHERE created_at >= ?1 AND created_at <= ?2")
1972 .bind(&[since.as_str().into(), format!("{until}T23:59:59.999Z").into()])?
1973 .first::<Included>(None)
1974 .await?
1975 .and_then(|r| r.micros)
1976 .unwrap_or(0);
1977 let (draws, refunds) = self.credit_effects(&since, &until).await?;
1978 overall.credits_used_micros = draws.iter().map(|(_, d)| d.micros).sum();
1979 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 person1980 // Tax and card fees came in with payments but are neither cash nor
1981 // revenue: balances and plan payments are credited without them
1982 // (tax.rs), so cash above never holds them. Shown apart.
1983 (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 daily1984 let mut products: Vec<ProductMargin> = products.into_values().collect();
1985 products.sort_by_key(|p| std::cmp::Reverse(p.cost_micros.max(p.value_micros)));
1986
1987 #[derive(Deserialize)]
1988 struct DriftRow {
1989 bucket: String,
1990 kind: String,
1991 ours: f64,
1992 cloudflare: f64,
1993 delta_percent: Option<f64>,
1994 detail: String,
1995 found_at: String,
1996 }
1997 let drift = self
1998 .db
1999 .prepare("SELECT * FROM cost_drift ORDER BY kind, bucket")
2000 .all()
2001 .await?
2002 .results::<DriftRow>()?
2003 .into_iter()
2004 .map(|r| CostDrift {
2005 title: costs::bucket_title(&r.bucket),
2006 bucket: r.bucket,
2007 kind: r.kind,
2008 ours: r.ours,
2009 cloudflare: r.cloudflare,
2010 delta_percent: r.delta_percent,
2011 detail: r.detail,
2012 found_at: r.found_at,
2013 })
2014 .collect();
2015
2016 #[derive(Deserialize)]
2017 struct Top {
2018 workspace: String,
2019 cost: Option<i64>,
2020 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 it2021 given: Option<i64>,
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily2022 internal: i64,
2023 }
2024 let top_workspaces = self
2025 .db
2026 .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 it2027 "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 daily2028 CASE WHEN workspace IN ({}) THEN 1 ELSE 0 END AS internal
2029 FROM workspace_costs WHERE day >= ?1 AND day <= ?2 GROUP BY workspace ORDER BY cost DESC LIMIT 15",
2030 crate::sales::INTERNAL_SQL
2031 ))
2032 .bind(&[since.as_str().into(), until.as_str().into()])?
2033 .all()
2034 .await?
2035 .results::<Top>()?
2036 .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 it2037 .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 daily2038 .collect();
2039
2040 #[derive(Deserialize)]
2041 struct Summary {
2042 source: String,
2043 product: String,
2044 meter: String,
2045 raw_name: String,
2046 unit: String,
2047 quantity: f64,
2048 cost_usd: f64,
2049 }
2050 let lines = self
2051 .db
2052 .prepare(
2053 "SELECT source, product, meter, MAX(raw_name) AS raw_name, MAX(unit) AS unit, SUM(quantity) AS quantity, SUM(cost_usd) AS cost_usd
2054 FROM cost_lines WHERE day >= ?1 AND day <= ?2 GROUP BY source, product, meter ORDER BY cost_usd DESC, product, meter LIMIT 200",
2055 )
2056 .bind(&[since.as_str().into(), until.as_str().into()])?
2057 .all()
2058 .await?
2059 .results::<Summary>()?
2060 .into_iter()
2061 .map(|l| CostLineSummary {
2062 bucket: costs::classify(&rules, &l.product, &l.meter).map(|r| r.bucket.clone()),
2063 product: l.product,
2064 meter: l.meter,
2065 raw_name: l.raw_name,
2066 unit: l.unit,
2067 source: l.source,
2068 quantity: l.quantity,
2069 cost_micros: micros(l.cost_usd),
2070 })
2071 .collect();
2072
2073 #[derive(Deserialize)]
2074 struct MapRow {
2075 product: String,
2076 meter: String,
2077 bucket: String,
2078 price_meter: Option<String>,
2079 own_meter: Option<String>,
2080 scale_to_own: i64,
2081 drift_percent: f64,
2082 note: String,
2083 updated_at: String,
2084 updated_by: String,
2085 }
2086 let mappings = self
2087 .db
2088 .prepare("SELECT * FROM cost_map ORDER BY product, meter")
2089 .all()
2090 .await?
2091 .results::<MapRow>()?
2092 .into_iter()
2093 .map(|m| CostMapping {
2094 product: m.product,
2095 meter: m.meter,
2096 bucket: m.bucket,
2097 price_meter: m.price_meter,
2098 own_meter: m.own_meter,
2099 scale_to_own: m.scale_to_own == 1,
2100 drift_percent: m.drift_percent,
2101 note: m.note,
2102 updated_at: m.updated_at,
2103 updated_by: m.updated_by,
2104 })
2105 .collect();
2106
2107 #[derive(Deserialize)]
2108 struct Fetched {
2109 at: Option<String>,
2110 }
2111 let fetched_at = self.db.prepare("SELECT MAX(fetched_at) AS at FROM cost_lines").first::<Fetched>(None).await?.and_then(|f| f.at);
2112
2113 Ok(CostsReport {
2114 configured,
2115 fetched_at,
2116 days: days
2117 .iter()
2118 .map(|d| CostDay {
2119 day: d.day.clone(),
2120 bucket: d.bucket.clone(),
2121 cf_cost_micros: d.cf_cost_micros,
2122 own_cost_micros: d.own_cost_micros,
2123 value_micros: d.value_micros,
2124 cash_micros: d.cash_micros,
2125 })
2126 .collect(),
2127 since,
2128 until,
2129 products,
2130 overall,
2131 drift,
2132 alerts: self.admin_cost_alerts(AdminCostAlertsArgs {}).await?,
2133 proposals: self.proposals().await?,
2134 versions: self.versions().await?,
2135 top_workspaces,
2136 lines,
2137 mappings,
2138 settings: self.cost_settings().await?,
Spend caps: a monthly budget for comped workspaces and a daily breaker on what g1t pays2139 caps: self.spend_caps().await?,
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily2140 })
2141 }
2142}
2143
2144#[cfg(test)]
2145mod tests {
2146 use super::*;
2147
Margin alerts measure what is sold, and say dollars when a percentage would mislead2148 #[test]
Models' margin read -14%: usage nothing paid for is valued at price, not $02149 fn usage_nothing_paid_for_is_valued_at_price_and_paid_usage_at_what_was_paid() {
2150 // A free period: charged nothing, drawn from nothing.
2151 assert_eq!(usage_value(false, 1_000_000, 0, 20), 1_200_000);
2152 // Charged, or drawn from a trial: what was paid.
2153 assert_eq!(usage_value(false, 1_000_000, 1_200_000, 20), 1_200_000);
2154 assert_eq!(usage_value(false, 1_000_000, 900_000, 20), 900_000);
2155 // g1t's own: at price.
2156 assert_eq!(usage_value(true, 1_000_000, 0, 20), 1_200_000);
2157 // No cost, nothing paid: nothing.
2158 assert_eq!(usage_value(false, 0, 0, 20), 0);
2159 }
2160
2161 #[test]
Margin alerts measure what is sold, and say dollars when a percentage would mislead2162 fn the_overall_alert_says_dollars_while_little_comes_in() {
2163 let small = overall_detail(90_000, 7_500_000, 3, 10.0, -8239.7);
2164 assert!(small.contains("took in $0.09 against $7.50"), "{small}");
2165 assert!(!small.contains('%'), "{small}");
2166 let real = overall_detail(30_000_000, 40_000_000, 3, 10.0, -33.3);
2167 assert!(real.contains("as low as -33.3%"), "{real}");
2168 }
2169
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily2170 fn rule(product: &str, meter: &str, bucket: &str, own: Option<&str>) -> Rule {
2171 Rule { product: product.into(), meter: meter.into(), bucket: bucket.into(), price_meter: None, own_meter: own.map(Into::into), drift_percent: 10.0 }
2172 }
2173
2174 fn rules() -> Vec<Rule> {
2175 vec![
2176 rule("containers", "*", "sandboxes", None),
2177 rule("workers", "*", "platform", None),
2178 rule("artifacts", "*", "git", Some("git_operations")),
2179 rule("artifacts", "events_", "git", Some("git_operations")),
2180 ]
2181 }
2182
2183 fn revenue_map() -> BTreeMap<String, String> {
2184 [("sandbox", "sandboxes"), ("git", "git"), ("plan", "platform")].iter().map(|(k, v)| (k.to_string(), v.to_string())).collect()
2185 }
2186
2187 fn line(day: &str, source: &str, product: &str, meter: &str, quantity: f64, cost: f64) -> LineRow {
2188 LineRow { day: day.into(), source: source.into(), product: product.into(), meter: meter.into(), quantity, cost_usd: cost }
2189 }
2190
2191 fn usage(day: &str, workspace: &str, key: &str, value: i64, cash: i64, cost: i64) -> UsageRow {
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb972192 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 daily2193 }
2194
2195 #[test]
2196 fn a_day_puts_the_bill_g1ts_counts_and_charges_side_by_side() {
2197 let lines = vec![
2198 line("2026-10-15", SOURCE_BILLABLE, "containers", "container_memory", 1000.0, 2.00),
2199 line("2026-10-15", SOURCE_BILLABLE, "artifacts", "artifacts_operations", 30_000.0, 3.00),
2200 // Artifacts' own events: not used while the bill has a count.
2201 line("2026-10-15", SOURCE_ARTIFACTS, "artifacts", "events_pull", 29_000.0, 0.0),
2202 line("2026-10-15", SOURCE_BILLABLE, "workers", "workers_cpu_ms", 1.0, 0.50),
2203 line("2026-10-15", SOURCE_BILLABLE, "browser_rendering", "browser_hours", 2.0, 0.25),
2204 ];
2205 let own = vec![
2206 OwnRow { day: "2026-10-15".into(), meter: "git_operations".into(), workspace: "acme".into(), quantity: 7_500.0 },
2207 OwnRow { day: "2026-10-15".into(), meter: "git_operations".into(), workspace: "beta".into(), quantity: 2_500.0 },
2208 ];
2209 let usage = vec![
2210 usage("2026-10-15", "acme", "sandbox", 2_400_000, 1_000_000, 2_000_000),
2211 usage("2026-10-15", "beta", "sandbox", 1_200_000, 1_200_000, 1_000_000),
2212 usage("2026-10-15", "acme", "git", 600_000, 600_000, 500_000),
2213 usage("2026-10-15", "acme", "implement", 120_000, 120_000, 100_000),
2214 usage("2026-10-15", "beta", "plan", 20_000_000, 20_000_000, 0),
2215 ];
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 running2216 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 daily2217 let get = |bucket: &str| days.iter().find(|d| d.bucket == bucket).unwrap();
2218 let sandboxes = get("sandboxes");
2219 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));
2220 let git = get("git");
2221 assert_eq!(git.cf_cost_micros, 3_000_000);
2222 assert_eq!((git.cf_quantity, git.own_quantity), (30_000.0, 10_000.0));
2223 assert_eq!(get("platform").value_micros, 20_000_000);
2224 // Not mapped: a leak until someone maps it.
2225 assert_eq!(get(UNMAPPED).cf_cost_micros, 250_000);
2226 // Models: no Cloudflare line, their cost is g1t's own.
2227 assert_eq!(get("models").cost(), 100_000);
2228 // Git's cost shared by g1t's own counts (Cloudflare gave none per
2229 // workspace here): three quarters to acme.
2230 let share = |ws: &str, bucket: &str| workspaces.iter().find(|w| w.workspace == ws && w.bucket == bucket).map(|w| (w.cost, w.revenue));
2231 assert_eq!(share("acme", "git"), Some((2_250_000, 600_000)));
2232 assert_eq!(share("beta", "git"), Some((750_000, 0)));
2233 // Every bucket's cost is shared out exactly.
2234 for d in &days {
2235 let shared: i64 = workspaces.iter().filter(|w| w.bucket == d.bucket).map(|w| w.cost).sum();
2236 assert_eq!(shared, d.cost(), "{}", d.bucket);
2237 }
2238 }
2239
2240 #[test]
2241 fn artifacts_events_count_when_the_bill_does_not() {
2242 let lines = vec![
2243 line("2026-10-05", SOURCE_ARTIFACTS, "artifacts", "events_pull", 120.0, 0.0),
2244 line("2026-10-05", SOURCE_ARTIFACTS, "artifacts", "events_push", 30.0, 0.0),
2245 line("2026-10-05", SOURCE_ARTIFACTS, "artifacts", "events_ratelimited", 9.0, 0.0),
2246 ];
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 running2247 let (days, _) = fold(&rules(), &revenue_map(), &lines, &[], &[], &BTreeSet::new());
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily2248 assert_eq!(days[0].cf_quantity, 150.0);
2249 assert_eq!(days[0].cf_cost_micros, 0);
2250 }
2251
2252 #[test]
2253 fn month_end_meters_are_told_by_the_day_from_snapshots() {
2254 let snap = |day: &str, cost: i64, charge: i64| (day.to_string(), "acme".to_string(), "git".to_string(), cost, charge);
2255 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)]);
2256 assert_eq!(
2257 rows.iter().map(|r| (r.day.as_str(), r.cost, r.value)).collect::<Vec<_>>(),
2258 vec![("2026-10-30", 100, 120), ("2026-10-31", 150, 180), ("2026-11-01", 40, 48)]
2259 );
2260 }
2261
2262 #[test]
2263 fn a_plan_payment_is_spread_over_the_month_it_pays_for() {
2264 let days = spread("2026-10-01T00:00:00.000Z", 20_000_000, 30);
2265 assert_eq!(days.len(), 30);
2266 assert_eq!(days[0], ("2026-10-01".to_string(), 666_667));
2267 assert_eq!(days[29], ("2026-10-30".to_string(), 666_666));
2268 assert_eq!(days.iter().map(|d| d.1).sum::<i64>(), 20_000_000);
2269 assert!(spread("2026-10-01", 0, 30).is_empty());
2270 assert_eq!(dollars(17_024_000), "$17.02");
2271 assert_eq!(dollars(-27_668_620), "-$27.67");
2272 assert_eq!(dollars(63_000), "$0.063");
2273 }
2274
2275 #[test]
2276 fn margins_and_deltas() {
2277 assert_eq!(margin_percent(1_200_000, 1_000_000).map(|m| (m * 100.0).round() / 100.0), Some(16.67));
2278 assert_eq!(margin_percent(0, 5), None);
2279 assert_eq!(delta_percent(110.0, 100.0), Some(10.0));
2280 assert_eq!(delta_percent(1.0, 0.0), None);
2281 }
2282
2283 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 running2284 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 daily2285 }
2286
2287 #[test]
2288 fn counts_more_than_the_threshold_apart_are_drift() {
2289 // Cloudflare counted 30,000 operations where g1t counted 10,000:
2290 // binding reads, perhaps. -66.7%.
2291 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);
2292 assert_eq!(drift.iter().map(|d| d.kind).collect::<Vec<_>>(), vec![DriftKind::Count, DriftKind::Cost]);
2293 assert!((drift[0].delta_percent.unwrap() + 66.666).abs() < 0.01);
2294 // 9% apart: within 10%.
2295 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).is_empty());
2296 // Uncounted products have no count drift.
2297 assert!(drifts("sandboxes", &[day("sandboxes", 1_000_000, 1_050_000, 1_200_000, 5.0, 0.0)], 10.0, false, 100_000).is_empty());
2298 }
2299
2300 #[test]
2301 fn cost_with_no_revenue_is_a_leak_but_not_for_running_g1t() {
2302 let leak = drifts("actions_cache", &[day("actions_cache", 400_000, 0, 0, 0.0, 0.0)], 10.0, false, 100_000);
2303 assert_eq!(leak.len(), 1);
2304 assert_eq!(leak[0].kind, DriftKind::Leak);
2305 assert!(drifts("platform", &[day("platform", 5_000_000, 0, 0, 0.0, 0.0)], 10.0, false, 100_000).is_empty());
2306 // Pennies say nothing.
2307 assert!(drifts("actions_cache", &[day("actions_cache", 50_000, 0, 0, 0.0, 0.0)], 10.0, false, 100_000).is_empty());
2308 assert!(drifts(UNMAPPED, &[day(UNMAPPED, 250_000, 0, 0, 0.0, 0.0)], 10.0, false, 100_000)[0].kind == DriftKind::Leak);
2309 }
2310
2311 #[test]
2312 fn a_margin_alert_needs_n_days_in_a_row_under_the_floor() {
2313 let s = |d: &str, revenue: i64, cost: i64| (d.to_string(), revenue, cost);
2314 // 5%, 0%, -20%: three days under 10%.
2315 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)];
2316 let (from, worst) = breach(&series, 10.0, 3, 100_000).unwrap();
2317 assert_eq!(from, "10-14");
2318 assert!((worst + 20.0).abs() < 1e-9);
2319 // A good day in the window clears it.
2320 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)];
2321 assert!(breach(&mended, 10.0, 3, 100_000).is_none());
2322 // Cost with no revenue at all is the worst margin there is.
2323 assert_eq!(breach(&[s("10-16", 0, 500_000)], 10.0, 1, 100_000).unwrap().1, -100.0);
2324 // Too little cost to judge.
2325 assert!(breach(&[s("10-16", 0, 5_000)], 10.0, 1, 100_000).is_none());
2326 assert!(breach(&series, 10.0, 9, 100_000).is_none());
2327 }
2328
2329 #[test]
2330 fn shared_costs_add_up_to_the_bill() {
2331 let w = |k: &str, v: f64| (k.to_string(), v);
2332 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)]);
2333 assert_eq!(attribute(10, &[w("a", 3.0), w("b", 1.0), w("a", 0.0)]), vec![("a".into(), 8), ("b".into(), 2)]);
2334 assert!(attribute(10, &[w("a", 0.0)]).is_empty());
2335 assert!(attribute(0, &[w("a", 1.0)]).is_empty());
2336 }
2337
2338 #[test]
Costs: counts are compared with Cloudflare's from the day g1t started counting, so days before a meter existed are not drift2339 fn counts_are_compared_from_the_day_g1t_started_counting() {
2340 let on = |day: &str, cf: f64, own: f64| ProductDay { day: day.into(), bucket: "git".into(), cf_quantity: cf, own_quantity: own, ..ProductDay::default() };
2341 // Five days of Cloudflare's count before g1t's meter, then two that match.
2342 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)];
2343 assert!(drifts("git", &days, 10.0, true, 0).iter().all(|d| d.kind != DriftKind::Count));
2344 // A real gap on the days both counted still shows.
2345 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)];
2346 let found = drifts("git", &days, 10.0, true, 0);
2347 let count = found.iter().find(|d| d.kind == DriftKind::Count).unwrap();
2348 assert_eq!((count.ours, count.cloudflare), (531.0, 1000.0));
2349 // A meter that never counted is compared over every day.
2350 let days = vec![on("2026-10-06", 400.0, 0.0)];
2351 assert!(drifts("git", &days, 10.0, true, 0).iter().any(|d| d.kind == DriftKind::Count));
2352 }
2353
2354 #[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 it2355 fn what_g1t_gives_away_is_kept_apart_from_what_it_sells() {
2356 let map = BTreeMap::new();
2357 // A comped workspace (all of it given), one in its trial (half paid
2358 // 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 running2359 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 it2360 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 running2361 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 it2362 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 running2363 // Nothing priced that day: free use.
2364 let free = usage("2026-10-15", "gamma", "agent", 0, 0, 1_000_000);
2365 let internal = BTreeSet::from(["flagon".to_string()]);
2366 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 it2367 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 running2368 assert_eq!(models.cost(), 4_000_000);
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging2369 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 running2370 let given = |w: &str| workspaces.iter().find(|x| x.workspace == w).unwrap().given.total();
2371 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 it2372 }
2373
2374 #[test]
Merge branch 'worktree-agent-a633ac0f7f66d419d'2375 fn a_discounted_sale_keeps_its_margin_and_counts_the_discount_as_given() {
2376 // $1 of model cost at 20%, sold to an account with 30% off: charged
2377 // $0.84, and $0.36 below cost plus the margin given (as usage_rows
2378 // reads the ledger: value at price, the discount part given).
2379 let mut sale = usage("2026-10-15", "acme", "agent", 1_200_000, 840_000, 1_000_000);
2380 sale.given = Given { discount: 360_000, ..Given::default() };
2381 let (days, _) = fold(&[], &BTreeMap::new(), &[], &[], &[sale], &BTreeSet::new());
2382 let models = days.iter().find(|d| d.bucket == "models").unwrap();
2383 assert_eq!(models.value_micros, 1_200_000);
2384 assert_eq!(models.given, Given { discount: 300_000, ..Given::default() });
2385 // What was sold (cost less given) still makes the margin.
2386 let sold = models.cost() - models.given.total();
2387 assert_eq!(margin_percent(models.cash_micros, sold).map(|m| m.round()), Some(17.0));
2388 }
2389
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging2390 fn draw(kind: CreditKind, reference: &str, task: Option<&str>, at: &str, micros: i64) -> (String, crate::grants::Draw) {
2391 let draw = crate::grants::Draw { grant: "crd_a".into(), kind, reference: reference.into(), task: task.map(Into::into), at: at.into(), micros };
2392 ("acme".to_owned(), draw)
2393 }
2394
2395 #[test]
2396 fn usage_paid_for_with_credit_is_given_not_money_in() {
2397 // $1.20 of usage on $1 of cost, all of it paid with promotional credit.
2398 let mut rows = vec![usage("2026-10-15", "acme", "implement", 1_200_000, 1_200_000, 1_000_000)];
2399 apply_credits(&mut rows, &[draw(CreditKind::Promotional, "run_1", Some("implement"), "2026-10-15T10:00:00Z", 1_200_000)], &[]);
2400 assert_eq!(rows[0].cash, 0);
2401 assert_eq!(rows[0].given, Given { credit_promotional: 1_200_000, ..Given::default() });
2402 let (days, workspaces) = fold(&[], &BTreeMap::new(), &[], &[], &rows, &BTreeSet::new());
2403 let models = days.iter().find(|d| d.bucket == "models").unwrap();
2404 // Valued at its price, none of it money in, all of its cost given:
2405 // the margin on what was sold is untouched by it.
2406 assert_eq!((models.value_micros, models.cash_micros), (1_200_000, 0));
2407 assert_eq!(models.given, Given { credit_promotional: 1_000_000, ..Given::default() });
2408 assert_eq!(models.cost() - models.given.total(), 0);
2409 assert_eq!(workspaces[0].given.total(), 1_000_000);
2410 // Half paid with goodwill credit: half the cost given, half sold.
2411 let mut rows = vec![usage("2026-10-15", "acme", "implement", 1_200_000, 1_200_000, 1_000_000)];
2412 apply_credits(&mut rows, &[draw(CreditKind::Goodwill, "run_1", Some("implement"), "2026-10-15T10:00:00Z", 600_000)], &[]);
2413 let (days, _) = fold(&[], &BTreeMap::new(), &[], &[], &rows, &BTreeSet::new());
2414 let models = days.iter().find(|d| d.bucket == "models").unwrap();
2415 assert_eq!(models.cash_micros, 600_000);
2416 assert_eq!(models.given, Given { credit_goodwill: 500_000, ..Given::default() });
2417 let sold = models.cost() - models.given.total();
2418 assert_eq!(margin_percent(models.cash_micros, sold).map(|m| m.round()), Some(17.0));
2419 }
2420
2421 #[test]
2422 fn what_a_refund_pays_for_is_paid_for_and_the_refund_comes_off_its_day() {
2423 // A refund's credit pays for usage: still money in, nothing given.
2424 let mut rows = vec![usage("2026-10-15", "acme", "implement", 1_200_000, 1_200_000, 1_000_000)];
2425 apply_credits(&mut rows, &[draw(CreditKind::Refund, "run_9", Some("implement"), "2026-10-15T10:00:00Z", 1_200_000)], &[]);
2426 assert_eq!((rows[0].cash, rows[0].given), (1_200_000, Given::default()));
2427 // The $3 refunded for Oct 2 comes off that day's money in, shared
2428 // over what was paid that day.
2429 let mut rows = vec![
2430 usage("2026-10-02", "acme", "implement", 4_000_000, 4_000_000, 3_000_000),
2431 usage("2026-10-02", "acme", "sandbox", 2_000_000, 2_000_000, 1_500_000),
2432 usage("2026-10-02", "beta", "implement", 9_000_000, 9_000_000, 7_000_000),
2433 ];
2434 let refund = crate::grants::Refunded { workspace: "acme".into(), day: "2026-10-02".into(), micros: 3_000_000 };
2435 apply_credits(&mut rows, &[], std::slice::from_ref(&refund));
2436 assert_eq!((rows[0].cash, rows[1].cash, rows[2].cash), (2_000_000, 1_000_000, 9_000_000));
2437 assert!(rows.iter().all(|r| r.given == Given::default()));
2438 // Nothing paid that day: a line of its own, money in less than nothing.
2439 let mut rows = vec![];
2440 apply_credits(&mut rows, &[], &[refund]);
2441 assert_eq!((rows[0].key.as_str(), rows[0].cash, rows[0].value), ("other", -3_000_000, 0));
2442 }
2443
2444 #[test]
2445 fn credit_spent_on_month_end_meters_is_a_line_of_its_own() {
2446 // Storage is reconciled from snapshots, not its ledger line: what
2447 // credit paid of it is its own row on the day it was charged.
2448 let mut rows = vec![usage("2026-10-01", "acme", "implement", 1_000, 1_000, 800)];
2449 apply_credits(&mut rows, &[draw(CreditKind::Goodwill, "storage/2026-09", Some("storage"), "2026-10-01T00:05:00Z", 2_000_000)], &[]);
2450 assert_eq!(rows.len(), 2);
2451 assert_eq!((rows[1].key.as_str(), rows[1].cash, rows[1].value), ("storage", -2_000_000, 0));
2452 assert_eq!(rows[1].given.credit_goodwill, 2_000_000);
2453 assert_eq!(rows[0].cash, 1_000);
2454 }
2455
Merge branch 'worktree-agent-a633ac0f7f66d419d'2456 #[test]
2457 fn the_gateways_total_against_the_ledgers_model_cost_is_drift() {
2458 // The gateway priced $5 of g1t's own traffic; the ledger has $3.
2459 let short = drifts("models", &[day("models", 5_000_000, 3_000_000, 3_600_000, 0.0, 0.0)], 10.0, false, 100_000);
2460 assert_eq!(short.iter().map(|d| d.kind).collect::<Vec<_>>(), vec![DriftKind::Cost]);
2461 assert!((short[0].delta_percent.unwrap() + 40.0).abs() < 1e-9);
2462 // Gateway traffic with nothing on the ledger at all: cost drift and a leak.
2463 let none = drifts("models", &[day("models", 2_000_000, 0, 0, 0.0, 0.0)], 10.0, false, 100_000);
2464 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 said2465 // Within the threshold: nothing.
Merge branch 'worktree-agent-a633ac0f7f66d419d'2466 assert!(drifts("models", &[day("models", 1_050_000, 1_000_000, 1_200_000, 0.0, 0.0)], 10.0, false, 100_000).is_empty());
Billing: AI Gateway's analytics are read with the token that can see them, and a gateway that priced nothing is said2467 // The gateway priced nothing against a ledger that has model cost:
2468 // not agreement (a token that cannot see AI Gateway reads as no
2469 // rows), so it is said. Under the minimum, or no model cost: nothing.
2470 let silent = drifts("models", &[day("models", 0, 1_000_000, 1_200_000, 0.0, 0.0)], 10.0, false, 100_000);
2471 assert_eq!(silent, vec![Drift { bucket: "models".into(), kind: DriftKind::Cost, ours: 1_000_000.0, cloudflare: 0.0, delta_percent: None }]);
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb972472 let said = models_detail(&silent[0], &costs::GatewayCaveats::default(), &[]);
Billing: AI Gateway's analytics are read with the token that can see them, and a gateway that priced nothing is said2473 assert!(said.contains("$1.00") && said.contains("priced nothing") && said.contains("AI Gateway: Read"), "{said}");
2474 assert!(drifts("models", &[day("models", 0, 50_000, 60_000, 0.0, 0.0)], 10.0, false, 100_000).is_empty());
2475 assert!(drifts("models", &[day("models", 0, 0, 0, 0.0, 0.0)], 10.0, false, 100_000).is_empty());
Merge branch 'worktree-agent-a633ac0f7f66d419d'2476 // The detail says which way and why it may be off.
2477 let caveats = costs::GatewayCaveats { cache_read_tokens: 3_000_000.0, unpriced: vec!["anthropic_claude_new_1".into()], ..Default::default() };
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb972478 let detail = models_detail(&short[0], &caveats, &[]);
Merge branch 'worktree-agent-a633ac0f7f66d419d'2479 assert!(detail.contains("$5.00") && detail.contains("$3.00") && detail.contains("were not charged"), "{detail}");
2480 assert!(detail.contains("3,000,000 prompt-cache read") && detail.contains("no price for anthropic_claude_new_1"), "{detail}");
2481 }
2482
2483 #[test]
2484 fn model_usage_the_gateway_cannot_price_is_drift_even_when_the_totals_agree() {
2485 assert!(unpriced_drift(&costs::GatewayCaveats::default()).is_none());
2486 // Cache tokens alone are a note on the cost drift, not drift.
2487 assert!(unpriced_drift(&costs::GatewayCaveats { cache_write_tokens: 10.0, ..Default::default() }).is_none());
2488 let (drift, detail) = unpriced_drift(&costs::GatewayCaveats { unpriced: vec!["anthropic_claude_new_1".into()], short_runs: 2, ..Default::default() }).unwrap();
2489 assert_eq!((drift.bucket.as_str(), drift.kind.as_str()), ("models", "unpriced"));
2490 assert!(detail.contains("no price for anthropic_claude_new_1") && detail.contains("2 runs were settled"), "{detail}");
2491 }
2492
2493 #[test]
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily2494 fn a_workspace_that_costs_more_than_it_pays_is_flagged() {
2495 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)];
2496 let found = anomalies(&rows, 1.0, 1_000_000);
2497 assert_eq!(found, vec![("acme".to_string(), 5_000_000, 1_000_000)]);
2498 // At twice its revenue as the threshold, $5 against $3 is fine.
2499 assert!(anomalies(&[("acme".to_string(), 5_000_000, 3_000_000)], 2.0, 1_000_000).is_empty());
2500 }
2501
2502 #[test]
2503 fn a_git_operation_costs_what_cloudflare_counts_for_it() {
2504 // $0.15 per 1,000 of Cloudflare's operations, on the charged days.
2505 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();
2506 assert!((rate - 0.000_15).abs() < 1e-12);
2507 // Cloudflare counted 3 for every 1 g1t did: binding reads count.
2508 let per_op = derived_unit_cost(rate, 300_000.0, 100_000.0).unwrap();
2509 let per_thousand_micros = per_op * unit_size("1,000 operations") * 1e6;
2510 assert!((per_thousand_micros - 450_000.0).abs() < 1e-6, "{per_thousand_micros}");
2511 // Too few of g1t's units to say.
2512 assert!(derived_unit_cost(rate, 3_000.0, 500.0).is_none());
2513 assert!(billed_rate(&[(10_000.0, 0.0)]).is_none());
2514 assert_eq!(unit_size("million requests"), 1e6);
2515 assert_eq!(unit_size("second"), 1.0);
2516 }
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb972517
2518 /// The case that started it: syntaqx's ~$8.62 of model usage was wiped
2519 /// by a testing reset, AI Gateway still priced all $11.11, and the
2520 /// ledger had $2.49 left.
2521 fn gateway_and_ledger(kept: bool) -> (Vec<ProductDay>, Vec<WorkspaceDay>) {
2522 let rules = vec![rule("ai_gateway_requests", "*", "models", None), rule("containers", "*", "sandboxes", None)];
2523 let lines = vec![
2524 line("2026-10-05", costs::SOURCE_GATEWAY, "ai_gateway_requests", "anthropic_claude_opus_5_5", 1.0, 11.11),
2525 line("2026-10-05", SOURCE_BILLABLE, "containers", "container_memory", 10.0, 0.30),
2526 ];
2527 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)];
2528 if kept {
2529 // What the reset kept (reset_costs), read back for the day.
2530 usage.extend(reset_usage(&[
2531 ("2026-10-05".into(), "syntaqx".into(), "models".into(), 8_620_000, 10_344_000),
2532 ("2026-10-05".into(), "syntaqx".into(), "sandboxes".into(), 100_000, 120_000),
2533 // The reset's own row is not usage.
2534 ("2026-10-07".into(), "syntaqx".into(), String::new(), 0, 0),
2535 ]));
2536 }
2537 fold(&rules, &revenue_map(), &lines, &[], &usage, &BTreeSet::new())
2538 }
2539
2540 #[test]
2541 fn what_a_reset_kept_is_on_the_ledgers_side_of_the_models_drift() {
2542 let models = |days: &[ProductDay]| days.iter().find(|d| d.bucket == "models").cloned().unwrap();
2543 // Without it: AI Gateway's $11.11 against the ledger's $2.49.
2544 let (days, _) = gateway_and_ledger(false);
2545 let drift = drifts("models", &[models(&days)], 10.0, false, 100_000);
2546 assert_eq!(drift.iter().map(|d| d.kind).collect::<Vec<_>>(), vec![DriftKind::Cost]);
2547 assert_eq!((drift[0].ours, drift[0].cloudflare), (2_490_000.0, 11_110_000.0));
2548 // With it: the ledger's model cost and the reset's add up to the gateway's.
2549 let (days, _) = gateway_and_ledger(true);
2550 let m = models(&days);
2551 assert_eq!(m.own_cost_micros, 11_110_000);
2552 assert!(drifts("models", &[m], 10.0, false, 100_000).is_empty());
2553 // The reset's own row makes no bucket of its own.
2554 assert!(!days.iter().any(|d| d.bucket.is_empty()));
2555 }
2556
2557 #[test]
2558 fn what_a_reset_kept_is_given_away_as_testing_resets() {
2559 let (days, workspaces) = gateway_and_ledger(true);
2560 let models = days.iter().find(|d| d.bucket == "models").unwrap();
2561 // All of syntaqx's model cost is given, none of it money in.
2562 assert_eq!(models.given, Given { reset: 8_620_000, ..Given::default() });
2563 assert_eq!(models.cash_micros, 2_988_000);
2564 // Cloudflare's sandbox cost is shared by what each workspace's usage
2565 // cost: syntaqx's half is given too.
2566 let sandboxes = days.iter().find(|d| d.bucket == "sandboxes").unwrap();
2567 assert_eq!((sandboxes.cost(), sandboxes.given.reset), (300_000, 150_000));
2568 // Who g1t paid: syntaqx is still on it, all of its cost given.
2569 let syntaqx: Vec<&WorkspaceDay> = workspaces.iter().filter(|w| w.workspace == "syntaqx").collect();
2570 assert_eq!(syntaqx.iter().map(|w| w.cost).sum::<i64>(), 8_770_000);
2571 assert!(syntaqx.iter().all(|w| w.given.reset == w.cost && w.given.total() == w.cost && w.revenue == 0));
2572 // The statement reads it back from margin_days by why.
2573 let row = MarginRow {
2574 day: models.day.clone(),
2575 bucket: models.bucket.clone(),
2576 cf_cost_micros: models.cf_cost_micros,
2577 own_cost_micros: models.own_cost_micros,
2578 value_micros: models.value_micros,
2579 cash_micros: models.cash_micros,
2580 cf_quantity: 0.0,
2581 own_quantity: 0.0,
2582 given_comped_micros: Some(0),
2583 given_free_micros: Some(0),
2584 given_trial_micros: Some(0),
2585 given_pool_micros: Some(0),
2586 given_discount_micros: Some(0),
2587 given_credit_promotional_micros: Some(0),
2588 given_credit_goodwill_micros: Some(0),
2589 given_reset_micros: Some(models.given.reset),
2590 };
2591 assert_eq!(ProductDay::from(row).given, models.given);
2592 }
2593
2594 #[test]
2595 fn reconciling_again_gives_the_same_answer() {
2596 assert_eq!(gateway_and_ledger(true), gateway_and_ledger(true));
2597 // A reset's kept rows are read back exactly as kept: running it
2598 // again cannot count them twice.
2599 let kept = [("2026-10-05".to_string(), "syntaqx".to_string(), "models".to_string(), 8_620_000, 10_344_000)];
2600 assert_eq!(reset_usage(&kept), reset_usage(&kept));
2601 assert_eq!(reset_usage(&kept).len(), 1);
2602 }
2603
2604 #[test]
2605 fn a_reset_from_before_resets_kept_their_cost_is_said_not_called_a_leak() {
2606 let notes = reset_notes(
2607 &[("ws_syntaqx".into(), "2026-10-07T09:41:00.000Z".into()), ("ws_acme".into(), "2026-10-08T01:00:00.000Z".into())],
2608 &[("acme".into(), "2026-10-08T01:00:00.000Z".into(), 1_500_000)],
2609 );
2610 assert_eq!(
2611 notes,
2612 vec![
2613 ResetNote { workspace: "syntaqx".into(), day: "2026-10-07".into(), recorded: false, models_micros: 0 },
2614 ResetNote { workspace: "acme".into(), day: "2026-10-08".into(), recorded: true, models_micros: 1_500_000 },
2615 ]
2616 );
2617 let drift = Drift { bucket: "models".into(), kind: DriftKind::Cost, ours: 2_490_000.0, cloudflare: 11_110_000.0, delta_percent: Some(-77.6) };
2618 let detail = models_detail(&drift, &costs::GatewayCaveats::default(), &notes);
2619 assert!(detail.contains("includes model usage wiped by a testing reset of syntaqx on 2026-10-07"), "{detail}");
2620 assert!(detail.contains("not a leak") && detail.contains("leaves the 7 days on 2026-10-14"), "{detail}");
2621 assert!(!detail.contains("a gap that stays is a leak"), "{detail}");
2622 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}");
2623 // The models leak is not raised while such a reset is in the window.
2624 let leak = Drift { bucket: "models".into(), kind: DriftKind::Leak, ours: 0.0, cloudflare: 11_110_000.0, delta_percent: None };
2625 assert!(wiped_not_leaked(&leak, &notes));
2626 assert!(!wiped_not_leaked(&leak, &notes[1..]));
2627 assert!(!wiped_not_leaked(&Drift { bucket: "actions_cache".into(), ..leak }, &notes));
2628 // No reset: the detail is as before.
2629 assert!(models_detail(&drift, &costs::GatewayCaveats::default(), &[]).contains("a gap that stays is a leak"));
2630 }
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily2631}

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