Skip to content
2,639 linesCodeBlameRaw

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

Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1//! What g1t earns on each thing it sells, measured against what
2//! Cloudflare actually charged for it.
3//!
4//! Once a day, after `costs` has read Cloudflare's bill, the reconciler
5//! puts three figures side by side for every day and each of g1t's
6//! products (a "bucket": sandboxes, deployments, git, repository storage,
7//! …):
8//!
9//! 1. **What Cloudflare charged**: the day's cost lines `cost_map` gives
10//! the bucket.
11//! 2. **What g1t's meters recorded**: the cost on the ledger's entries for
12//! it (the price book's cost at the time) and, where a mapping names
13//! one, g1t's own count of the same units (git operations).
14//! 3. **What customers were charged**: the entries' value at price, before
15//! the plan's included usage, a trial or a pool paid part of it; and of
16//! that, what workspaces paid. Month-end meters (git, storage, scans,
17//! embeddings, the actions cache) come from daily snapshots of what they
18//! had come to (`pending_days`). The plan's price is the `platform`
19//! bucket's: the plan pays for running g1t.
20//!
21//! From those: margin per product (value against cost) and for all of g1t
22//! (money in against every cost); drift (counts or costs that disagree past
23//! a mapping's threshold, and leaks: cost with no revenue, or a Cloudflare
24//! meter no one mapped); each workspace's cost, Cloudflare's figure shared
25//! out by each workspace's own meters; and price proposals when a unit's
26//! real cost has moved (`pricing`). Alerts go to staff by email and as a
27//! banner in sudo. See docs/BILLING_OPERATIONS.md.
28
29use std::collections::{BTreeMap, BTreeSet};
30
31use g1t_contracts::billing::*;
32use g1t_contracts::{FailureCode, Outcome, new_id};
33use g1t_contracts::time::rfc3339;
34use g1t_kit::now_ms;
35use serde::{Deserialize, Serialize};
36use worker::wasm_bindgen::JsValue;
37use worker::{Env, Result};
38
39use crate::Billing;
40use crate::costs::{self, ARTIFACTS_OPERATIONS, DAY_MS, Rule, SOURCE_ARTIFACTS, SOURCE_BILLABLE, UNMAPPED};
41
42/// Buckets that are the cost of running g1t, paid by the plan rather than
43/// sold by the unit: never a leak for having no revenue of their own.
44pub(crate) const OVERHEAD: [&str; 1] = ["platform"];
45/// Buckets Cloudflare does not bill: their cost is g1t's own figure.
46pub(crate) const NOT_CLOUDFLARE: [&str; 1] = ["models"];
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<()> {
Merge branch 'main' into actions-toolkit-oidc-artifacts920 email_staff_page(env, to, subject, lines, ("Costs & margin", "https://sudo.g1t.sh/costs"), "g1t-billing's margin guard").await
921}
922
923/// Emails staff, linking to a page of sudo (`page`: its name and address)
924/// and saying what sent it.
925pub(crate) async fn email_staff_page(env: &Env, to: &str, subject: &str, lines: &[String], page: (&str, &str), sender: &str) -> Result<()> {
926 let (name, link) = page;
927 let text = format!("{}\n\n{name}: {link}\n\nSent by {sender} (COSTS_ALERT_EMAIL).\n", lines.join("\n\n"));
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily928 let mut html = String::from("<div style=\"font-family:system-ui,sans-serif;max-width:560px;margin:0 auto;padding:24px 16px;color:#16150f\">");
929 for line in lines {
930 html.push_str(&format!("<p style=\"font-size:15px;line-height:1.6\">{}</p>", escape(line)));
931 }
932 html.push_str(&format!(
Merge branch 'main' into actions-toolkit-oidc-artifacts933 "<p><a href=\"{link}\">Open {} in sudo</a></p><p style=\"font-size:13px;color:#6e6a5e\">Sent by {} (COSTS_ALERT_EMAIL).</p></div>",
934 escape(name),
935 escape(sender)
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily936 ));
937 let mail = Mail { to, from: "g1t <noreply@g1t.sh>", subject, text, html };
938 let binding = g1t_kit::js::binding(env, "EMAIL")?;
939 g1t_kit::js::call(&binding, "send", &[g1t_kit::js::to_js(&mail)?]).await?;
940 Ok(())
941}
942
943#[derive(Deserialize)]
944struct AlertRow {
945 id: String,
946 kind: String,
947 subject: String,
948 detail: String,
949 since: String,
950 opened_at: String,
951 emailed_at: Option<String>,
952}
953
954impl From<AlertRow> for MarginAlert {
955 fn from(r: AlertRow) -> Self {
956 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 }
957 }
958}
959
960#[derive(Deserialize)]
961struct MarginRow {
962 day: String,
963 bucket: String,
964 cf_cost_micros: i64,
965 own_cost_micros: i64,
966 value_micros: i64,
967 cash_micros: i64,
968 cf_quantity: f64,
969 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 it970 #[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 running971 given_comped_micros: Option<i64>,
972 #[serde(default)]
973 given_free_micros: Option<i64>,
974 #[serde(default)]
975 given_trial_micros: Option<i64>,
976 #[serde(default)]
977 given_pool_micros: Option<i64>,
Merge branch 'worktree-agent-a633ac0f7f66d419d'978 #[serde(default)]
979 given_discount_micros: Option<i64>,
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging980 #[serde(default)]
981 given_credit_promotional_micros: Option<i64>,
982 #[serde(default)]
983 given_credit_goodwill_micros: Option<i64>,
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97984 #[serde(default)]
985 given_reset_micros: Option<i64>,
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily986}
987
988impl From<MarginRow> for ProductDay {
989 fn from(r: MarginRow) -> Self {
990 ProductDay {
991 day: r.day,
992 bucket: r.bucket,
993 cf_cost_micros: r.cf_cost_micros,
994 own_cost_micros: r.own_cost_micros,
995 value_micros: r.value_micros,
996 cash_micros: r.cash_micros,
997 cf_quantity: r.cf_quantity,
998 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 running999 given: Given {
1000 comped: r.given_comped_micros.unwrap_or(0),
1001 free: r.given_free_micros.unwrap_or(0),
1002 trial: r.given_trial_micros.unwrap_or(0),
1003 pool: r.given_pool_micros.unwrap_or(0),
Merge branch 'worktree-agent-a633ac0f7f66d419d'1004 discount: r.given_discount_micros.unwrap_or(0),
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging1005 credit_promotional: r.given_credit_promotional_micros.unwrap_or(0),
1006 credit_goodwill: r.given_credit_goodwill_micros.unwrap_or(0),
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb971007 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 running1008 },
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1009 }
1010 }
1011}
1012
1013impl Billing {
1014 /// The day's work: read Cloudflare's bill and g1t's own counts,
1015 /// reconcile, look for drift, measure unit costs, apply prices whose
1016 /// day has come, and raise or clear alerts.
1017 pub(crate) async fn costs_daily(&self, env: &Env, keeper: &crate::keeper::Keeper) -> Result<CostsRun> {
1018 let mut run = CostsRun::default();
1019 let (since, until) = match self.read_cloudflare(keeper, &mut run.problems).await? {
1020 Some((since, until, lines)) => {
1021 run.lines = lines;
1022 (since, until)
1023 }
1024 // Without the bill, still reconcile what g1t knows itself, over
1025 // the same days the bill would be read for.
1026 None => {
1027 #[derive(Deserialize)]
1028 struct Last {
1029 day: Option<String>,
1030 }
1031 let last = self.db.prepare("SELECT MAX(day) AS day FROM margin_days").first::<Last>(None).await?.and_then(|l| l.day);
1032 costs::window(last.as_deref(), now_ms())
1033 }
1034 };
Costs: Cloudflare's subscriptions read from Cloudflare each day, the estimate only until then; sudo's costs split into Costs & margin and Bill & pricing1035 // Not a problem for the run: the last read, or the estimate, stays.
1036 if keeper.can_read_bill()
1037 && let Err(error) = self.read_subscriptions(keeper).await
1038 {
1039 worker::console_error!("Cloudflare's subscriptions were not read: {error}");
1040 }
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1041 if let Err(error) = self.count_own(&since, &until).await {
1042 run.problems.push(format!("g1t's own counts could not be read: {error}"));
1043 }
1044 self.snapshot_pending(&until).await?;
Models' margin read -14%: usage nothing paid for is valued at price, not $01045 // Reconciled over the whole window sudo shows, not only the days the
1046 // bill was read for: it reads only what is already kept, so a change
1047 // in how a day is valued reaches every day shown at the next run.
1048 let window = day_before(&until, costs::BACKFILL_DAYS - 1);
1049 let reconcile_from = if window < since { window } else { since.clone() };
1050 run.days = self.reconcile_range(&reconcile_from, &until).await?;
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1051 let drift = self.find_drift(&until).await?;
1052 run.proposals = self.measure_units(&until).await?;
1053 self.apply_due_versions().await?;
1054 run.alerts = self.raise_alerts(env, &until, &drift).await?;
1055 if let Some(identity) = &self.identity
1056 && let Err(error) = self.tell_owners_of_rises(identity).await
1057 {
1058 run.problems.push(format!("owners could not be told of a price rise: {error}"));
1059 }
1060 for problem in &run.problems {
1061 worker::console_warn!("costs: {problem}");
1062 }
1063 Ok(run)
1064 }
1065
1066 /// What each month-end source had come to by the end of `day`.
1067 async fn snapshot_pending(&self, day: &str) -> Result<()> {
1068 self.db
1069 .prepare(
1070 "INSERT INTO pending_days (day, workspace, source, cost_micros, charge_micros)
1071 SELECT ?1, workspace, source, COALESCE(cost_micros, 0), COALESCE(charge_micros, 0) FROM pending_usage WHERE month = ?2
1072 ON CONFLICT (day, workspace, source) DO UPDATE SET cost_micros = excluded.cost_micros, charge_micros = excluded.charge_micros",
1073 )
1074 .bind(&[day.into(), day[..7].into()])?
1075 .run()
1076 .await?;
1077 Ok(())
1078 }
1079
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb971080 /// What customers were charged on the days, by workspace and key: every
1081 /// workspace's, or only `only`'s.
1082 async fn usage_rows(&self, since: &str, until: &str, only: Option<&str>) -> Result<Vec<UsageRow>> {
1083 let only_sql = only.unwrap_or("");
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1084 #[derive(Deserialize)]
1085 struct Row {
1086 day: String,
1087 workspace: String,
1088 key: String,
1089 internal: i64,
1090 own_provider: i64,
1091 cash: Option<i64>,
1092 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 running1093 trial: Option<i64>,
1094 oss: Option<i64>,
1095 covered: Option<i64>,
Merge branch 'worktree-agent-a633ac0f7f66d419d'1096 discount: Option<i64>,
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1097 cost: Option<i64>,
1098 }
1099 let charged_here = crate::storage::CHARGED_HERE.iter().map(|s| format!("'{s}'")).collect::<Vec<_>>().join(", ");
1100 let end = format!("{until}T23:59:59.999Z");
1101 let rows = self
1102 .db
1103 .prepare(format!(
1104 "SELECT substr(created_at, 1, 10) AS day, workspace,
1105 CASE WHEN task = 'deployments' AND reference LIKE 'deploy/%' THEN 'builds' ELSE COALESCE(task, 'other') END AS key,
1106 CASE WHEN workspace IN ({internal}) THEN 1 ELSE 0 END AS internal,
1107 CASE WHEN billed_to = 'workspace' THEN 1 ELSE 0 END AS own_provider,
1108 -SUM(amount_micros) AS cash,
1109 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 running1110 SUM(COALESCE(trial_micros, 0)) AS trial,
1111 SUM(COALESCE(oss_micros, 0)) AS oss,
1112 SUM(COALESCE(given_micros, 0)) AS covered,
Merge branch 'worktree-agent-a633ac0f7f66d419d'1113 SUM(COALESCE(discount_micros, 0)) AS discount,
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1114 SUM(COALESCE(cost_micros, 0)) AS cost
1115 FROM ledger
1116 WHERE kind = 'usage' AND created_at >= ?1 AND created_at <= ?2 AND COALESCE(task, '') NOT IN ({charged_here})
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb971117 AND (?3 = '' OR workspace = ?3)
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1118 GROUP BY 1, 2, 3, 4, 5",
1119 internal = crate::sales::INTERNAL_SQL
1120 ))
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb971121 .bind(&[since.into(), end.as_str().into(), only_sql.into()])?
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1122 .all()
1123 .await?
1124 .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 it1125 let mut internal = BTreeSet::new();
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1126 let mut out: Vec<UsageRow> = rows
1127 .into_iter()
1128 .map(|r| {
1129 // A workspace's own model provider was paid there: no cost
1130 // to g1t. g1t's own workspaces are valued at price.
1131 let cost = if r.own_provider == 1 { 0 } else { r.cost.unwrap_or(0) };
1132 let cash = r.cash.unwrap_or(0);
Merge branch 'worktree-agent-a633ac0f7f66d419d'1133 // A discount took its part below cost plus the margin: it is
1134 // valued at price and that part counted as given, so a
1135 // discounted sale never reads as margin lost.
1136 let discount = r.discount.unwrap_or(0).max(0);
1137 let paid = cash + r.drawn.unwrap_or(0) + discount;
Models' margin read -14%: usage nothing paid for is valued at price, not $01138 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 running1139 let given = if r.internal == 1 {
1140 Given { comped: value, ..Given::default() }
1141 } else if paid == 0 && cost > 0 {
1142 Given { free: value, ..Given::default() }
1143 } else {
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging1144 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 running1145 };
Costs: margin is measured on what was sold; comped workspaces, free periods, the trial and the pools are given away, a budget shown beside it1146 if r.internal == 1 {
1147 internal.insert(r.workspace.clone());
1148 }
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb971149 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 daily1150 })
1151 .collect();
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging1152 // Credits from g1t: what promotional and goodwill credit paid for
1153 // is given, not money in; a refund gives money back on its day.
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb971154 let (mut draws, mut refunds) = self.credit_effects(since, until).await?;
1155 if let Some(only) = only {
1156 draws.retain(|(workspace, _)| workspace == only);
1157 refunds.retain(|r| r.workspace == only);
1158 }
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging1159 apply_credits(&mut out, &draws, &refunds);
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1160 // Month-end sources, from their daily snapshots.
1161 #[derive(Deserialize)]
1162 struct Snap {
1163 day: String,
1164 workspace: String,
1165 source: String,
1166 cost_micros: i64,
1167 charge_micros: i64,
1168 }
1169 let snaps = self
1170 .db
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb971171 .prepare("SELECT day, workspace, source, cost_micros, charge_micros FROM pending_days WHERE day >= ?1 AND day <= ?2 AND (?3 = '' OR workspace = ?3)")
1172 .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 daily1173 .all()
1174 .await?
1175 .results::<Snap>()?
1176 .into_iter()
1177 .filter(|s| crate::storage::CHARGED_HERE.contains(&s.source.as_str()) || s.source == "domains")
1178 .map(|s| (s.day, s.workspace, s.source, s.cost_micros, s.charge_micros))
1179 .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 it1180 out.extend(pending_deltas(&snaps).into_iter().filter(|u| u.day.as_str() >= since).map(|mut u| {
1181 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 running1182 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 it1183 }
1184 u
1185 }));
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1186 // The plan's price, spread over the 30 days it pays for, so a month's
1187 // payment does not read as one very good day and 29 bad ones.
1188 #[derive(Deserialize)]
1189 struct Plan {
1190 day: String,
1191 workspace: String,
1192 micros: Option<i64>,
1193 }
1194 let plans = self
1195 .db
1196 .prepare(
1197 "SELECT substr(paid_at, 1, 10) AS day, workspace, SUM(amount_micros) AS micros FROM plan_payments
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb971198 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 daily1199 )
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb971200 .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 daily1201 .all()
1202 .await?
1203 .results::<Plan>()?;
1204 for p in plans {
1205 for (day, micros) in spread(&p.day, p.micros.unwrap_or(0), PLAN_DAYS) {
1206 if day.as_str() >= since && day.as_str() <= until {
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb971207 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 daily1208 }
1209 }
1210 }
1211 Ok(out)
1212 }
1213
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb971214 /// Which bucket each ledger key (and month-end source) is revenue of.
1215 async fn revenue_map(&self) -> Result<BTreeMap<String, String>> {
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1216 #[derive(Deserialize)]
1217 struct Map {
1218 key: String,
1219 bucket: String,
1220 }
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb971221 Ok(self
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1222 .db
1223 .prepare("SELECT key, bucket FROM revenue_map")
1224 .all()
1225 .await?
1226 .results::<Map>()?
1227 .into_iter()
1228 .map(|m| (m.key, m.bucket))
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb971229 .collect())
1230 }
1231
1232 /// What a testing reset of `workspace` is about to wipe that g1t paid
1233 /// for, a row per day and bucket: its whole ledger and month-end
1234 /// snapshots, valued as the reconciliation values them.
1235 pub(crate) async fn wiped_by_reset(&self, workspace: &str) -> Result<Vec<Wiped>> {
1236 let today = rfc3339(now_ms())[..10].to_owned();
1237 let rows = self.usage_rows(RESET_HISTORY_FROM, &today, Some(workspace)).await?;
1238 Ok(wiped(&rows, &self.revenue_map().await?, self.margin_percent))
1239 }
1240
1241 /// What testing resets kept for the days, as usage rows.
1242 async fn reset_rows(&self, since: &str, until: &str) -> Result<Vec<UsageRow>> {
1243 #[derive(Deserialize)]
1244 struct Kept {
1245 day: String,
1246 workspace: String,
1247 bucket: String,
1248 cost: Option<i64>,
1249 value: Option<i64>,
1250 }
1251 let kept = self
1252 .db
1253 .prepare(
1254 "SELECT day, workspace, bucket, SUM(cost_micros) AS cost, SUM(value_micros) AS value FROM reset_costs
1255 WHERE day >= ?1 AND day <= ?2 AND bucket <> '' GROUP BY day, workspace, bucket",
1256 )
1257 .bind(&[since.into(), until.into()])?
1258 .all()
1259 .await?
1260 .results::<Kept>()?;
1261 Ok(reset_usage(
1262 &kept.into_iter().map(|k| (k.day, k.workspace, k.bucket, k.cost.unwrap_or(0), k.value.unwrap_or(0))).collect::<Vec<_>>(),
1263 ))
1264 }
1265
1266 /// Testing resets on or after `since` (the day they wiped usage up to
1267 /// is their own, so one before it wiped nothing in the days): those
1268 /// that kept what they wiped (`reset_costs`) and those from before
1269 /// resets did, known only from the audit log.
1270 async fn resets_since(&self, since: &str, until: &str) -> Result<Vec<ResetNote>> {
1271 let end = format!("{until}T23:59:59.999Z");
1272 #[derive(Deserialize)]
1273 struct Audit {
1274 account: String,
1275 created_at: String,
1276 }
1277 let audits = self
1278 .db
1279 .prepare("SELECT account, created_at FROM admin_actions WHERE action = 'reset' AND created_at >= ?1 AND created_at <= ?2")
1280 .bind(&[since.into(), end.as_str().into()])?
1281 .all()
1282 .await?
1283 .results::<Audit>()?;
1284 #[derive(Deserialize)]
1285 struct Kept {
1286 workspace: String,
1287 reset_at: String,
1288 models: Option<i64>,
1289 }
1290 let kept = self
1291 .db
1292 .prepare(
1293 "SELECT workspace, reset_at, SUM(CASE WHEN bucket = ?3 AND day >= ?1 THEN cost_micros ELSE 0 END) AS models
1294 FROM reset_costs WHERE reset_at >= ?1 AND reset_at <= ?2 GROUP BY workspace, reset_at",
1295 )
1296 .bind(&[since.into(), end.as_str().into(), NOT_CLOUDFLARE[0].into()])?
1297 .all()
1298 .await?
1299 .results::<Kept>()?;
1300 Ok(reset_notes(
1301 &audits.into_iter().map(|a| (a.account, a.created_at)).collect::<Vec<_>>(),
1302 &kept.into_iter().map(|k| (k.workspace, k.reset_at, k.models.unwrap_or(0))).collect::<Vec<_>>(),
1303 ))
1304 }
1305
1306 /// Reconciles the days and writes `margin_days` and `workspace_costs`.
1307 async fn reconcile_range(&self, since: &str, until: &str) -> Result<u32> {
1308 let rules = self.rules().await?;
1309 let revenue_map = self.revenue_map().await?;
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1310 let lines = self
1311 .db
1312 .prepare("SELECT day, source, product, meter, quantity, cost_usd FROM cost_lines WHERE day >= ?1 AND day <= ?2")
1313 .bind(&[since.into(), until.into()])?
1314 .all()
1315 .await?
1316 .results::<LineRow>()?;
1317 let own = self
1318 .db
1319 .prepare("SELECT day, meter, workspace, quantity FROM own_counts WHERE day >= ?1 AND day <= ?2")
1320 .bind(&[since.into(), until.into()])?
1321 .all()
1322 .await?
1323 .results::<OwnRow>()?;
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb971324 let mut usage = self.usage_rows(since, until, None).await?;
1325 // What testing resets wiped: still paid for, now given away.
1326 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 running1327 #[derive(Deserialize)]
1328 struct Internal {
1329 workspace: String,
1330 }
1331 let internal: BTreeSet<String> = self
1332 .db
1333 .prepare(format!("WITH i(workspace) AS ({}) SELECT DISTINCT workspace FROM i", crate::sales::INTERNAL_SQL))
1334 .all()
1335 .await?
1336 .results::<Internal>()?
1337 .into_iter()
1338 .map(|i| i.workspace)
1339 .collect();
1340 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 daily1341 let now = rfc3339(now_ms());
1342 self.db
1343 .batch(vec![
1344 self.db.prepare("DELETE FROM margin_days WHERE day >= ?1 AND day <= ?2").bind(&[since.into(), until.into()])?,
1345 self.db.prepare("DELETE FROM workspace_costs WHERE day >= ?1 AND day <= ?2").bind(&[since.into(), until.into()])?,
1346 ])
1347 .await?;
1348 for chunk in days.chunks(50) {
1349 let mut statements = Vec::with_capacity(chunk.len());
1350 for d in chunk {
1351 statements.push(
1352 self.db
1353 .prepare(
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb971354 "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)
1355 VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1356 )
1357 .bind(&[
1358 d.day.as_str().into(),
1359 d.bucket.as_str().into(),
1360 (d.cf_cost_micros as f64).into(),
1361 (d.own_cost_micros as f64).into(),
1362 (d.value_micros as f64).into(),
1363 (d.cash_micros as f64).into(),
1364 d.cf_quantity.into(),
1365 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 running1366 (d.given.total() as f64).into(),
1367 (d.given.comped as f64).into(),
1368 (d.given.free as f64).into(),
1369 (d.given.trial as f64).into(),
1370 (d.given.pool as f64).into(),
Merge branch 'worktree-agent-a633ac0f7f66d419d'1371 (d.given.discount as f64).into(),
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging1372 (d.given.credit_promotional as f64).into(),
1373 (d.given.credit_goodwill as f64).into(),
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb971374 (d.given.reset as f64).into(),
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1375 now.as_str().into(),
1376 ])?,
1377 );
1378 }
1379 self.db.batch(statements).await?;
1380 }
1381 for chunk in workspaces.chunks(50) {
1382 let mut statements = Vec::with_capacity(chunk.len());
1383 for w in chunk {
1384 statements.push(
1385 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 it1386 .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 daily1387 .bind(&[
1388 w.day.as_str().into(),
1389 w.workspace.as_str().into(),
1390 w.bucket.as_str().into(),
1391 (w.cost as f64).into(),
1392 (w.revenue as f64).into(),
Margin alerts measure what is sold, and say dollars when a percentage would mislead1393 (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 running1394 (w.given.total() as f64).into(),
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1395 ])?,
1396 );
1397 }
1398 self.db.batch(statements).await?;
1399 }
1400 Ok(costs::days_between(since, until).len() as u32)
1401 }
1402
1403 async fn margin_days(&self, since: &str, until: &str) -> Result<Vec<ProductDay>> {
1404 Ok(self
1405 .db
1406 .prepare("SELECT * FROM margin_days WHERE day >= ?1 AND day <= ?2 ORDER BY day, bucket")
1407 .bind(&[since.into(), until.into()])?
1408 .all()
1409 .await?
1410 .results::<MarginRow>()?
1411 .into_iter()
1412 .map(ProductDay::from)
1413 .collect())
1414 }
1415
Merge branch 'worktree-agent-a633ac0f7f66d419d'1416 /// What AI Gateway's lines over the days, and the runs settled in them,
1417 /// say about whether its cost is what the providers bill.
1418 async fn gateway_caveats(&self, since: &str, until: &str) -> Result<costs::GatewayCaveats> {
1419 #[derive(Deserialize)]
1420 struct Line {
1421 meter: String,
1422 quantity: f64,
1423 cost_usd: f64,
1424 }
1425 let lines: Vec<(String, f64, f64)> = self
1426 .db
1427 .prepare("SELECT meter, quantity, cost_usd FROM cost_lines WHERE source = ?1 AND day >= ?2 AND day <= ?3")
1428 .bind(&[costs::SOURCE_GATEWAY.into(), since.into(), until.into()])?
1429 .all()
1430 .await?
1431 .results::<Line>()?
1432 .into_iter()
1433 .map(|l| (l.meter, l.quantity, l.cost_usd))
1434 .collect();
1435 let mut caveats = costs::gateway_caveats(&lines);
1436 #[derive(Deserialize)]
1437 struct Short {
1438 n: Option<f64>,
1439 }
1440 caveats.short_runs = self
1441 .db
1442 .prepare("SELECT COUNT(*) AS n FROM runs WHERE gateway_note IS NOT NULL AND settled_at >= ?1 AND settled_at <= ?2")
1443 .bind(&[since.into(), format!("{until}T23:59:59.999Z").into()])?
1444 .first::<Short>(None)
1445 .await?
1446 .and_then(|s| s.n)
1447 .unwrap_or(0.0) as u32;
1448 Ok(caveats)
1449 }
1450
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1451 /// Drift over the last week, written to `cost_drift` (replacing the
1452 /// last run's), with unmapped Cloudflare meters as leaks.
1453 async fn find_drift(&self, until: &str) -> Result<Vec<(Drift, String)>> {
1454 let since = day_before(until, DRIFT_DAYS - 1);
1455 let settings = self.cost_settings().await?;
1456 let rules = self.rules().await?;
1457 let days = self.margin_days(&since, until).await?;
1458 let mut by: BTreeMap<String, Vec<ProductDay>> = BTreeMap::new();
1459 for d in days {
1460 by.entry(d.bucket.clone()).or_default().push(d);
1461 }
Merge branch 'worktree-agent-a633ac0f7f66d419d'1462 let caveats = self.gateway_caveats(&since, until).await?;
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb971463 let resets = self.resets_since(&since, until).await?;
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1464 let mut found = Vec::new();
Merge branch 'worktree-agent-a633ac0f7f66d419d'1465 if let Some(drift) = unpriced_drift(&caveats) {
1466 found.push(drift);
1467 }
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1468 for (bucket, days) in &by {
1469 let bucket_rules: Vec<&Rule> = rules.iter().filter(|r| &r.bucket == bucket).collect();
1470 let threshold = bucket_rules.iter().map(|r| r.drift_percent).fold(f64::INFINITY, f64::min);
1471 let threshold = if threshold.is_finite() { threshold } else { 10.0 };
1472 let counted = bucket_rules.iter().any(|r| r.own_meter.is_some());
1473 for drift in drifts(bucket, days, threshold, counted, settings.min_daily_cost_micros) {
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb971474 if wiped_not_leaked(&drift, &resets) {
1475 continue;
1476 }
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1477 let title = costs::bucket_title(bucket);
1478 let detail = match drift.kind {
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb971479 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 daily1480 DriftKind::Count => format!(
One operation mapping, owned by repos; billing reads it instead of keeping its own1481 "{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 daily1482 crate::features::thousands(drift.ours.max(0.0).round() as u64),
1483 crate::features::thousands(drift.cloudflare.max(0.0).round() as u64),
1484 drift.delta_percent.unwrap_or(0.0)
1485 ),
1486 DriftKind::Cost => format!(
1487 "{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.",
1488 dollars(drift.cloudflare as i64),
1489 dollars(drift.ours as i64),
1490 drift.delta_percent.unwrap_or(0.0)
1491 ),
1492 DriftKind::Leak if bucket == UNMAPPED => {
1493 format!("Cloudflare charged {} for meters no mapping claims. Map them on Costs & margin.", dollars(drift.cloudflare as i64))
1494 }
Merge branch 'worktree-agent-a633ac0f7f66d419d'1495 DriftKind::Leak if NOT_CLOUDFLARE.contains(&bucket.as_str()) => format!(
1496 "{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).",
1497 dollars(drift.cloudflare as i64)
1498 ),
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1499 DriftKind::Leak => format!(
1500 "{title}: Cloudflare charged {} over the last {DRIFT_DAYS} days and customers were charged nothing for it.",
1501 dollars(drift.cloudflare as i64)
1502 ),
Merge branch 'worktree-agent-a633ac0f7f66d419d'1503 // Raised from the gateway's lines, not per bucket.
1504 DriftKind::Unpriced => unpriced_detail(&caveats),
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1505 };
1506 found.push((drift, detail));
1507 }
1508 }
1509 let now = rfc3339(now_ms());
1510 let mut statements = vec![self.db.prepare("DELETE FROM cost_drift")];
1511 for (drift, detail) in &found {
1512 statements.push(
1513 self.db
1514 .prepare("INSERT OR REPLACE INTO cost_drift (bucket, kind, ours, cloudflare, delta_percent, detail, found_at) VALUES (?, ?, ?, ?, ?, ?, ?)")
1515 .bind(&[
1516 drift.bucket.as_str().into(),
1517 drift.kind.as_str().into(),
1518 drift.ours.into(),
1519 drift.cloudflare.into(),
1520 drift.delta_percent.map_or(JsValue::NULL, JsValue::from),
1521 detail.as_str().into(),
1522 now.as_str().into(),
1523 ])?,
1524 );
1525 }
1526 self.db.batch(statements).await?;
1527 Ok(found)
1528 }
1529
1530 /// Unit costs from the bill for mappings that scale to g1t's own count
1531 /// (git operations), proposed to the price book.
1532 async fn measure_units(&self, until: &str) -> Result<u32> {
1533 #[derive(Deserialize)]
1534 struct Scaled {
1535 product: String,
1536 meter: String,
1537 price_meter: String,
1538 own_meter: String,
1539 unit: Option<String>,
1540 }
1541 let scaled = self
1542 .db
1543 .prepare(
1544 "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
1545 WHERE m.scale_to_own = 1 AND m.price_meter IS NOT NULL AND m.own_meter IS NOT NULL",
1546 )
1547 .all()
1548 .await?
1549 .results::<Scaled>()?;
1550 let since = day_before(until, MEASURE_DAYS - 1);
1551 let rules = self.rules().await?;
1552 let mut proposed = 0;
1553 for s in scaled {
1554 #[derive(Deserialize)]
1555 struct Day {
1556 product: String,
1557 meter: String,
1558 quantity: f64,
1559 cost_usd: f64,
1560 }
1561 let lines = self
1562 .db
1563 .prepare("SELECT product, meter, quantity, cost_usd FROM cost_lines WHERE source = ?1 AND product = ?2 AND day >= ?3 AND day <= ?4")
1564 .bind(&[SOURCE_BILLABLE.into(), s.product.as_str().into(), since.as_str().into(), until.into()])?
1565 .all()
1566 .await?
1567 .results::<Day>()?;
1568 // Only the lines this very mapping claims.
1569 let mine: Vec<(f64, f64)> = lines
1570 .iter()
1571 .filter(|l| costs::classify(&rules, &l.product, &l.meter).is_some_and(|r| r.product == s.product && r.meter == s.meter))
1572 .map(|l| (l.quantity, l.cost_usd))
1573 .collect();
1574 let Some(rate) = billed_rate(&mine) else { continue };
1575 let cf_units: f64 = mine.iter().map(|(q, _)| q).sum();
1576 #[derive(Deserialize)]
1577 struct Own {
1578 total: Option<f64>,
1579 }
1580 let own_units = self
1581 .db
1582 .prepare("SELECT SUM(quantity) AS total FROM own_counts WHERE meter = ?1 AND day >= ?2 AND day <= ?3")
1583 .bind(&[s.own_meter.as_str().into(), since.as_str().into(), until.into()])?
1584 .first::<Own>(None)
1585 .await?
1586 .and_then(|o| o.total)
1587 .unwrap_or(0.0);
1588 let Some(per_unit) = derived_unit_cost(rate, cf_units, own_units) else { continue };
1589 let size = unit_size(s.unit.as_deref().unwrap_or("1"));
1590 let measured = per_unit * size * 1_000_000.0;
1591 let reason = format!(
1592 "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 {})",
1593 rate * 1000.0,
1594 cf_units / own_units,
1595 crate::features::thousands(cf_units.round() as u64),
1596 crate::features::thousands(own_units.round() as u64)
1597 );
1598 if self.propose(&s.price_meter, measured, &reason, "reconciler").await?.is_some() {
1599 proposed += 1;
1600 }
1601 }
1602 Ok(proposed)
1603 }
1604
1605 /// Opens, updates and closes margin alerts, and emails staff about new
1606 /// ones (and open ones each week).
1607 async fn raise_alerts(&self, env: &Env, until: &str, drift: &[(Drift, String)]) -> Result<u32> {
1608 let settings = self.cost_settings().await?;
1609 let since = day_before(until, u64::from(settings.alert_days.max(1)) - 1);
1610 let days = self.margin_days(&since, until).await?;
1611 let mut conditions: Vec<(String, String, String, String)> = Vec::new();
1612 // Each product under the floor.
1613 let mut by: BTreeMap<String, Vec<(String, i64, i64)>> = BTreeMap::new();
1614 let mut all: BTreeMap<String, (i64, i64)> = BTreeMap::new();
1615 for d in &days {
1616 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 it1617 // What g1t gave away (comped workspaces, free periods, the
1618 // trial and the pools) is a budget it chose to spend, watched on
1619 // 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 daily1620 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 running1621 overall.1 += (d.cost() - d.given.total()).max(0);
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1622 if !OVERHEAD.contains(&d.bucket.as_str()) && d.bucket != UNMAPPED {
1623 by.entry(d.bucket.clone()).or_default().push((d.day.clone(), d.value_micros, d.cost()));
1624 }
1625 }
1626 let floor = settings.margin_floor_percent;
1627 let n = settings.alert_days as usize;
1628 for (bucket, series) in &by {
1629 if let Some((from, worst)) = breach(series, floor, n, settings.min_daily_cost_micros) {
1630 conditions.push((
1631 "margin".into(),
1632 bucket.clone(),
1633 format!("{}: margin under {floor:.0}% for {n} days running, as low as {worst:.1}%.", costs::bucket_title(bucket)),
1634 from,
1635 ));
Margin alerts measure what is sold, and say dollars when a percentage would mislead1636 }
1637 }
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1638 let series: Vec<(String, i64, i64)> = all.into_iter().map(|(day, (revenue, cost))| (day, revenue, cost)).collect();
1639 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 mislead1640 let tail = &series[series.len().saturating_sub(n)..];
1641 let (took, spent) = tail.iter().fold((0i64, 0i64), |(r, c), (_, revenue, cost)| (r + revenue, c + cost));
1642 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 daily1643 }
1644 for (d, detail) in drift {
1645 let kind = if d.kind == DriftKind::Leak { "leak" } else { "drift" };
1646 conditions.push((kind.into(), format!("{}:{}", d.bucket, d.kind.as_str()), detail.clone(), until.to_owned()));
1647 }
1648 // Workspaces costing more than they pay.
1649 for (workspace, cost, revenue) in self.workspace_anomalies(until, &settings).await? {
1650 conditions.push((
1651 "workspace".into(),
1652 workspace.clone(),
Margin alerts measure what is sold, and say dollars when a percentage would mislead1653 format!(
1654 "{workspace} cost g1t {} on Cloudflare over {ANOMALY_DAYS} days, and its usage was priced at {}: its prices are below cost.",
1655 dollars(cost),
1656 dollars(revenue)
1657 ),
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1658 day_before(until, ANOMALY_DAYS - 1),
1659 ));
1660 }
1661
1662 let open = self
1663 .db
1664 .prepare("SELECT * FROM margin_alerts WHERE resolved_at IS NULL")
1665 .all()
1666 .await?
1667 .results::<AlertRow>()?;
1668 let now = now_ms();
1669 let stamp = rfc3339(now);
1670 let mut to_email: Vec<String> = Vec::new();
1671 let mut kept: BTreeSet<String> = BTreeSet::new();
1672 for (kind, subject, detail, from) in &conditions {
1673 match open.iter().find(|a| &a.kind == kind && &a.subject == subject) {
1674 Some(alert) => {
1675 kept.insert(alert.id.clone());
1676 self.db
1677 .prepare("UPDATE margin_alerts SET detail = ? WHERE id = ?")
1678 .bind(&[detail.as_str().into(), alert.id.as_str().into()])?
1679 .run()
1680 .await?;
1681 let stale = alert
1682 .emailed_at
1683 .as_deref()
1684 .and_then(g1t_contracts::time::parse_rfc3339)
1685 .is_none_or(|at| now.saturating_sub(at) >= REMIND_MS);
1686 if stale && kind != "workspace" {
1687 to_email.push(format!("Still open: {detail}"));
1688 kept.insert(format!("email:{}", alert.id));
1689 }
1690 }
1691 None => {
1692 let id = new_id("mal", now);
1693 self.db
1694 .prepare("INSERT INTO margin_alerts (id, kind, subject, detail, since, opened_at) VALUES (?, ?, ?, ?, ?, ?)")
1695 .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()])?
1696 .run()
1697 .await?;
1698 kept.insert(id.clone());
Margin alerts measure what is sold, and say dollars when a percentage would mislead1699 // A workspace's is for Reach out, not the inbox.
1700 if kind != "workspace" {
1701 to_email.push(detail.clone());
1702 }
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1703 kept.insert(format!("email:{id}"));
1704 }
1705 }
1706 }
1707 for alert in &open {
1708 if !kept.contains(&alert.id) {
1709 self.db
1710 .prepare("UPDATE margin_alerts SET resolved_at = ? WHERE id = ?")
1711 .bind(&[stamp.as_str().into(), alert.id.as_str().into()])?
1712 .run()
1713 .await?;
1714 }
1715 }
1716 let to = env.var("COSTS_ALERT_EMAIL").map(|v| v.to_string()).unwrap_or_default();
1717 if !to_email.is_empty() && !to.trim().is_empty() {
1718 let subject = format!("[g1t costs] {} margin alert{}", to_email.len(), if to_email.len() == 1 { "" } else { "s" });
1719 match email_staff(env, to.trim(), &subject, &to_email).await {
1720 Ok(()) => {
1721 for marker in kept.iter().filter_map(|k| k.strip_prefix("email:")) {
1722 self.db
1723 .prepare("UPDATE margin_alerts SET emailed_at = ? WHERE id = ?")
1724 .bind(&[stamp.as_str().into(), marker.into()])?
1725 .run()
1726 .await?;
1727 }
1728 }
1729 Err(error) => worker::console_error!("could not email the margin alerts: {error}"),
1730 }
1731 }
1732 Ok(conditions.len() as u32)
1733 }
1734
1735 /// 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 mislead1736 /// Each day's cost shared out to comped workspaces.
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1737 async fn workspace_anomalies(&self, until: &str, settings: &CostSettings) -> Result<Vec<(String, i64, i64)>> {
1738 #[derive(Deserialize)]
1739 struct Row {
1740 workspace: String,
1741 cost: Option<i64>,
1742 revenue: Option<i64>,
1743 }
1744 let rows = self
1745 .db
1746 .prepare(format!(
Margin alerts measure what is sold, and say dollars when a percentage would mislead1747 // Against what its usage was priced at, not the cash it
1748 // paid: a trial or a gift paying for usage is not a price
1749 // below cost.
The workspace cost alert compares only days that carry their value, not the days before it was kept1750 // Days from before value_micros was kept have none: only days
1751 // since the first one that does are compared.
Margin alerts measure what is sold, and say dollars when a percentage would mislead1752 "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 kept1753 WHERE day >= ?1 AND day <= ?2 AND workspace NOT IN ({})
1754 AND day >= (SELECT MIN(day) FROM workspace_costs WHERE value_micros > 0)
1755 GROUP BY workspace",
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1756 crate::sales::INTERNAL_SQL
1757 ))
1758 .bind(&[day_before(until, ANOMALY_DAYS - 1).into(), until.into()])?
1759 .all()
1760 .await?
1761 .results::<Row>()?;
1762 let rows: Vec<(String, i64, i64)> = rows.into_iter().map(|r| (r.workspace, r.cost.unwrap_or(0), r.revenue.unwrap_or(0))).collect();
1763 Ok(anomalies(&rows, settings.anomaly_factor, settings.anomaly_floor_micros))
1764 }
1765
1766 /// For Reach out: workspaces with an open cost-over-revenue alert,
1767 /// each with its detail and cost.
1768 pub(crate) async fn costing_more_than_they_pay(&self) -> Result<Vec<(String, String, i64)>> {
1769 let alerts = self
1770 .db
1771 .prepare("SELECT * FROM margin_alerts WHERE resolved_at IS NULL AND kind = 'workspace' ORDER BY opened_at DESC LIMIT 50")
1772 .all()
1773 .await?
1774 .results::<AlertRow>()?;
1775 let mut out = Vec::new();
1776 for alert in alerts {
1777 #[derive(Deserialize)]
1778 struct Cost {
1779 cost: Option<i64>,
1780 }
1781 let cost = self
1782 .db
1783 .prepare("SELECT SUM(cost_micros) AS cost FROM workspace_costs WHERE workspace = ? AND day >= ?")
1784 .bind(&[alert.subject.as_str().into(), alert.since.as_str().into()])?
1785 .first::<Cost>(None)
1786 .await?
1787 .and_then(|c| c.cost)
1788 .unwrap_or(0);
1789 out.push((alert.subject, alert.detail, cost));
1790 }
1791 Ok(out)
1792 }
1793
1794 /// `admin_cost_alerts`: what sudo's banner says.
1795 pub(crate) async fn admin_cost_alerts(&self, _: AdminCostAlertsArgs) -> Result<Vec<MarginAlert>> {
1796 Ok(self
1797 .db
1798 .prepare("SELECT * FROM margin_alerts WHERE resolved_at IS NULL ORDER BY opened_at DESC LIMIT 50")
1799 .all()
1800 .await?
1801 .results::<AlertRow>()?
1802 .into_iter()
1803 .map(MarginAlert::from)
1804 .collect())
1805 }
1806
1807 /// `admin_run_costs`: the daily run, now.
1808 pub(crate) async fn admin_run_costs(&self, env: &Env, a: AdminRunCostsArgs) -> Result<Outcome<CostsRun>> {
1809 let keeper = crate::keeper::Keeper::from_env(env);
1810 let run = self.costs_daily(env, &keeper).await?;
1811 if !a.by.is_empty() {
1812 self.audit(
1813 "costs",
1814 "costs_run",
1815 &format!("{} lines, {} days, {} proposals, {} alerts", run.lines, run.days, run.proposals, run.alerts),
1816 &a.by,
1817 )
1818 .await?;
1819 }
1820 Ok(Outcome::Ok(run))
1821 }
1822
1823 /// `admin_set_cost_mapping`.
1824 pub(crate) async fn admin_set_cost_mapping(&self, a: AdminSetCostMappingArgs) -> Result<Outcome<CostMapping>> {
1825 let product = costs::slug(&a.product);
1826 let meter = if a.meter.trim() == "*" { "*".to_owned() } else { costs::slug(&a.meter) };
1827 if product.is_empty() || meter.is_empty() {
1828 return Ok(Outcome::fail(FailureCode::Invalid, "Name Cloudflare's product and a meter (or * for all of it)."));
1829 }
1830 let now = rfc3339(now_ms());
1831 if a.remove {
1832 self.db
1833 .prepare("DELETE FROM cost_map WHERE product = ? AND meter = ?")
1834 .bind(&[product.as_str().into(), meter.as_str().into()])?
1835 .run()
1836 .await?;
1837 self.audit("costs", "cost_mapping_removed", &format!("{product}/{meter}"), &a.by).await?;
1838 return Ok(Outcome::Ok(CostMapping {
1839 product,
1840 meter,
1841 bucket: String::new(),
1842 price_meter: None,
1843 own_meter: None,
1844 scale_to_own: false,
1845 drift_percent: 0.0,
1846 note: String::new(),
1847 updated_at: now,
1848 updated_by: a.by,
1849 }));
1850 }
1851 let bucket = costs::slug(&a.bucket);
1852 if bucket.is_empty() {
1853 return Ok(Outcome::fail(FailureCode::Invalid, "Say which of g1t's products it is a cost of."));
1854 }
1855 let clean = |v: Option<String>| v.map(|v| v.trim().to_owned()).filter(|v| !v.is_empty());
1856 let (price_meter, own_meter) = (clean(a.price_meter), clean(a.own_meter));
1857 let drift = a.drift_percent.filter(|d| d.is_finite() && *d > 0.0).unwrap_or(10.0);
1858 self.db
1859 .prepare(
1860 "INSERT INTO cost_map (product, meter, bucket, price_meter, own_meter, scale_to_own, drift_percent, note, updated_at, updated_by)
1861 VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10)
1862 ON CONFLICT (product, meter) DO UPDATE SET bucket = ?3, price_meter = ?4, own_meter = ?5, scale_to_own = ?6,
1863 drift_percent = ?7, note = ?8, updated_at = ?9, updated_by = ?10",
1864 )
1865 .bind(&[
1866 product.as_str().into(),
1867 meter.as_str().into(),
1868 bucket.as_str().into(),
1869 crate::optional(price_meter.as_deref()),
1870 crate::optional(own_meter.as_deref()),
1871 i32::from(a.scale_to_own).into(),
1872 drift.into(),
1873 a.note.trim().into(),
1874 now.as_str().into(),
1875 a.by.as_str().into(),
1876 ])?
1877 .run()
1878 .await?;
1879 self.audit("costs", "cost_mapping", &format!("{product}/{meter} → {bucket}"), &a.by).await?;
1880 Ok(Outcome::Ok(CostMapping {
1881 product,
1882 meter,
1883 bucket,
1884 price_meter,
1885 own_meter,
1886 scale_to_own: a.scale_to_own,
1887 drift_percent: drift,
1888 note: a.note.trim().to_owned(),
1889 updated_at: now,
1890 updated_by: a.by,
1891 }))
1892 }
1893
1894 /// `admin_costs`: the Costs & margin page.
1895 pub(crate) async fn admin_costs(&self, a: AdminCostsArgs, configured: bool) -> Result<CostsReport> {
1896 let until = rfc3339(now_ms())[..10].to_owned();
1897 let span = u64::from(a.days.unwrap_or(30).clamp(7, 90));
1898 let since = day_before(&until, span - 1);
1899 let days = self.margin_days(&since, &until).await?;
1900 let rules = self.rules().await?;
1901
1902 let mut products: BTreeMap<String, ProductMargin> = BTreeMap::new();
1903 let mut overall = OverallMargin::default();
1904 for d in &days {
1905 let p = products.entry(d.bucket.clone()).or_insert_with(|| ProductMargin {
1906 bucket: d.bucket.clone(),
1907 title: costs::bucket_title(&d.bucket),
1908 cost_source: if NOT_CLOUDFLARE.contains(&d.bucket.as_str()) { "ledger" } else { "cloudflare" }.into(),
1909 overhead: OVERHEAD.contains(&d.bucket.as_str()),
1910 ..ProductMargin::default()
1911 });
1912 p.cf_cost_micros += d.cf_cost_micros;
1913 p.own_cost_micros += d.own_cost_micros;
1914 p.value_micros += d.value_micros;
1915 p.cost_micros += d.cost();
1916 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 running1917 overall.given_micros += d.given.total();
1918 if NOT_CLOUDFLARE.contains(&d.bucket.as_str()) {
1919 overall.models_cost_micros += d.cost();
1920 } else {
1921 overall.cloudflare_cost_micros += d.cost();
1922 }
1923 overall.given_comped_micros += d.given.comped;
1924 overall.given_free_micros += d.given.free;
1925 overall.given_trial_micros += d.given.trial;
1926 overall.given_pool_micros += d.given.pool;
Merge branch 'worktree-agent-a633ac0f7f66d419d'1927 overall.given_discount_micros += d.given.discount;
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging1928 overall.given_credit_promotional_micros += d.given.credit_promotional;
1929 overall.given_credit_goodwill_micros += d.given.credit_goodwill;
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb971930 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 running1931 let sold = (d.cost() - d.given.total()).max(0);
1932 if OVERHEAD.contains(&d.bucket.as_str()) {
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1933 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 running1934 overall.running_cost_micros += sold;
1935 } else if d.bucket == UNMAPPED {
1936 overall.usage_micros += d.cash_micros;
1937 overall.unmapped_cost_micros += sold;
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1938 } else {
1939 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 running1940 overall.usage_cost_micros += sold;
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1941 }
1942 }
1943 for p in products.values_mut() {
1944 p.margin_micros = p.value_micros - p.cost_micros;
1945 p.margin_percent = margin_percent(p.value_micros, p.cost_micros);
1946 }
1947 let revenue = overall.usage_micros + overall.plans_micros;
1948 overall.margin_micros = revenue - overall.cost_micros;
1949 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 it1950 let sold = (overall.cost_micros - overall.given_micros).max(0);
1951 overall.sold_margin_micros = revenue - sold;
1952 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)1953 // The plan's included usage was paid for by the plan's price: it is
1954 // money in for the usage it covered, and out of what the plans
1955 // leave for running g1t.
1956 #[derive(Deserialize)]
1957 struct Included {
1958 micros: Option<i64>,
1959 }
1960 overall.included_micros = self
1961 .db
1962 .prepare(format!(
1963 "SELECT SUM(COALESCE(credit_micros, 0)) AS micros FROM ledger
1964 WHERE kind = 'usage' AND created_at >= ?1 AND created_at <= ?2 AND workspace NOT IN ({})",
1965 crate::sales::INTERNAL_SQL
1966 ))
1967 .bind(&[since.as_str().into(), format!("{until}T23:59:59.999Z").into()])?
1968 .first::<Included>(None)
1969 .await?
1970 .and_then(|r| r.micros)
1971 .unwrap_or(0);
1972 let usage_in = overall.usage_micros + overall.included_micros;
1973 overall.usage_margin_micros = usage_in - overall.usage_cost_micros;
1974 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 charging1975 // Credits from g1t over the range: given, spent, and refunds' money
1976 // given back.
1977 overall.credits_given_micros = self
1978 .db
1979 .prepare("SELECT SUM(amount_micros) AS micros FROM credit_grants WHERE created_at >= ?1 AND created_at <= ?2")
1980 .bind(&[since.as_str().into(), format!("{until}T23:59:59.999Z").into()])?
1981 .first::<Included>(None)
1982 .await?
1983 .and_then(|r| r.micros)
1984 .unwrap_or(0);
1985 let (draws, refunds) = self.credit_effects(&since, &until).await?;
1986 overall.credits_used_micros = draws.iter().map(|(_, d)| d.micros).sum();
1987 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 person1988 // Tax and card fees came in with payments but are neither cash nor
1989 // revenue: balances and plan payments are credited without them
1990 // (tax.rs), so cash above never holds them. Shown apart.
1991 (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 daily1992 let mut products: Vec<ProductMargin> = products.into_values().collect();
1993 products.sort_by_key(|p| std::cmp::Reverse(p.cost_micros.max(p.value_micros)));
1994
1995 #[derive(Deserialize)]
1996 struct DriftRow {
1997 bucket: String,
1998 kind: String,
1999 ours: f64,
2000 cloudflare: f64,
2001 delta_percent: Option<f64>,
2002 detail: String,
2003 found_at: String,
2004 }
2005 let drift = self
2006 .db
2007 .prepare("SELECT * FROM cost_drift ORDER BY kind, bucket")
2008 .all()
2009 .await?
2010 .results::<DriftRow>()?
2011 .into_iter()
2012 .map(|r| CostDrift {
2013 title: costs::bucket_title(&r.bucket),
2014 bucket: r.bucket,
2015 kind: r.kind,
2016 ours: r.ours,
2017 cloudflare: r.cloudflare,
2018 delta_percent: r.delta_percent,
2019 detail: r.detail,
2020 found_at: r.found_at,
2021 })
2022 .collect();
2023
2024 #[derive(Deserialize)]
2025 struct Top {
2026 workspace: String,
2027 cost: Option<i64>,
2028 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 it2029 given: Option<i64>,
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily2030 internal: i64,
2031 }
2032 let top_workspaces = self
2033 .db
2034 .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 it2035 "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 daily2036 CASE WHEN workspace IN ({}) THEN 1 ELSE 0 END AS internal
2037 FROM workspace_costs WHERE day >= ?1 AND day <= ?2 GROUP BY workspace ORDER BY cost DESC LIMIT 15",
2038 crate::sales::INTERNAL_SQL
2039 ))
2040 .bind(&[since.as_str().into(), until.as_str().into()])?
2041 .all()
2042 .await?
2043 .results::<Top>()?
2044 .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 it2045 .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 daily2046 .collect();
2047
2048 #[derive(Deserialize)]
2049 struct Summary {
2050 source: String,
2051 product: String,
2052 meter: String,
2053 raw_name: String,
2054 unit: String,
2055 quantity: f64,
2056 cost_usd: f64,
2057 }
2058 let lines = self
2059 .db
2060 .prepare(
2061 "SELECT source, product, meter, MAX(raw_name) AS raw_name, MAX(unit) AS unit, SUM(quantity) AS quantity, SUM(cost_usd) AS cost_usd
2062 FROM cost_lines WHERE day >= ?1 AND day <= ?2 GROUP BY source, product, meter ORDER BY cost_usd DESC, product, meter LIMIT 200",
2063 )
2064 .bind(&[since.as_str().into(), until.as_str().into()])?
2065 .all()
2066 .await?
2067 .results::<Summary>()?
2068 .into_iter()
2069 .map(|l| CostLineSummary {
2070 bucket: costs::classify(&rules, &l.product, &l.meter).map(|r| r.bucket.clone()),
2071 product: l.product,
2072 meter: l.meter,
2073 raw_name: l.raw_name,
2074 unit: l.unit,
2075 source: l.source,
2076 quantity: l.quantity,
2077 cost_micros: micros(l.cost_usd),
2078 })
2079 .collect();
2080
2081 #[derive(Deserialize)]
2082 struct MapRow {
2083 product: String,
2084 meter: String,
2085 bucket: String,
2086 price_meter: Option<String>,
2087 own_meter: Option<String>,
2088 scale_to_own: i64,
2089 drift_percent: f64,
2090 note: String,
2091 updated_at: String,
2092 updated_by: String,
2093 }
2094 let mappings = self
2095 .db
2096 .prepare("SELECT * FROM cost_map ORDER BY product, meter")
2097 .all()
2098 .await?
2099 .results::<MapRow>()?
2100 .into_iter()
2101 .map(|m| CostMapping {
2102 product: m.product,
2103 meter: m.meter,
2104 bucket: m.bucket,
2105 price_meter: m.price_meter,
2106 own_meter: m.own_meter,
2107 scale_to_own: m.scale_to_own == 1,
2108 drift_percent: m.drift_percent,
2109 note: m.note,
2110 updated_at: m.updated_at,
2111 updated_by: m.updated_by,
2112 })
2113 .collect();
2114
2115 #[derive(Deserialize)]
2116 struct Fetched {
2117 at: Option<String>,
2118 }
2119 let fetched_at = self.db.prepare("SELECT MAX(fetched_at) AS at FROM cost_lines").first::<Fetched>(None).await?.and_then(|f| f.at);
2120
2121 Ok(CostsReport {
2122 configured,
2123 fetched_at,
2124 days: days
2125 .iter()
2126 .map(|d| CostDay {
2127 day: d.day.clone(),
2128 bucket: d.bucket.clone(),
2129 cf_cost_micros: d.cf_cost_micros,
2130 own_cost_micros: d.own_cost_micros,
2131 value_micros: d.value_micros,
2132 cash_micros: d.cash_micros,
2133 })
2134 .collect(),
2135 since,
2136 until,
2137 products,
2138 overall,
2139 drift,
2140 alerts: self.admin_cost_alerts(AdminCostAlertsArgs {}).await?,
2141 proposals: self.proposals().await?,
2142 versions: self.versions().await?,
2143 top_workspaces,
2144 lines,
2145 mappings,
2146 settings: self.cost_settings().await?,
Spend caps: a monthly budget for comped workspaces and a daily breaker on what g1t pays2147 caps: self.spend_caps().await?,
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily2148 })
2149 }
2150}
2151
2152#[cfg(test)]
2153mod tests {
2154 use super::*;
2155
Margin alerts measure what is sold, and say dollars when a percentage would mislead2156 #[test]
Models' margin read -14%: usage nothing paid for is valued at price, not $02157 fn usage_nothing_paid_for_is_valued_at_price_and_paid_usage_at_what_was_paid() {
2158 // A free period: charged nothing, drawn from nothing.
2159 assert_eq!(usage_value(false, 1_000_000, 0, 20), 1_200_000);
2160 // Charged, or drawn from a trial: what was paid.
2161 assert_eq!(usage_value(false, 1_000_000, 1_200_000, 20), 1_200_000);
2162 assert_eq!(usage_value(false, 1_000_000, 900_000, 20), 900_000);
2163 // g1t's own: at price.
2164 assert_eq!(usage_value(true, 1_000_000, 0, 20), 1_200_000);
2165 // No cost, nothing paid: nothing.
2166 assert_eq!(usage_value(false, 0, 0, 20), 0);
2167 }
2168
2169 #[test]
Margin alerts measure what is sold, and say dollars when a percentage would mislead2170 fn the_overall_alert_says_dollars_while_little_comes_in() {
2171 let small = overall_detail(90_000, 7_500_000, 3, 10.0, -8239.7);
2172 assert!(small.contains("took in $0.09 against $7.50"), "{small}");
2173 assert!(!small.contains('%'), "{small}");
2174 let real = overall_detail(30_000_000, 40_000_000, 3, 10.0, -33.3);
2175 assert!(real.contains("as low as -33.3%"), "{real}");
2176 }
2177
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily2178 fn rule(product: &str, meter: &str, bucket: &str, own: Option<&str>) -> Rule {
2179 Rule { product: product.into(), meter: meter.into(), bucket: bucket.into(), price_meter: None, own_meter: own.map(Into::into), drift_percent: 10.0 }
2180 }
2181
2182 fn rules() -> Vec<Rule> {
2183 vec![
2184 rule("containers", "*", "sandboxes", None),
2185 rule("workers", "*", "platform", None),
2186 rule("artifacts", "*", "git", Some("git_operations")),
2187 rule("artifacts", "events_", "git", Some("git_operations")),
2188 ]
2189 }
2190
2191 fn revenue_map() -> BTreeMap<String, String> {
2192 [("sandbox", "sandboxes"), ("git", "git"), ("plan", "platform")].iter().map(|(k, v)| (k.to_string(), v.to_string())).collect()
2193 }
2194
2195 fn line(day: &str, source: &str, product: &str, meter: &str, quantity: f64, cost: f64) -> LineRow {
2196 LineRow { day: day.into(), source: source.into(), product: product.into(), meter: meter.into(), quantity, cost_usd: cost }
2197 }
2198
2199 fn usage(day: &str, workspace: &str, key: &str, value: i64, cash: i64, cost: i64) -> UsageRow {
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb972200 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 daily2201 }
2202
2203 #[test]
2204 fn a_day_puts_the_bill_g1ts_counts_and_charges_side_by_side() {
2205 let lines = vec![
2206 line("2026-10-15", SOURCE_BILLABLE, "containers", "container_memory", 1000.0, 2.00),
2207 line("2026-10-15", SOURCE_BILLABLE, "artifacts", "artifacts_operations", 30_000.0, 3.00),
2208 // Artifacts' own events: not used while the bill has a count.
2209 line("2026-10-15", SOURCE_ARTIFACTS, "artifacts", "events_pull", 29_000.0, 0.0),
2210 line("2026-10-15", SOURCE_BILLABLE, "workers", "workers_cpu_ms", 1.0, 0.50),
2211 line("2026-10-15", SOURCE_BILLABLE, "browser_rendering", "browser_hours", 2.0, 0.25),
2212 ];
2213 let own = vec![
2214 OwnRow { day: "2026-10-15".into(), meter: "git_operations".into(), workspace: "acme".into(), quantity: 7_500.0 },
2215 OwnRow { day: "2026-10-15".into(), meter: "git_operations".into(), workspace: "beta".into(), quantity: 2_500.0 },
2216 ];
2217 let usage = vec![
2218 usage("2026-10-15", "acme", "sandbox", 2_400_000, 1_000_000, 2_000_000),
2219 usage("2026-10-15", "beta", "sandbox", 1_200_000, 1_200_000, 1_000_000),
2220 usage("2026-10-15", "acme", "git", 600_000, 600_000, 500_000),
2221 usage("2026-10-15", "acme", "implement", 120_000, 120_000, 100_000),
2222 usage("2026-10-15", "beta", "plan", 20_000_000, 20_000_000, 0),
2223 ];
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 running2224 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 daily2225 let get = |bucket: &str| days.iter().find(|d| d.bucket == bucket).unwrap();
2226 let sandboxes = get("sandboxes");
2227 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));
2228 let git = get("git");
2229 assert_eq!(git.cf_cost_micros, 3_000_000);
2230 assert_eq!((git.cf_quantity, git.own_quantity), (30_000.0, 10_000.0));
2231 assert_eq!(get("platform").value_micros, 20_000_000);
2232 // Not mapped: a leak until someone maps it.
2233 assert_eq!(get(UNMAPPED).cf_cost_micros, 250_000);
2234 // Models: no Cloudflare line, their cost is g1t's own.
2235 assert_eq!(get("models").cost(), 100_000);
2236 // Git's cost shared by g1t's own counts (Cloudflare gave none per
2237 // workspace here): three quarters to acme.
2238 let share = |ws: &str, bucket: &str| workspaces.iter().find(|w| w.workspace == ws && w.bucket == bucket).map(|w| (w.cost, w.revenue));
2239 assert_eq!(share("acme", "git"), Some((2_250_000, 600_000)));
2240 assert_eq!(share("beta", "git"), Some((750_000, 0)));
2241 // Every bucket's cost is shared out exactly.
2242 for d in &days {
2243 let shared: i64 = workspaces.iter().filter(|w| w.bucket == d.bucket).map(|w| w.cost).sum();
2244 assert_eq!(shared, d.cost(), "{}", d.bucket);
2245 }
2246 }
2247
2248 #[test]
2249 fn artifacts_events_count_when_the_bill_does_not() {
2250 let lines = vec![
2251 line("2026-10-05", SOURCE_ARTIFACTS, "artifacts", "events_pull", 120.0, 0.0),
2252 line("2026-10-05", SOURCE_ARTIFACTS, "artifacts", "events_push", 30.0, 0.0),
2253 line("2026-10-05", SOURCE_ARTIFACTS, "artifacts", "events_ratelimited", 9.0, 0.0),
2254 ];
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 running2255 let (days, _) = fold(&rules(), &revenue_map(), &lines, &[], &[], &BTreeSet::new());
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily2256 assert_eq!(days[0].cf_quantity, 150.0);
2257 assert_eq!(days[0].cf_cost_micros, 0);
2258 }
2259
2260 #[test]
2261 fn month_end_meters_are_told_by_the_day_from_snapshots() {
2262 let snap = |day: &str, cost: i64, charge: i64| (day.to_string(), "acme".to_string(), "git".to_string(), cost, charge);
2263 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)]);
2264 assert_eq!(
2265 rows.iter().map(|r| (r.day.as_str(), r.cost, r.value)).collect::<Vec<_>>(),
2266 vec![("2026-10-30", 100, 120), ("2026-10-31", 150, 180), ("2026-11-01", 40, 48)]
2267 );
2268 }
2269
2270 #[test]
2271 fn a_plan_payment_is_spread_over_the_month_it_pays_for() {
2272 let days = spread("2026-10-01T00:00:00.000Z", 20_000_000, 30);
2273 assert_eq!(days.len(), 30);
2274 assert_eq!(days[0], ("2026-10-01".to_string(), 666_667));
2275 assert_eq!(days[29], ("2026-10-30".to_string(), 666_666));
2276 assert_eq!(days.iter().map(|d| d.1).sum::<i64>(), 20_000_000);
2277 assert!(spread("2026-10-01", 0, 30).is_empty());
2278 assert_eq!(dollars(17_024_000), "$17.02");
2279 assert_eq!(dollars(-27_668_620), "-$27.67");
2280 assert_eq!(dollars(63_000), "$0.063");
2281 }
2282
2283 #[test]
2284 fn margins_and_deltas() {
2285 assert_eq!(margin_percent(1_200_000, 1_000_000).map(|m| (m * 100.0).round() / 100.0), Some(16.67));
2286 assert_eq!(margin_percent(0, 5), None);
2287 assert_eq!(delta_percent(110.0, 100.0), Some(10.0));
2288 assert_eq!(delta_percent(1.0, 0.0), None);
2289 }
2290
2291 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 running2292 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 daily2293 }
2294
2295 #[test]
2296 fn counts_more_than_the_threshold_apart_are_drift() {
2297 // Cloudflare counted 30,000 operations where g1t counted 10,000:
2298 // binding reads, perhaps. -66.7%.
2299 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);
2300 assert_eq!(drift.iter().map(|d| d.kind).collect::<Vec<_>>(), vec![DriftKind::Count, DriftKind::Cost]);
2301 assert!((drift[0].delta_percent.unwrap() + 66.666).abs() < 0.01);
2302 // 9% apart: within 10%.
2303 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());
2304 // Uncounted products have no count drift.
2305 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());
2306 }
2307
2308 #[test]
2309 fn cost_with_no_revenue_is_a_leak_but_not_for_running_g1t() {
2310 let leak = drifts("actions_cache", &[day("actions_cache", 400_000, 0, 0, 0.0, 0.0)], 10.0, false, 100_000);
2311 assert_eq!(leak.len(), 1);
2312 assert_eq!(leak[0].kind, DriftKind::Leak);
2313 assert!(drifts("platform", &[day("platform", 5_000_000, 0, 0, 0.0, 0.0)], 10.0, false, 100_000).is_empty());
2314 // Pennies say nothing.
2315 assert!(drifts("actions_cache", &[day("actions_cache", 50_000, 0, 0, 0.0, 0.0)], 10.0, false, 100_000).is_empty());
2316 assert!(drifts(UNMAPPED, &[day(UNMAPPED, 250_000, 0, 0, 0.0, 0.0)], 10.0, false, 100_000)[0].kind == DriftKind::Leak);
2317 }
2318
2319 #[test]
2320 fn a_margin_alert_needs_n_days_in_a_row_under_the_floor() {
2321 let s = |d: &str, revenue: i64, cost: i64| (d.to_string(), revenue, cost);
2322 // 5%, 0%, -20%: three days under 10%.
2323 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)];
2324 let (from, worst) = breach(&series, 10.0, 3, 100_000).unwrap();
2325 assert_eq!(from, "10-14");
2326 assert!((worst + 20.0).abs() < 1e-9);
2327 // A good day in the window clears it.
2328 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)];
2329 assert!(breach(&mended, 10.0, 3, 100_000).is_none());
2330 // Cost with no revenue at all is the worst margin there is.
2331 assert_eq!(breach(&[s("10-16", 0, 500_000)], 10.0, 1, 100_000).unwrap().1, -100.0);
2332 // Too little cost to judge.
2333 assert!(breach(&[s("10-16", 0, 5_000)], 10.0, 1, 100_000).is_none());
2334 assert!(breach(&series, 10.0, 9, 100_000).is_none());
2335 }
2336
2337 #[test]
2338 fn shared_costs_add_up_to_the_bill() {
2339 let w = |k: &str, v: f64| (k.to_string(), v);
2340 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)]);
2341 assert_eq!(attribute(10, &[w("a", 3.0), w("b", 1.0), w("a", 0.0)]), vec![("a".into(), 8), ("b".into(), 2)]);
2342 assert!(attribute(10, &[w("a", 0.0)]).is_empty());
2343 assert!(attribute(0, &[w("a", 1.0)]).is_empty());
2344 }
2345
2346 #[test]
Costs: counts are compared with Cloudflare's from the day g1t started counting, so days before a meter existed are not drift2347 fn counts_are_compared_from_the_day_g1t_started_counting() {
2348 let on = |day: &str, cf: f64, own: f64| ProductDay { day: day.into(), bucket: "git".into(), cf_quantity: cf, own_quantity: own, ..ProductDay::default() };
2349 // Five days of Cloudflare's count before g1t's meter, then two that match.
2350 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)];
2351 assert!(drifts("git", &days, 10.0, true, 0).iter().all(|d| d.kind != DriftKind::Count));
2352 // A real gap on the days both counted still shows.
2353 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)];
2354 let found = drifts("git", &days, 10.0, true, 0);
2355 let count = found.iter().find(|d| d.kind == DriftKind::Count).unwrap();
2356 assert_eq!((count.ours, count.cloudflare), (531.0, 1000.0));
2357 // A meter that never counted is compared over every day.
2358 let days = vec![on("2026-10-06", 400.0, 0.0)];
2359 assert!(drifts("git", &days, 10.0, true, 0).iter().any(|d| d.kind == DriftKind::Count));
2360 }
2361
2362 #[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 it2363 fn what_g1t_gives_away_is_kept_apart_from_what_it_sells() {
2364 let map = BTreeMap::new();
2365 // A comped workspace (all of it given), one in its trial (half paid
2366 // 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 running2367 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 it2368 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 running2369 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 it2370 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 running2371 // Nothing priced that day: free use.
2372 let free = usage("2026-10-15", "gamma", "agent", 0, 0, 1_000_000);
2373 let internal = BTreeSet::from(["flagon".to_string()]);
2374 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 it2375 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 running2376 assert_eq!(models.cost(), 4_000_000);
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging2377 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 running2378 let given = |w: &str| workspaces.iter().find(|x| x.workspace == w).unwrap().given.total();
2379 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 it2380 }
2381
2382 #[test]
Merge branch 'worktree-agent-a633ac0f7f66d419d'2383 fn a_discounted_sale_keeps_its_margin_and_counts_the_discount_as_given() {
2384 // $1 of model cost at 20%, sold to an account with 30% off: charged
2385 // $0.84, and $0.36 below cost plus the margin given (as usage_rows
2386 // reads the ledger: value at price, the discount part given).
2387 let mut sale = usage("2026-10-15", "acme", "agent", 1_200_000, 840_000, 1_000_000);
2388 sale.given = Given { discount: 360_000, ..Given::default() };
2389 let (days, _) = fold(&[], &BTreeMap::new(), &[], &[], &[sale], &BTreeSet::new());
2390 let models = days.iter().find(|d| d.bucket == "models").unwrap();
2391 assert_eq!(models.value_micros, 1_200_000);
2392 assert_eq!(models.given, Given { discount: 300_000, ..Given::default() });
2393 // What was sold (cost less given) still makes the margin.
2394 let sold = models.cost() - models.given.total();
2395 assert_eq!(margin_percent(models.cash_micros, sold).map(|m| m.round()), Some(17.0));
2396 }
2397
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging2398 fn draw(kind: CreditKind, reference: &str, task: Option<&str>, at: &str, micros: i64) -> (String, crate::grants::Draw) {
2399 let draw = crate::grants::Draw { grant: "crd_a".into(), kind, reference: reference.into(), task: task.map(Into::into), at: at.into(), micros };
2400 ("acme".to_owned(), draw)
2401 }
2402
2403 #[test]
2404 fn usage_paid_for_with_credit_is_given_not_money_in() {
2405 // $1.20 of usage on $1 of cost, all of it paid with promotional credit.
2406 let mut rows = vec![usage("2026-10-15", "acme", "implement", 1_200_000, 1_200_000, 1_000_000)];
2407 apply_credits(&mut rows, &[draw(CreditKind::Promotional, "run_1", Some("implement"), "2026-10-15T10:00:00Z", 1_200_000)], &[]);
2408 assert_eq!(rows[0].cash, 0);
2409 assert_eq!(rows[0].given, Given { credit_promotional: 1_200_000, ..Given::default() });
2410 let (days, workspaces) = fold(&[], &BTreeMap::new(), &[], &[], &rows, &BTreeSet::new());
2411 let models = days.iter().find(|d| d.bucket == "models").unwrap();
2412 // Valued at its price, none of it money in, all of its cost given:
2413 // the margin on what was sold is untouched by it.
2414 assert_eq!((models.value_micros, models.cash_micros), (1_200_000, 0));
2415 assert_eq!(models.given, Given { credit_promotional: 1_000_000, ..Given::default() });
2416 assert_eq!(models.cost() - models.given.total(), 0);
2417 assert_eq!(workspaces[0].given.total(), 1_000_000);
2418 // Half paid with goodwill credit: half the cost given, half sold.
2419 let mut rows = vec![usage("2026-10-15", "acme", "implement", 1_200_000, 1_200_000, 1_000_000)];
2420 apply_credits(&mut rows, &[draw(CreditKind::Goodwill, "run_1", Some("implement"), "2026-10-15T10:00:00Z", 600_000)], &[]);
2421 let (days, _) = fold(&[], &BTreeMap::new(), &[], &[], &rows, &BTreeSet::new());
2422 let models = days.iter().find(|d| d.bucket == "models").unwrap();
2423 assert_eq!(models.cash_micros, 600_000);
2424 assert_eq!(models.given, Given { credit_goodwill: 500_000, ..Given::default() });
2425 let sold = models.cost() - models.given.total();
2426 assert_eq!(margin_percent(models.cash_micros, sold).map(|m| m.round()), Some(17.0));
2427 }
2428
2429 #[test]
2430 fn what_a_refund_pays_for_is_paid_for_and_the_refund_comes_off_its_day() {
2431 // A refund's credit pays for usage: still money in, nothing given.
2432 let mut rows = vec![usage("2026-10-15", "acme", "implement", 1_200_000, 1_200_000, 1_000_000)];
2433 apply_credits(&mut rows, &[draw(CreditKind::Refund, "run_9", Some("implement"), "2026-10-15T10:00:00Z", 1_200_000)], &[]);
2434 assert_eq!((rows[0].cash, rows[0].given), (1_200_000, Given::default()));
2435 // The $3 refunded for Oct 2 comes off that day's money in, shared
2436 // over what was paid that day.
2437 let mut rows = vec![
2438 usage("2026-10-02", "acme", "implement", 4_000_000, 4_000_000, 3_000_000),
2439 usage("2026-10-02", "acme", "sandbox", 2_000_000, 2_000_000, 1_500_000),
2440 usage("2026-10-02", "beta", "implement", 9_000_000, 9_000_000, 7_000_000),
2441 ];
2442 let refund = crate::grants::Refunded { workspace: "acme".into(), day: "2026-10-02".into(), micros: 3_000_000 };
2443 apply_credits(&mut rows, &[], std::slice::from_ref(&refund));
2444 assert_eq!((rows[0].cash, rows[1].cash, rows[2].cash), (2_000_000, 1_000_000, 9_000_000));
2445 assert!(rows.iter().all(|r| r.given == Given::default()));
2446 // Nothing paid that day: a line of its own, money in less than nothing.
2447 let mut rows = vec![];
2448 apply_credits(&mut rows, &[], &[refund]);
2449 assert_eq!((rows[0].key.as_str(), rows[0].cash, rows[0].value), ("other", -3_000_000, 0));
2450 }
2451
2452 #[test]
2453 fn credit_spent_on_month_end_meters_is_a_line_of_its_own() {
2454 // Storage is reconciled from snapshots, not its ledger line: what
2455 // credit paid of it is its own row on the day it was charged.
2456 let mut rows = vec![usage("2026-10-01", "acme", "implement", 1_000, 1_000, 800)];
2457 apply_credits(&mut rows, &[draw(CreditKind::Goodwill, "storage/2026-09", Some("storage"), "2026-10-01T00:05:00Z", 2_000_000)], &[]);
2458 assert_eq!(rows.len(), 2);
2459 assert_eq!((rows[1].key.as_str(), rows[1].cash, rows[1].value), ("storage", -2_000_000, 0));
2460 assert_eq!(rows[1].given.credit_goodwill, 2_000_000);
2461 assert_eq!(rows[0].cash, 1_000);
2462 }
2463
Merge branch 'worktree-agent-a633ac0f7f66d419d'2464 #[test]
2465 fn the_gateways_total_against_the_ledgers_model_cost_is_drift() {
2466 // The gateway priced $5 of g1t's own traffic; the ledger has $3.
2467 let short = drifts("models", &[day("models", 5_000_000, 3_000_000, 3_600_000, 0.0, 0.0)], 10.0, false, 100_000);
2468 assert_eq!(short.iter().map(|d| d.kind).collect::<Vec<_>>(), vec![DriftKind::Cost]);
2469 assert!((short[0].delta_percent.unwrap() + 40.0).abs() < 1e-9);
2470 // Gateway traffic with nothing on the ledger at all: cost drift and a leak.
2471 let none = drifts("models", &[day("models", 2_000_000, 0, 0, 0.0, 0.0)], 10.0, false, 100_000);
2472 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 said2473 // Within the threshold: nothing.
Merge branch 'worktree-agent-a633ac0f7f66d419d'2474 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 said2475 // The gateway priced nothing against a ledger that has model cost:
2476 // not agreement (a token that cannot see AI Gateway reads as no
2477 // rows), so it is said. Under the minimum, or no model cost: nothing.
2478 let silent = drifts("models", &[day("models", 0, 1_000_000, 1_200_000, 0.0, 0.0)], 10.0, false, 100_000);
2479 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-a69aeabc4b0deeb972480 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 said2481 assert!(said.contains("$1.00") && said.contains("priced nothing") && said.contains("AI Gateway: Read"), "{said}");
2482 assert!(drifts("models", &[day("models", 0, 50_000, 60_000, 0.0, 0.0)], 10.0, false, 100_000).is_empty());
2483 assert!(drifts("models", &[day("models", 0, 0, 0, 0.0, 0.0)], 10.0, false, 100_000).is_empty());
Merge branch 'worktree-agent-a633ac0f7f66d419d'2484 // The detail says which way and why it may be off.
2485 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-a69aeabc4b0deeb972486 let detail = models_detail(&short[0], &caveats, &[]);
Merge branch 'worktree-agent-a633ac0f7f66d419d'2487 assert!(detail.contains("$5.00") && detail.contains("$3.00") && detail.contains("were not charged"), "{detail}");
2488 assert!(detail.contains("3,000,000 prompt-cache read") && detail.contains("no price for anthropic_claude_new_1"), "{detail}");
2489 }
2490
2491 #[test]
2492 fn model_usage_the_gateway_cannot_price_is_drift_even_when_the_totals_agree() {
2493 assert!(unpriced_drift(&costs::GatewayCaveats::default()).is_none());
2494 // Cache tokens alone are a note on the cost drift, not drift.
2495 assert!(unpriced_drift(&costs::GatewayCaveats { cache_write_tokens: 10.0, ..Default::default() }).is_none());
2496 let (drift, detail) = unpriced_drift(&costs::GatewayCaveats { unpriced: vec!["anthropic_claude_new_1".into()], short_runs: 2, ..Default::default() }).unwrap();
2497 assert_eq!((drift.bucket.as_str(), drift.kind.as_str()), ("models", "unpriced"));
2498 assert!(detail.contains("no price for anthropic_claude_new_1") && detail.contains("2 runs were settled"), "{detail}");
2499 }
2500
2501 #[test]
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily2502 fn a_workspace_that_costs_more_than_it_pays_is_flagged() {
2503 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)];
2504 let found = anomalies(&rows, 1.0, 1_000_000);
2505 assert_eq!(found, vec![("acme".to_string(), 5_000_000, 1_000_000)]);
2506 // At twice its revenue as the threshold, $5 against $3 is fine.
2507 assert!(anomalies(&[("acme".to_string(), 5_000_000, 3_000_000)], 2.0, 1_000_000).is_empty());
2508 }
2509
2510 #[test]
2511 fn a_git_operation_costs_what_cloudflare_counts_for_it() {
2512 // $0.15 per 1,000 of Cloudflare's operations, on the charged days.
2513 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();
2514 assert!((rate - 0.000_15).abs() < 1e-12);
2515 // Cloudflare counted 3 for every 1 g1t did: binding reads count.
2516 let per_op = derived_unit_cost(rate, 300_000.0, 100_000.0).unwrap();
2517 let per_thousand_micros = per_op * unit_size("1,000 operations") * 1e6;
2518 assert!((per_thousand_micros - 450_000.0).abs() < 1e-6, "{per_thousand_micros}");
2519 // Too few of g1t's units to say.
2520 assert!(derived_unit_cost(rate, 3_000.0, 500.0).is_none());
2521 assert!(billed_rate(&[(10_000.0, 0.0)]).is_none());
2522 assert_eq!(unit_size("million requests"), 1e6);
2523 assert_eq!(unit_size("second"), 1.0);
2524 }
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb972525
2526 /// The case that started it: syntaqx's ~$8.62 of model usage was wiped
2527 /// by a testing reset, AI Gateway still priced all $11.11, and the
2528 /// ledger had $2.49 left.
2529 fn gateway_and_ledger(kept: bool) -> (Vec<ProductDay>, Vec<WorkspaceDay>) {
2530 let rules = vec![rule("ai_gateway_requests", "*", "models", None), rule("containers", "*", "sandboxes", None)];
2531 let lines = vec![
2532 line("2026-10-05", costs::SOURCE_GATEWAY, "ai_gateway_requests", "anthropic_claude_opus_5_5", 1.0, 11.11),
2533 line("2026-10-05", SOURCE_BILLABLE, "containers", "container_memory", 10.0, 0.30),
2534 ];
2535 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)];
2536 if kept {
2537 // What the reset kept (reset_costs), read back for the day.
2538 usage.extend(reset_usage(&[
2539 ("2026-10-05".into(), "syntaqx".into(), "models".into(), 8_620_000, 10_344_000),
2540 ("2026-10-05".into(), "syntaqx".into(), "sandboxes".into(), 100_000, 120_000),
2541 // The reset's own row is not usage.
2542 ("2026-10-07".into(), "syntaqx".into(), String::new(), 0, 0),
2543 ]));
2544 }
2545 fold(&rules, &revenue_map(), &lines, &[], &usage, &BTreeSet::new())
2546 }
2547
2548 #[test]
2549 fn what_a_reset_kept_is_on_the_ledgers_side_of_the_models_drift() {
2550 let models = |days: &[ProductDay]| days.iter().find(|d| d.bucket == "models").cloned().unwrap();
2551 // Without it: AI Gateway's $11.11 against the ledger's $2.49.
2552 let (days, _) = gateway_and_ledger(false);
2553 let drift = drifts("models", &[models(&days)], 10.0, false, 100_000);
2554 assert_eq!(drift.iter().map(|d| d.kind).collect::<Vec<_>>(), vec![DriftKind::Cost]);
2555 assert_eq!((drift[0].ours, drift[0].cloudflare), (2_490_000.0, 11_110_000.0));
2556 // With it: the ledger's model cost and the reset's add up to the gateway's.
2557 let (days, _) = gateway_and_ledger(true);
2558 let m = models(&days);
2559 assert_eq!(m.own_cost_micros, 11_110_000);
2560 assert!(drifts("models", &[m], 10.0, false, 100_000).is_empty());
2561 // The reset's own row makes no bucket of its own.
2562 assert!(!days.iter().any(|d| d.bucket.is_empty()));
2563 }
2564
2565 #[test]
2566 fn what_a_reset_kept_is_given_away_as_testing_resets() {
2567 let (days, workspaces) = gateway_and_ledger(true);
2568 let models = days.iter().find(|d| d.bucket == "models").unwrap();
2569 // All of syntaqx's model cost is given, none of it money in.
2570 assert_eq!(models.given, Given { reset: 8_620_000, ..Given::default() });
2571 assert_eq!(models.cash_micros, 2_988_000);
2572 // Cloudflare's sandbox cost is shared by what each workspace's usage
2573 // cost: syntaqx's half is given too.
2574 let sandboxes = days.iter().find(|d| d.bucket == "sandboxes").unwrap();
2575 assert_eq!((sandboxes.cost(), sandboxes.given.reset), (300_000, 150_000));
2576 // Who g1t paid: syntaqx is still on it, all of its cost given.
2577 let syntaqx: Vec<&WorkspaceDay> = workspaces.iter().filter(|w| w.workspace == "syntaqx").collect();
2578 assert_eq!(syntaqx.iter().map(|w| w.cost).sum::<i64>(), 8_770_000);
2579 assert!(syntaqx.iter().all(|w| w.given.reset == w.cost && w.given.total() == w.cost && w.revenue == 0));
2580 // The statement reads it back from margin_days by why.
2581 let row = MarginRow {
2582 day: models.day.clone(),
2583 bucket: models.bucket.clone(),
2584 cf_cost_micros: models.cf_cost_micros,
2585 own_cost_micros: models.own_cost_micros,
2586 value_micros: models.value_micros,
2587 cash_micros: models.cash_micros,
2588 cf_quantity: 0.0,
2589 own_quantity: 0.0,
2590 given_comped_micros: Some(0),
2591 given_free_micros: Some(0),
2592 given_trial_micros: Some(0),
2593 given_pool_micros: Some(0),
2594 given_discount_micros: Some(0),
2595 given_credit_promotional_micros: Some(0),
2596 given_credit_goodwill_micros: Some(0),
2597 given_reset_micros: Some(models.given.reset),
2598 };
2599 assert_eq!(ProductDay::from(row).given, models.given);
2600 }
2601
2602 #[test]
2603 fn reconciling_again_gives_the_same_answer() {
2604 assert_eq!(gateway_and_ledger(true), gateway_and_ledger(true));
2605 // A reset's kept rows are read back exactly as kept: running it
2606 // again cannot count them twice.
2607 let kept = [("2026-10-05".to_string(), "syntaqx".to_string(), "models".to_string(), 8_620_000, 10_344_000)];
2608 assert_eq!(reset_usage(&kept), reset_usage(&kept));
2609 assert_eq!(reset_usage(&kept).len(), 1);
2610 }
2611
2612 #[test]
2613 fn a_reset_from_before_resets_kept_their_cost_is_said_not_called_a_leak() {
2614 let notes = reset_notes(
2615 &[("ws_syntaqx".into(), "2026-10-07T09:41:00.000Z".into()), ("ws_acme".into(), "2026-10-08T01:00:00.000Z".into())],
2616 &[("acme".into(), "2026-10-08T01:00:00.000Z".into(), 1_500_000)],
2617 );
2618 assert_eq!(
2619 notes,
2620 vec![
2621 ResetNote { workspace: "syntaqx".into(), day: "2026-10-07".into(), recorded: false, models_micros: 0 },
2622 ResetNote { workspace: "acme".into(), day: "2026-10-08".into(), recorded: true, models_micros: 1_500_000 },
2623 ]
2624 );
2625 let drift = Drift { bucket: "models".into(), kind: DriftKind::Cost, ours: 2_490_000.0, cloudflare: 11_110_000.0, delta_percent: Some(-77.6) };
2626 let detail = models_detail(&drift, &costs::GatewayCaveats::default(), &notes);
2627 assert!(detail.contains("includes model usage wiped by a testing reset of syntaqx on 2026-10-07"), "{detail}");
2628 assert!(detail.contains("not a leak") && detail.contains("leaves the 7 days on 2026-10-14"), "{detail}");
2629 assert!(!detail.contains("a gap that stays is a leak"), "{detail}");
2630 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}");
2631 // The models leak is not raised while such a reset is in the window.
2632 let leak = Drift { bucket: "models".into(), kind: DriftKind::Leak, ours: 0.0, cloudflare: 11_110_000.0, delta_percent: None };
2633 assert!(wiped_not_leaked(&leak, &notes));
2634 assert!(!wiped_not_leaked(&leak, &notes[1..]));
2635 assert!(!wiped_not_leaked(&Drift { bucket: "actions_cache".into(), ..leak }, &notes));
2636 // No reset: the detail is as before.
2637 assert!(models_detail(&drift, &costs::GatewayCaveats::default(), &[]).contains("a gap that stays is a leak"));
2638 }
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily2639}

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