Skip to content
2,716 linesCodeBlameRaw

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

Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1//! What g1t earns on each thing it sells, measured against what
2//! Cloudflare actually charged for it.
3//!
4//! Once a day, after `costs` has read Cloudflare's bill, the reconciler
5//! puts three figures side by side for every day and each of g1t's
6//! products (a "bucket": sandboxes, deployments, git, repository storage,
7//! …):
8//!
9//! 1. **What Cloudflare charged**: the day's cost lines `cost_map` gives
10//! the bucket.
11//! 2. **What g1t's meters recorded**: the cost on the ledger's entries for
12//! it (the price book's cost at the time) and, where a mapping names
13//! one, g1t's own count of the same units (git operations).
14//! 3. **What customers were charged**: the entries' value at price, before
15//! the plan's included usage, a trial or a pool paid part of it; and of
16//! that, what workspaces paid. Month-end meters (git, storage, scans,
17//! embeddings, the actions cache) come from daily snapshots of what they
18//! had come to (`pending_days`). The plan's price is the `platform`
19//! bucket's: the plan pays for running g1t.
20//!
21//! From those: margin per product (value against cost) and for all of g1t
22//! (money in against every cost); drift (counts or costs that disagree past
23//! a mapping's threshold, and leaks: cost with no revenue, or a Cloudflare
24//! meter no one mapped); each workspace's cost, Cloudflare's figure shared
25//! out by each workspace's own meters; and price proposals when a unit's
26//! real cost has moved (`pricing`). Alerts go to staff by email and as a
27//! banner in sudo. See docs/BILLING_OPERATIONS.md.
28
29use std::collections::{BTreeMap, BTreeSet};
30
31use g1t_contracts::billing::*;
32use g1t_contracts::{FailureCode, Outcome, new_id};
33use g1t_contracts::time::rfc3339;
34use g1t_kit::now_ms;
35use serde::{Deserialize, Serialize};
36use worker::wasm_bindgen::JsValue;
37use worker::{Env, Result};
38
39use crate::Billing;
40use crate::costs::{self, ARTIFACTS_OPERATIONS, DAY_MS, Rule, SOURCE_ARTIFACTS, SOURCE_BILLABLE, UNMAPPED};
41
42/// Buckets that are the cost of running g1t, paid by the plan rather than
43/// sold by the unit: never a leak for having no revenue of their own.
44pub(crate) const OVERHEAD: [&str; 1] = ["platform"];
45/// Buckets Cloudflare does not bill: their cost is g1t's own figure.
46pub(crate) const NOT_CLOUDFLARE: [&str; 1] = ["models"];
Merge costs and margin review: gateway query, own spend, discount meters, superseded rises47/// The key an agent's planning run is reconciled under. Its ledger task
48/// is `plan`, which is also the plan's payments' key (`revenue_map`: the
49/// platform bucket), so read as `plan` its model cost went to running g1t,
50/// where Cloudflare's bill is the cost, and was lost. No `revenue_map`
51/// row: models, like every agent run.
52pub(crate) const PLANNING_KEY: &str = "planning";
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily53/// The days drift is judged over.
54const DRIFT_DAYS: u64 = 7;
55/// The days a workspace's cost is set against its revenue.
56const ANOMALY_DAYS: u64 = 30;
57/// The days a unit's cost is measured over.
58const MEASURE_DAYS: u64 = 30;
59/// Fewer of g1t's units than this say nothing about cost per unit.
60const MIN_UNITS: f64 = 1_000.0;
61/// An open alert is emailed again after this long.
62const REMIND_MS: u64 = 7 * DAY_MS;
63
64// ---------------------------------------------------------------------
65// The arithmetic, apart from the database so it can be tested.
66// ---------------------------------------------------------------------
67
68/// One of g1t's products on one day.
69#[derive(Clone, Debug, Default, PartialEq)]
70pub(crate) struct ProductDay {
71 pub day: String,
72 pub bucket: String,
73 /// What Cloudflare charged g1t, in millionths of a dollar.
74 pub cf_cost_micros: i64,
75 /// What g1t's meters recorded it cost (the price book's cost).
76 pub own_cost_micros: i64,
77 /// What customers were charged for it at price, before what paid.
78 pub value_micros: i64,
79 /// Of that, what workspaces paid themselves.
80 pub cash_micros: i64,
81 /// Units Cloudflare counted and units g1t counted, where a mapping
82 /// says they are the same units.
83 pub cf_quantity: f64,
84 pub own_quantity: f64,
Costs: margin is measured on what was sold; comped workspaces, free periods, the trial and the pools are given away, a budget shown beside it85 /// Of `cost()`, what went on usage g1t gave away (the workspaces'
86 /// `WorkspaceDay::given`, added up).
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running87 pub given: Given,
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily88}
89
90impl ProductDay {
91 /// What it cost: Cloudflare's figure where Cloudflare bills it, else
92 /// g1t's own (models are billed by their providers, through the gateway).
93 pub fn cost(&self) -> i64 {
94 if NOT_CLOUDFLARE.contains(&self.bucket.as_str()) { self.own_cost_micros } else { self.cf_cost_micros }
95 }
96}
97
98/// A line of Cloudflare's bill, as stored.
99#[derive(Clone, Debug, Deserialize)]
100pub(crate) struct LineRow {
101 pub day: String,
102 pub source: String,
103 pub product: String,
104 pub meter: String,
105 pub quantity: f64,
106 pub cost_usd: f64,
107}
108
109/// A count of g1t's own, as stored.
110#[derive(Clone, Debug, Deserialize)]
111pub(crate) struct OwnRow {
112 pub day: String,
113 pub meter: String,
114 pub workspace: String,
115 pub quantity: f64,
116}
117
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running118/// What g1t gave away, by why: its own comped workspaces, free use (a
119/// free period, free allowances, overruns g1t covered), the trial, and the
Merge branch 'worktree-agent-a633ac0f7f66d419d'120/// open-source pool, and discounts on an account's terms (what they took
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging121/// below cost plus the margin, `ledger.discount_micros`), and credits g1t
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97122/// staff gave, promotional and goodwill, when spent (`grants`), and usage a
123/// testing reset wiped (`reset_costs`): g1t paid for it and nobody will.
124/// The Team plan's included usage is paid for by the plan's price, so it is
125/// 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 running126#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
127pub(crate) struct Given {
128 pub comped: i64,
129 pub free: i64,
130 pub trial: i64,
131 pub pool: i64,
Merge branch 'worktree-agent-a633ac0f7f66d419d'132 pub discount: i64,
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging133 pub credit_promotional: i64,
134 pub credit_goodwill: i64,
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97135 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 running136}
137
138impl Given {
139 pub fn total(&self) -> i64 {
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97140 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 charging141 }
142
143 /// Credits from g1t, both kinds.
144 pub fn credit(&self) -> i64 {
145 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 running146 }
147
148 fn add(&mut self, other: &Given) {
149 self.comped += other.comped;
150 self.free += other.free;
151 self.trial += other.trial;
152 self.pool += other.pool;
Merge branch 'worktree-agent-a633ac0f7f66d419d'153 self.discount += other.discount;
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging154 self.credit_promotional += other.credit_promotional;
155 self.credit_goodwill += other.credit_goodwill;
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97156 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 running157 }
158
159 /// The same shares of `cost` as these are of `value`, at most all of it.
160 fn of(&self, cost: i64, value: i64) -> Given {
161 let total = self.total();
162 if value <= 0 || cost <= 0 || total <= 0 {
163 return Given::default();
164 }
165 let given = cost as i128 * total.min(value) as i128 / value as i128;
166 let part = |x: i64| (given * x.max(0) as i128 / total as i128) as i64;
Merge branch 'worktree-agent-a633ac0f7f66d419d'167 Given {
168 comped: part(self.comped),
169 free: part(self.free),
170 trial: part(self.trial),
171 pool: part(self.pool),
172 discount: part(self.discount),
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging173 credit_promotional: part(self.credit_promotional),
174 credit_goodwill: part(self.credit_goodwill),
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97175 reset: part(self.reset),
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging176 }
177 }
178}
179
180/// Credits from g1t in the reconciliation (`grants`): usage paid for with
181/// promotional or goodwill credit is given, not money in; a refund comes
182/// off money in on the day it refunds, shared over that day's paid usage.
183/// What credit paid for that is not among `rows` (month-end meters, or
184/// what was owed from before) is a row of its own on its day.
185pub(crate) fn apply_credits(rows: &mut Vec<UsageRow>, draws: &[(String, crate::grants::Draw)], refunds: &[crate::grants::Refunded]) {
186 let mut paid: BTreeMap<(String, String, String), Given> = BTreeMap::new();
187 for (workspace, draw) in draws {
188 let given = paid
189 .entry((draw.at[..10].to_owned(), workspace.clone(), crate::grants::usage_key(draw.task.as_deref(), &draw.reference)))
190 .or_default();
191 match draw.kind {
192 CreditKind::Promotional => given.credit_promotional += draw.micros,
193 CreditKind::Goodwill => given.credit_goodwill += draw.micros,
194 // Money already paid: what it pays for is paid for.
195 CreditKind::Refund | CreditKind::Purchased => {}
196 }
197 }
198 for ((day, workspace, key), given) in paid {
199 if given.credit() == 0 {
200 continue;
Merge branch 'worktree-agent-a633ac0f7f66d419d'201 }
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging202 match rows.iter_mut().find(|r| r.day == day && r.workspace == workspace && r.key == key) {
203 Some(row) => {
204 row.cash -= given.credit();
205 row.given.add(&given);
206 }
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97207 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 charging208 }
209 }
210 for refund in refunds {
211 let weights: Vec<(String, f64)> = rows
212 .iter()
213 .enumerate()
214 .filter(|(_, r)| r.day == refund.day && r.workspace == refund.workspace && r.cash > 0)
215 .map(|(i, r)| (format!("{i:08}"), r.cash as f64))
216 .collect();
217 let shares = attribute(refund.micros, &weights);
218 if shares.is_empty() {
219 rows.push(UsageRow {
220 day: refund.day.clone(),
221 workspace: refund.workspace.clone(),
222 key: "other".into(),
223 cash: -refund.micros,
224 ..UsageRow::default()
225 });
226 }
227 for (index, micros) in shares {
228 if let Ok(i) = index.parse::<usize>() {
229 rows[i].cash -= micros;
230 }
231 }
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 running232 }
233}
234
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily235/// What a workspace was charged for one key on one day.
236#[derive(Clone, Debug, Default, PartialEq)]
237pub(crate) struct UsageRow {
238 pub day: String,
239 pub workspace: String,
240 /// A ledger task (or `builds`), a month-end source, or `plan`.
241 pub key: String,
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97242 /// The bucket, where it is known already (what a testing reset kept,
243 /// `reset_costs`); else `key`'s, from `revenue_map`.
244 pub bucket: Option<String>,
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily245 pub value: i64,
246 pub cash: i64,
247 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 it248 /// 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 running249 /// workspaces and in a free period, else what the trial and the pool
250 /// paid and the overruns g1t covered.
251 pub given: Given,
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily252}
253
254/// One workspace's share of a product's cost on one day.
255#[derive(Clone, Debug, PartialEq)]
256pub(crate) struct WorkspaceDay {
257 pub day: String,
258 pub workspace: String,
259 pub bucket: String,
260 pub cost: i64,
Margin alerts measure what is sold, and say dollars when a percentage would mislead261 /// What the workspace paid in cash.
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily262 pub revenue: i64,
Margin alerts measure what is sold, and say dollars when a percentage would mislead263 /// What its usage was priced at, whoever paid for it.
264 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 running265 /// Of `cost`, the part g1t gave away: all of it for a comped workspace
266 /// or one with nothing priced that day (free use), else the cost times
267 /// the shares of its usage that day that g1t paid for.
268 pub given: Given,
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily269}
270
271fn micros(dollars: f64) -> i64 {
272 (dollars * 1_000_000.0).round() as i64
273}
274
275/// Puts the day's bill, g1t's counts and what customers were charged side
276/// by side, a row per day and bucket, and shares each bucket's cost out
277/// to workspaces.
278pub(crate) fn fold(
279 rules: &[Rule],
280 revenue_map: &BTreeMap<String, String>,
281 lines: &[LineRow],
282 own: &[OwnRow],
283 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 running284 internal: &BTreeSet<String>,
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily285) -> (Vec<ProductDay>, Vec<WorkspaceDay>) {
286 let mut days: BTreeMap<(String, String), ProductDay> = BTreeMap::new();
287 let entry = |day: &str, bucket: &str| -> ProductDay {
288 ProductDay { day: day.to_owned(), bucket: bucket.to_owned(), ..ProductDay::default() }
289 };
290 // Which of g1t's own meters count each bucket's units.
291 let mut own_meters: BTreeMap<&str, BTreeSet<&str>> = BTreeMap::new();
292 for rule in rules {
293 if let Some(meter) = &rule.own_meter {
294 own_meters.entry(rule.bucket.as_str()).or_default().insert(meter.as_str());
295 }
296 }
297 let mut events: BTreeMap<(String, String), f64> = BTreeMap::new();
298 for line in lines {
299 let rule = costs::classify(rules, &line.product, &line.meter);
300 let bucket = rule.map_or(UNMAPPED, |r| r.bucket.as_str());
301 let key = (line.day.clone(), bucket.to_owned());
302 if line.source == SOURCE_ARTIFACTS {
303 // What Artifacts counted: operations only, and only where the
304 // bill does not count them itself.
305 if ARTIFACTS_OPERATIONS.contains(&line.meter.as_str()) {
306 *events.entry(key).or_default() += line.quantity;
307 }
308 continue;
309 }
310 let row = days.entry(key.clone()).or_insert_with(|| entry(&key.0, &key.1));
311 row.cf_cost_micros += micros(line.cost_usd);
312 if line.source == SOURCE_BILLABLE && rule.is_some_and(|r| r.own_meter.is_some()) {
313 row.cf_quantity += line.quantity;
314 }
315 }
316 for (key, quantity) in events {
317 let row = days.entry(key.clone()).or_insert_with(|| entry(&key.0, &key.1));
318 if row.cf_quantity == 0.0 {
319 row.cf_quantity = quantity;
320 }
321 }
322 // g1t's own counts of the same units, by bucket and by workspace.
323 let mut own_by: BTreeMap<(String, String), Vec<(String, f64)>> = BTreeMap::new();
324 // Cloudflare's own count by workspace, where it gives one
325 // (`cloudflare_<bucket>`): the best way to share its cost.
326 let mut cf_by: BTreeMap<(String, String), Vec<(String, f64)>> = BTreeMap::new();
327 for count in own {
328 if let Some(bucket) = count.meter.strip_prefix("cloudflare_") {
329 cf_by.entry((count.day.clone(), bucket.to_owned())).or_default().push((count.workspace.clone(), count.quantity));
330 continue;
331 }
332 for (bucket, meters) in &own_meters {
333 if meters.contains(count.meter.as_str()) {
334 let key = (count.day.clone(), (*bucket).to_owned());
335 days.entry(key.clone()).or_insert_with(|| entry(&key.0, &key.1)).own_quantity += count.quantity;
336 own_by.entry(key).or_default().push((count.workspace.clone(), count.quantity));
337 }
338 }
339 }
340 // What customers were charged.
341 let bucket_of = |key: &str| revenue_map.get(key).cloned().unwrap_or_else(|| "models".to_owned());
342 let mut value_by: BTreeMap<(String, String), Vec<(String, f64)>> = BTreeMap::new();
343 let mut cost_by: BTreeMap<(String, String), Vec<(String, f64)>> = BTreeMap::new();
344 let mut revenue: BTreeMap<(String, String, String), i64> = BTreeMap::new();
Margin alerts measure what is sold, and say dollars when a percentage would mislead345 let mut valued: BTreeMap<(String, String, String), i64> = BTreeMap::new();
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily346 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 running347 let mut gave: BTreeMap<(String, String), (Given, i64)> = BTreeMap::new();
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily348 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 it349 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 running350 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 it351 g.1 += u.value;
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97352 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 daily353 let key = (u.day.clone(), bucket.clone());
354 let row = days.entry(key.clone()).or_insert_with(|| entry(&key.0, &key.1));
355 row.own_cost_micros += u.cost;
356 row.value_micros += u.value;
357 row.cash_micros += u.cash;
358 value_by.entry(key.clone()).or_default().push((u.workspace.clone(), u.value as f64));
359 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 mislead360 *revenue.entry((u.day.clone(), u.workspace.clone(), bucket.clone())).or_default() += u.cash;
361 *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 daily362 active.entry(u.day.clone()).or_default().push((u.workspace.clone(), u.value.max(u.cost) as f64));
363 }
364 // 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 running365 // workspace, else by g1t's own count of its units, else by what its
366 // usage cost (so free use carries its own cost), else by what it was
367 // charged; running g1t, and what no one mapped, by each workspace's
368 // share of all usage that day.
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily369 let mut shares: BTreeMap<(String, String, String), i64> = BTreeMap::new();
370 for ((day, bucket), row) in &days {
371 let key = (day.clone(), bucket.clone());
372 let weigh = |m: &BTreeMap<(String, String), Vec<(String, f64)>>| m.get(&key).filter(|w| w.iter().any(|(_, v)| *v > 0.0)).cloned();
373 let weights = if OVERHEAD.contains(&bucket.as_str()) || bucket == UNMAPPED {
374 active.get(day).cloned()
375 } 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 running376 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 daily377 };
378 for (workspace, micros) in attribute(row.cost(), &weights.unwrap_or_default()) {
379 *shares.entry((day.clone(), workspace, bucket.clone())).or_default() += micros;
380 }
381 }
382 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 it383 let workspaces: Vec<WorkspaceDay> = keys
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily384 .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 it385 .map(|(day, workspace, bucket)| {
386 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 running387 // The day's shares given away apply to every bucket, so a
388 // comped workspace's part of running g1t is given too. A
389 // workspace with nothing priced that day used g1t for free.
390 let given = if internal.contains(&workspace) {
391 Given { comped: cost, ..Given::default() }
392 } else {
393 match gave.get(&(day.clone(), workspace.clone())) {
394 Some((given, value)) if *value > 0 => given.of(cost, *value),
395 _ => Given { free: cost.max(0), ..Given::default() },
396 }
Costs: margin is measured on what was sold; comped workspaces, free periods, the trial and the pools are given away, a budget shown beside it397 };
398 WorkspaceDay {
399 cost,
400 revenue: revenue.get(&(day.clone(), workspace.clone(), bucket.clone())).copied().unwrap_or(0),
401 value: valued.get(&(day.clone(), workspace.clone(), bucket.clone())).copied().unwrap_or(0),
402 given,
403 day,
404 workspace,
405 bucket,
406 }
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily407 })
408 .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 it409 for w in &workspaces {
410 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 running411 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 it412 }
413 }
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily414 (days.into_values().collect(), workspaces)
415}
416
417/// A month-end source's day, from the snapshots of what it had come to:
418/// each day's figure less the day before's in the same month (the first
419/// day of a month, or the first snapshot, is its own).
420pub(crate) fn pending_deltas(snapshots: &[(String, String, String, i64, i64)]) -> Vec<UsageRow> {
421 // (day, workspace, source, cost, charge), any order.
422 let mut sorted = snapshots.to_vec();
423 sorted.sort_by(|a, b| (&a.1, &a.2, &a.0).cmp(&(&b.1, &b.2, &b.0)));
424 let mut out = Vec::new();
425 let mut previous: Option<&(String, String, String, i64, i64)> = None;
426 for snap in &sorted {
427 let (day, workspace, source, cost, charge) = snap;
428 let (before_cost, before_charge) = match previous {
429 Some(p) if p.1 == *workspace && p.2 == *source && p.0[..7] == day[..7] => (p.3, p.4),
430 _ => (0, 0),
431 };
432 let (cost, charge) = ((cost - before_cost).max(0), (charge - before_charge).max(0));
433 if cost > 0 || charge > 0 {
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97434 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 daily435 }
436 previous = Some(snap);
437 }
438 out
439}
440
Merge costs and margin review: gateway query, own spend, discount meters, superseded rises441/// A month-end meter's day on a 100%-discount workspace: all of it given
442/// (comped) and none of it money in. The snapshot holds what the month
443/// would charge before the discount, which the month's close takes off in
444/// full; counted as paid, flagon-io's cache, embeddings and scans read as
445/// money in ($0.0023 on 2026-10-08).
446pub(crate) fn comped_meter(mut u: UsageRow) -> UsageRow {
447 u.given = Given { comped: u.value, ..Given::default() };
448 u.cash = 0;
449 u
450}
451
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily452/// Margin as a share of what was charged, in percent; None when nothing was.
453pub(crate) fn margin_percent(revenue_micros: i64, cost_micros: i64) -> Option<f64> {
454 (revenue_micros > 0).then(|| (revenue_micros - cost_micros) as f64 * 100.0 / revenue_micros as f64)
455}
456
457/// How far `ours` is from `theirs`, in percent of theirs; None when theirs
458/// is nothing.
459pub(crate) fn delta_percent(ours: f64, theirs: f64) -> Option<f64> {
460 (theirs > 0.0).then(|| (ours - theirs) * 100.0 / theirs)
461}
462
463#[derive(Clone, Copy, Debug, PartialEq, Eq)]
464pub(crate) enum DriftKind {
465 /// g1t counted a different number of units than Cloudflare did.
466 Count,
467 /// What Cloudflare charged differs from what the price book says the
468 /// same usage cost.
469 Cost,
470 /// Cloudflare charged for something nothing charges customers for.
471 Leak,
Merge branch 'worktree-agent-a633ac0f7f66d419d'472 /// Model usage AI Gateway put no price on: its cost is not what the
473 /// provider bills, so neither the ledger nor the gateway total has it.
474 Unpriced,
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily475}
476
477impl DriftKind {
478 pub fn as_str(self) -> &'static str {
479 match self {
480 DriftKind::Count => "count",
481 DriftKind::Cost => "cost",
482 DriftKind::Leak => "leak",
Merge branch 'worktree-agent-a633ac0f7f66d419d'483 DriftKind::Unpriced => "unpriced",
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily484 }
485 }
486}
487
488#[derive(Clone, Debug, PartialEq)]
489pub(crate) struct Drift {
490 pub bucket: String,
491 pub kind: DriftKind,
492 pub ours: f64,
493 pub cloudflare: f64,
494 pub delta_percent: Option<f64>,
495}
496
497/// Drift over a window for one bucket: counts more than `threshold`
498/// percent apart, a bill that far from the price book's cost of the same
499/// usage, and cost with nothing charged for it. Under `min_cost_micros`
500/// in all, cost says nothing.
501pub(crate) fn drifts(bucket: &str, days: &[ProductDay], threshold: f64, counted: bool, min_cost_micros: i64) -> Vec<Drift> {
502 let overhead = OVERHEAD.contains(&bucket);
503 let sum = |f: &dyn Fn(&ProductDay) -> f64| days.iter().map(f).sum::<f64>();
504 let cf_cost = sum(&|d| d.cf_cost_micros as f64);
505 let own_cost = sum(&|d| d.own_cost_micros as f64);
506 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 drift507 // Counts are compared from the first day g1t counted: before its meter
508 // was deployed there is only Cloudflare's side. A meter that never
509 // counted anything is compared over every day, so it still shows.
510 let first_counted = days.iter().filter(|d| d.own_quantity > 0.0).map(|d| d.day.as_str()).min();
511 let compared = |d: &&ProductDay| first_counted.is_none_or(|from| d.day.as_str() >= from);
512 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 daily513 let mut out = Vec::new();
514 if counted && cf_quantity > 0.0 {
515 let delta = delta_percent(own_quantity, cf_quantity);
516 if delta.is_some_and(|d| d.abs() > threshold) {
517 out.push(Drift { bucket: bucket.into(), kind: DriftKind::Count, ours: own_quantity, cloudflare: cf_quantity, delta_percent: delta });
518 }
519 }
520 let enough = cf_cost.max(own_cost) >= min_cost_micros as f64;
Merge branch 'worktree-agent-a633ac0f7f66d419d'521 // Models: what AI Gateway priced g1t's own provider traffic at (its
522 // lines, as "Cloudflare's" side) against the ledger's model cost. Only
523 // once the gateway has been read; then the ledger having none of it is
524 // drift too (traffic no run was charged for).
525 let models = NOT_CLOUDFLARE.contains(&bucket) && cf_cost > 0.0;
526 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 daily527 let delta = delta_percent(own_cost, cf_cost);
528 if delta.is_some_and(|d| d.abs() > threshold) {
529 out.push(Drift { bucket: bucket.into(), kind: DriftKind::Cost, ours: own_cost, cloudflare: cf_cost, delta_percent: delta });
530 }
531 }
Billing: AI Gateway's analytics are read with the token that can see them, and a gateway that priced nothing is said532 // The ledger has model cost and the gateway priced none of it: a token
533 // that cannot see AI Gateway reads as no rows, never an error, so this
534 // is not agreement. Said, rather than left as no row at all.
535 if NOT_CLOUDFLARE.contains(&bucket) && cf_cost <= 0.0 && own_cost >= min_cost_micros as f64 && own_cost > 0.0 {
536 out.push(Drift { bucket: bucket.into(), kind: DriftKind::Cost, ours: own_cost, cloudflare: 0.0, delta_percent: None });
537 }
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily538 if !overhead && cf_cost >= min_cost_micros as f64 && value <= 0.0 {
539 out.push(Drift { bucket: bucket.into(), kind: DriftKind::Leak, ours: value, cloudflare: cf_cost, delta_percent: None });
540 }
541 out
542}
543
Merge branch 'worktree-agent-a633ac0f7f66d419d'544/// What can make AI Gateway's cost differ from what the providers bill,
545/// said for staff: cache tokens (priced by the gateway at its own rates for
546/// them, which may lag the provider's), requests Cloudflare billed itself,
547/// models it has no price for, and runs settled short.
548fn caveat_notes(c: &costs::GatewayCaveats) -> Vec<String> {
549 let mut notes = Vec::new();
550 if c.cache_read_tokens > 0.0 || c.cache_write_tokens > 0.0 {
551 notes.push(format!(
552 "{} 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",
553 crate::features::thousands(c.cache_read_tokens.round() as u64),
554 crate::features::thousands(c.cache_write_tokens.round() as u64)
555 ));
556 }
557 if c.wholesale_usd > 0.0 {
558 notes.push(format!(
559 "{} of it Cloudflare billed itself (unified billing): that part is on Cloudflare's bill, not a provider's",
560 dollars(micros(c.wholesale_usd))
561 ));
562 }
563 if !c.unpriced.is_empty() {
564 notes.push(format!("it has no price for {} (tokens used, $0)", c.unpriced.join(", ")));
565 }
566 if c.short_runs > 0 {
567 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));
568 }
569 notes
570}
571
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97572/// Where a testing reset's history starts: all of a workspace's ledger.
573pub(crate) const RESET_HISTORY_FROM: &str = "2000-01-01";
574
575/// What a testing reset wiped that g1t paid for, on one day for one
576/// bucket (`reset_costs`).
577#[derive(Clone, Debug, PartialEq)]
578pub(crate) struct Wiped {
579 pub day: String,
580 pub bucket: String,
581 pub cost: i64,
582 pub value: i64,
583}
584
585/// A workspace's usage rows, about to be wiped, as what g1t paid for: the
586/// rows with a cost, by day and bucket, valued as the reconciliation
587/// valued them (at price where nothing paid). Plan payments, credits and
588/// a workspace's own model provider cost g1t nothing and are left out.
589pub(crate) fn wiped(rows: &[UsageRow], revenue_map: &BTreeMap<String, String>, margin_percent: u32) -> Vec<Wiped> {
590 let mut by: BTreeMap<(String, String), (i64, i64)> = BTreeMap::new();
591 for u in rows.iter().filter(|u| u.cost > 0) {
592 let bucket = u.bucket.clone().unwrap_or_else(|| revenue_map.get(&u.key).cloned().unwrap_or_else(|| NOT_CLOUDFLARE[0].to_owned()));
593 let sums = by.entry((u.day.clone(), bucket)).or_default();
594 sums.0 += u.cost;
595 sums.1 += u.value.max(0);
596 }
597 by.into_iter()
598 .map(|((day, bucket), (cost, value))| Wiped {
599 day,
600 bucket,
601 cost,
602 value: if value > 0 { value } else { crate::credits::with_margin(cost, margin_percent) },
603 })
604 .collect()
605}
606
607/// What testing resets kept, each (day, workspace, bucket, cost, value),
608/// as usage rows: valued as before, nothing paid, all of it given away
609/// (why "testing resets"). A reset's own row (bucket '') is not usage.
610pub(crate) fn reset_usage(kept: &[(String, String, String, i64, i64)]) -> Vec<UsageRow> {
611 kept.iter()
612 .filter(|(_, _, bucket, cost, value)| !bucket.is_empty() && (*cost != 0 || *value != 0))
613 .map(|(day, workspace, bucket, cost, value)| UsageRow {
614 day: day.clone(),
615 workspace: workspace.clone(),
616 key: "reset".into(),
617 bucket: Some(bucket.clone()),
618 value: *value,
619 cash: 0,
620 cost: *cost,
621 given: Given { reset: *value, ..Given::default() },
622 })
623 .collect()
624}
625
626/// A testing reset inside the drift window.
627#[derive(Clone, Debug, PartialEq)]
628pub(crate) struct ResetNote {
629 pub workspace: String,
630 /// The UTC day it was reset.
631 pub day: String,
632 /// Whether it kept what it wiped (`reset_costs`, migration 0046):
633 /// then the ledger's side has it, given away. A reset from before
634 /// that wiped model usage the gateway still counts.
635 pub recorded: bool,
636 /// Of what it kept, model cost on the window's days.
637 pub models_micros: i64,
638}
639
640/// The resets: each audit entry (account `ws_<slug>`, when) and each kept
641/// reset (workspace, reset_at, its model cost in the window). An audit
642/// entry with no kept reset at the same instant is from before resets kept
643/// what they wiped.
644pub(crate) fn reset_notes(audits: &[(String, String)], kept: &[(String, String, i64)]) -> Vec<ResetNote> {
645 let mut notes: Vec<(String, ResetNote)> = kept
646 .iter()
647 .map(|(workspace, at, models)| {
648 (at.clone(), ResetNote { workspace: workspace.clone(), day: at[..10.min(at.len())].to_owned(), recorded: true, models_micros: *models })
649 })
650 .collect();
651 for (account, at) in audits {
652 let workspace = account.strip_prefix("ws_").unwrap_or(account);
653 if !kept.iter().any(|(w, a, _)| w == workspace && a == at) {
654 notes.push((at.clone(), ResetNote { workspace: workspace.to_owned(), day: at[..10.min(at.len())].to_owned(), recorded: false, models_micros: 0 }));
655 }
656 }
657 notes.sort_by(|a, b| a.0.cmp(&b.0).then(a.1.workspace.cmp(&b.1.workspace)));
658 notes.into_iter().map(|(_, n)| n).collect()
659}
660
661/// Model usage a reset wiped before resets kept it is not a leak: while
662/// such a reset is in the window the models leak is not raised, and the
663/// models cost drift says what the gap is.
664pub(crate) fn wiped_not_leaked(drift: &Drift, resets: &[ResetNote]) -> bool {
665 drift.kind == DriftKind::Leak && NOT_CLOUDFLARE.contains(&drift.bucket.as_str()) && resets.iter().any(|r| !r.recorded)
666}
667
668/// What the models drift says about resets in the window.
669fn reset_sentences(resets: &[ResetNote]) -> Vec<String> {
670 resets
671 .iter()
672 .filter_map(|r| {
673 if !r.recorded {
674 Some(format!(
675 "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 {}.",
676 r.workspace,
677 r.day,
678 day_after(&r.day, DRIFT_DAYS)
679 ))
680 } else if r.models_micros > 0 {
681 Some(format!(
682 "The ledger's figure includes {} of model cost wiped by a testing reset of {} on {}, counted as given away (testing resets).",
683 dollars(r.models_micros),
684 r.workspace,
685 r.day
686 ))
687 } else {
688 None
689 }
690 })
691 .collect()
692}
693
694/// The models drift's detail: the gateway's total against the ledger's,
695/// and any testing reset in the window.
Merge costs and margin review: gateway query, own spend, discount meters, superseded rises696pub(crate) fn models_detail(drift: &Drift, caveats: &costs::GatewayCaveats, resets: &[ResetNote], read: &costs::GatewayRead) -> String {
Billing: AI Gateway's analytics are read with the token that can see them, and a gateway that priced nothing is said697 if drift.cloudflare <= 0.0 {
Merge costs and margin review: gateway query, own spend, discount meters, superseded rises698 let head = format!(
699 "Models: the ledger's model cost is {} over the last {DRIFT_DAYS} days and AI Gateway priced nothing, so the two were not compared.",
Billing: AI Gateway's analytics are read with the token that can see them, and a gateway that priced nothing is said700 dollars(drift.ours as i64)
701 );
Merge costs and margin review: gateway query, own spend, discount meters, superseded rises702 let why = if caveats.requests > 0.0 {
703 format!(
704 "The gateway logged {} requests in those days but put no price on them: it has no price for the models used{}. Add their prices to the gateway, or route those models where they are priced.",
705 crate::features::thousands(caveats.requests.round() as u64),
706 if caveats.unpriced.is_empty() { String::new() } else { format!(" ({})", caveats.unpriced.join(", ")) }
707 )
708 } else {
709 match read {
710 costs::GatewayRead::Empty { visible: Some(false) } => "The token billing reads AI Gateway with (CLOUDFLARE_USAGE_TOKEN, else CLOUDFLARE_BILLING_TOKEN) cannot see the gateway named in AI_GATEWAY_ID: Cloudflare refused it (no Account, AI Gateway, Read on the token, or no gateway by that id). Give the token AI Gateway Read, or fix AI_GATEWAY_ID.".to_owned(),
711 costs::GatewayRead::Empty { visible: Some(true) } => "The token can see the gateway and it logged no requests in those days: model calls went around it. Check that every caller of a hosted model uses the gateway's URL (services/models, the runner's ANTHROPIC_BASE_URL).".to_owned(),
712 costs::GatewayRead::Failed(error) => format!("AI Gateway's analytics could not be read: {error}"),
713 costs::GatewayRead::NotRead => "AI Gateway was not read on this run: no AI_GATEWAY_ID, or no CLOUDFLARE_USAGE_TOKEN or CLOUDFLARE_BILLING_TOKEN.".to_owned(),
714 costs::GatewayRead::Rows | costs::GatewayRead::Empty { visible: None } => "The gateway answered without requests for these days, and whether its token can see the gateway could not be told: either the token lacks AI Gateway Read, or model calls went around the gateway.".to_owned(),
715 }
716 };
717 return format!("{head} {why}");
Billing: AI Gateway's analytics are read with the token that can see them, and a gateway that priced nothing is said718 }
Merge branch 'worktree-agent-a633ac0f7f66d419d'719 let lower = drift.ours < drift.cloudflare;
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97720 let wiped = resets.iter().any(|r| !r.recorded);
Merge branch 'worktree-agent-a633ac0f7f66d419d'721 let mut detail = format!(
722 "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}%). {}",
723 dollars(drift.cloudflare as i64),
724 dollars(drift.ours as i64),
725 drift.delta_percent.unwrap_or(0.0),
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97726 if lower && wiped {
727 "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."
728 } else if lower {
Merge branch 'worktree-agent-a633ac0f7f66d419d'729 "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)."
730 } else {
731 "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."
732 }
733 );
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97734 for sentence in reset_sentences(resets) {
735 detail.push(' ');
736 detail.push_str(&sentence);
737 }
Merge branch 'worktree-agent-a633ac0f7f66d419d'738 let notes = caveat_notes(caveats);
739 if !notes.is_empty() {
740 detail.push_str(" The gateway's cost may be off: ");
741 detail.push_str(&notes.join("; "));
742 detail.push('.');
743 }
744 detail
745}
746
747/// The unpriced drift's detail.
748pub(crate) fn unpriced_detail(caveats: &costs::GatewayCaveats) -> String {
749 format!(
750 "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.",
751 caveat_notes(&costs::GatewayCaveats { cache_read_tokens: 0.0, cache_write_tokens: 0.0, wholesale_usd: 0.0, ..caveats.clone() }).join("; ")
752 )
753}
754
755/// Model usage AI Gateway could not price over the window, as drift on
756/// the models bucket: models with tokens and no cost, or runs settled
757/// short. None when there is none.
758pub(crate) fn unpriced_drift(caveats: &costs::GatewayCaveats) -> Option<(Drift, String)> {
759 if caveats.unpriced.is_empty() && caveats.short_runs == 0 {
760 return None;
761 }
762 let drift = Drift {
763 bucket: NOT_CLOUDFLARE[0].into(),
764 kind: DriftKind::Unpriced,
765 ours: f64::from(caveats.short_runs),
766 cloudflare: caveats.unpriced.len() as f64,
767 delta_percent: None,
768 };
769 Some((drift, unpriced_detail(caveats)))
770}
771
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily772/// When the last `days` in a row (each with enough cost to say something)
773/// were all under the floor: the first of them and the worst margin.
774/// Each item is a day's (day, revenue, cost).
775pub(crate) fn breach(series: &[(String, i64, i64)], floor_percent: f64, days: usize, min_cost_micros: i64) -> Option<(String, f64)> {
776 if days == 0 || series.len() < days {
777 return None;
778 }
779 let tail = &series[series.len() - days..];
780 let mut worst = f64::INFINITY;
781 for (_, revenue, cost) in tail {
782 if *cost < min_cost_micros {
783 return None;
784 }
785 let margin = margin_percent(*revenue, *cost).unwrap_or(-100.0);
786 if margin >= floor_percent {
787 return None;
788 }
789 worst = worst.min(margin);
790 }
791 Some((tail[0].0.clone(), worst))
792}
793
794/// `total` shared out in proportion to `weights`, in whole millionths that
795/// add up to it exactly (largest remainder first). Nothing to share, or no
796/// weight, shares nothing.
797pub(crate) fn attribute(total: i64, weights: &[(String, f64)]) -> Vec<(String, i64)> {
798 let mut merged: BTreeMap<String, f64> = BTreeMap::new();
799 for (key, w) in weights {
800 *merged.entry(key.clone()).or_default() += w.max(0.0);
801 }
802 let sum: f64 = merged.values().sum();
803 if total <= 0 || sum <= 0.0 {
804 return Vec::new();
805 }
806 let mut shares: Vec<(String, i64, f64)> = merged
807 .into_iter()
808 .map(|(key, w)| {
809 let exact = total as f64 * w / sum;
810 (key, exact.floor() as i64, exact - exact.floor())
811 })
812 .collect();
813 let mut left = total - shares.iter().map(|s| s.1).sum::<i64>();
814 let mut order: Vec<usize> = (0..shares.len()).collect();
815 order.sort_by(|a, b| shares[*b].2.total_cmp(&shares[*a].2).then(shares[*a].0.cmp(&shares[*b].0)));
816 for index in order {
817 if left <= 0 {
818 break;
819 }
820 shares[index].1 += 1;
821 left -= 1;
822 }
823 shares.into_iter().filter(|s| s.1 > 0).map(|(key, micros, _)| (key, micros)).collect()
824}
825
826/// Workspaces that cost g1t more than `factor` times what they paid, with
827/// at least `floor_micros` of cost: each (workspace, cost, revenue), the
828/// biggest gap first.
Models' margin read -14%: usage nothing paid for is valued at price, not $0829/// What a day's usage was worth at price. g1t's own workspaces are valued
830/// at price. So is usage nothing paid for, neither charged nor drawn from
831/// the plan, a trial, a pool or a gift (a free period): it was given away at
832/// its price, not sold for nothing. Anything paid keeps what it was paid, so
833/// a discount still shows as one.
834pub(crate) fn usage_value(internal: bool, cost: i64, paid: i64, margin_percent: u32) -> i64 {
835 if internal || (paid == 0 && cost > 0) {
836 return crate::credits::with_margin(cost, margin_percent);
837 }
838 paid
839}
840
Margin alerts measure what is sold, and say dollars when a percentage would mislead841/// What the overall alert says: the money as money, and a percentage only
842/// while there is enough coming in for one to mean something (a few cents
843/// against dollars of cost reads as -8000%).
844pub(crate) fn overall_detail(took: i64, spent: i64, days: usize, floor: f64, worst: f64) -> String {
845 if took < 1_000_000 * days as i64 {
846 return format!(
847 "All of g1t, comped workspaces left out: took in {} against {} of Cloudflare's bill over {days} days.",
848 dollars(took),
849 dollars(spent)
850 );
851 }
852 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}%.")
853}
854
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily855pub(crate) fn anomalies(rows: &[(String, i64, i64)], factor: f64, floor_micros: i64) -> Vec<(String, i64, i64)> {
856 let mut out: Vec<(String, i64, i64)> = rows
857 .iter()
858 .filter(|(_, cost, revenue)| *cost >= floor_micros && *cost as f64 > *revenue as f64 * factor)
859 .cloned()
860 .collect();
861 out.sort_by(|a, b| (b.1 - b.2).cmp(&(a.1 - a.2)).then(a.0.cmp(&b.0)));
862 out
863}
864
865/// Cloudflare's marginal rate for one of its units: the median over the
866/// charged days of cost over quantity, in dollars. None while the included
867/// amounts still cover it. Each item is a day's (quantity, cost).
868pub(crate) fn billed_rate(days: &[(f64, f64)]) -> Option<f64> {
869 let mut rates: Vec<f64> = days.iter().filter(|(q, c)| *q > 0.0 && *c > 0.0).map(|(q, c)| c / q).collect();
870 if rates.is_empty() {
871 return None;
872 }
873 rates.sort_by(f64::total_cmp);
874 Some(rates[rates.len() / 2])
875}
876
877/// What one of g1t's units costs, from Cloudflare's rate per its own unit
878/// and how many of Cloudflare's units each of g1t's took: if Cloudflare
879/// counts three operations for every git operation g1t counts, a git
880/// operation costs three of Cloudflare's. None without enough of g1t's
881/// units to say.
882pub(crate) fn derived_unit_cost(rate_per_cf_unit: f64, cf_units: f64, own_units: f64) -> Option<f64> {
883 (own_units >= MIN_UNITS && cf_units > 0.0 && rate_per_cf_unit > 0.0).then(|| rate_per_cf_unit * cf_units / own_units)
884}
885
886/// How many units a price is per: `1,000 operations` → 1,000, `million
887/// requests` → 1,000,000, `second` → 1.
888pub(crate) fn unit_size(unit: &str) -> f64 {
889 let first = unit.split_whitespace().next().unwrap_or_default().replace(',', "");
890 match first.as_str() {
891 "million" => 1_000_000.0,
892 "thousand" => 1_000.0,
893 n => n.parse().unwrap_or(1.0),
894 }
895}
896
897fn day_before(day: &str, days: u64) -> String {
898 let ms = g1t_contracts::time::parse_rfc3339(&format!("{day}T00:00:00Z")).unwrap_or(0);
899 rfc3339(ms.saturating_sub(days * DAY_MS))[..10].to_owned()
900}
901
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97902fn day_after(day: &str, days: u64) -> String {
903 let ms = g1t_contracts::time::parse_rfc3339(&format!("{day}T00:00:00Z")).unwrap_or(0);
904 rfc3339(ms + days * DAY_MS)[..10].to_owned()
905}
906
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily907/// Dollars to the cent from a dollar up, finer below: `$17.02`, `$0.063`.
908fn dollars(micros: i64) -> String {
909 if micros.abs() >= 1_000_000 {
910 let cents = (micros as f64 / 10_000.0).round() as i64;
911 format!("{}${}.{:02}", if cents < 0 { "-" } else { "" }, cents.abs() / 100, cents.abs() % 100)
912 } else {
913 crate::features::dollars(micros)
914 }
915}
916
917/// The days a plan payment is spread over.
918const PLAN_DAYS: u64 = 30;
919
920/// `micros` paid on `day` spread evenly over `days` days from it, in
921/// whole micros that add up to it (the first days take the remainder).
922pub(crate) fn spread(day: &str, micros: i64, days: u64) -> Vec<(String, i64)> {
923 if micros <= 0 || days == 0 {
924 return Vec::new();
925 }
926 let start = g1t_contracts::time::parse_rfc3339(&format!("{}T00:00:00Z", &day[..10.min(day.len())])).unwrap_or(0);
927 let each = micros / days as i64;
928 let rest = micros % days as i64;
929 (0..days)
930 .map(|n| (rfc3339(start + n * DAY_MS)[..10].to_owned(), each + i64::from((n as i64) < rest)))
931 .collect()
932}
933
934// ---------------------------------------------------------------------
935// The daily run, and what sudo reads.
936// ---------------------------------------------------------------------
937
938#[derive(Serialize)]
939struct Mail<'a> {
940 to: &'a str,
941 from: &'a str,
942 subject: &'a str,
943 text: String,
944 html: String,
945}
946
947fn escape(text: &str) -> String {
948 text.replace('&', "&amp;").replace('<', "&lt;").replace('>', "&gt;").replace('"', "&quot;")
949}
950
951/// Emails staff through Cloudflare Email Sending, the `EMAIL` binding.
Spend caps: a monthly budget for comped workspaces and a daily breaker on what g1t pays952pub(crate) async fn email_staff(env: &Env, to: &str, subject: &str, lines: &[String]) -> Result<()> {
Merge branch 'main' into actions-toolkit-oidc-artifacts953 email_staff_page(env, to, subject, lines, ("Costs & margin", "https://sudo.g1t.sh/costs"), "g1t-billing's margin guard").await
954}
955
956/// Emails staff, linking to a page of sudo (`page`: its name and address)
957/// and saying what sent it.
958pub(crate) async fn email_staff_page(env: &Env, to: &str, subject: &str, lines: &[String], page: (&str, &str), sender: &str) -> Result<()> {
959 let (name, link) = page;
960 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 daily961 let mut html = String::from("<div style=\"font-family:system-ui,sans-serif;max-width:560px;margin:0 auto;padding:24px 16px;color:#16150f\">");
962 for line in lines {
963 html.push_str(&format!("<p style=\"font-size:15px;line-height:1.6\">{}</p>", escape(line)));
964 }
965 html.push_str(&format!(
Merge branch 'main' into actions-toolkit-oidc-artifacts966 "<p><a href=\"{link}\">Open {} in sudo</a></p><p style=\"font-size:13px;color:#6e6a5e\">Sent by {} (COSTS_ALERT_EMAIL).</p></div>",
967 escape(name),
968 escape(sender)
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily969 ));
970 let mail = Mail { to, from: "g1t <noreply@g1t.sh>", subject, text, html };
971 let binding = g1t_kit::js::binding(env, "EMAIL")?;
972 g1t_kit::js::call(&binding, "send", &[g1t_kit::js::to_js(&mail)?]).await?;
973 Ok(())
974}
975
976#[derive(Deserialize)]
977struct AlertRow {
978 id: String,
979 kind: String,
980 subject: String,
981 detail: String,
982 since: String,
983 opened_at: String,
984 emailed_at: Option<String>,
985}
986
987impl From<AlertRow> for MarginAlert {
988 fn from(r: AlertRow) -> Self {
989 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 }
990 }
991}
992
993#[derive(Deserialize)]
994struct MarginRow {
995 day: String,
996 bucket: String,
997 cf_cost_micros: i64,
998 own_cost_micros: i64,
999 value_micros: i64,
1000 cash_micros: i64,
1001 cf_quantity: f64,
1002 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 it1003 #[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 running1004 given_comped_micros: Option<i64>,
1005 #[serde(default)]
1006 given_free_micros: Option<i64>,
1007 #[serde(default)]
1008 given_trial_micros: Option<i64>,
1009 #[serde(default)]
1010 given_pool_micros: Option<i64>,
Merge branch 'worktree-agent-a633ac0f7f66d419d'1011 #[serde(default)]
1012 given_discount_micros: Option<i64>,
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging1013 #[serde(default)]
1014 given_credit_promotional_micros: Option<i64>,
1015 #[serde(default)]
1016 given_credit_goodwill_micros: Option<i64>,
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb971017 #[serde(default)]
1018 given_reset_micros: Option<i64>,
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1019}
1020
1021impl From<MarginRow> for ProductDay {
1022 fn from(r: MarginRow) -> Self {
1023 ProductDay {
1024 day: r.day,
1025 bucket: r.bucket,
1026 cf_cost_micros: r.cf_cost_micros,
1027 own_cost_micros: r.own_cost_micros,
1028 value_micros: r.value_micros,
1029 cash_micros: r.cash_micros,
1030 cf_quantity: r.cf_quantity,
1031 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 running1032 given: Given {
1033 comped: r.given_comped_micros.unwrap_or(0),
1034 free: r.given_free_micros.unwrap_or(0),
1035 trial: r.given_trial_micros.unwrap_or(0),
1036 pool: r.given_pool_micros.unwrap_or(0),
Merge branch 'worktree-agent-a633ac0f7f66d419d'1037 discount: r.given_discount_micros.unwrap_or(0),
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging1038 credit_promotional: r.given_credit_promotional_micros.unwrap_or(0),
1039 credit_goodwill: r.given_credit_goodwill_micros.unwrap_or(0),
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb971040 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 running1041 },
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1042 }
1043 }
1044}
1045
1046impl Billing {
1047 /// The day's work: read Cloudflare's bill and g1t's own counts,
1048 /// reconcile, look for drift, measure unit costs, apply prices whose
1049 /// day has come, and raise or clear alerts.
1050 pub(crate) async fn costs_daily(&self, env: &Env, keeper: &crate::keeper::Keeper) -> Result<CostsRun> {
1051 let mut run = CostsRun::default();
Merge costs and margin review: gateway query, own spend, discount meters, superseded rises1052 let mut gateway = costs::GatewayRead::default();
1053 let (since, until) = match self.read_cloudflare(keeper, &mut run.problems, &mut gateway).await? {
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1054 Some((since, until, lines)) => {
1055 run.lines = lines;
1056 (since, until)
1057 }
1058 // Without the bill, still reconcile what g1t knows itself, over
1059 // the same days the bill would be read for.
1060 None => {
1061 #[derive(Deserialize)]
1062 struct Last {
1063 day: Option<String>,
1064 }
1065 let last = self.db.prepare("SELECT MAX(day) AS day FROM margin_days").first::<Last>(None).await?.and_then(|l| l.day);
1066 costs::window(last.as_deref(), now_ms())
1067 }
1068 };
Costs: Cloudflare's subscriptions read from Cloudflare each day, the estimate only until then; sudo's costs split into Costs & margin and Bill & pricing1069 // Not a problem for the run: the last read, or the estimate, stays.
1070 if keeper.can_read_bill()
1071 && let Err(error) = self.read_subscriptions(keeper).await
1072 {
1073 worker::console_error!("Cloudflare's subscriptions were not read: {error}");
1074 }
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1075 if let Err(error) = self.count_own(&since, &until).await {
1076 run.problems.push(format!("g1t's own counts could not be read: {error}"));
1077 }
1078 self.snapshot_pending(&until).await?;
Models' margin read -14%: usage nothing paid for is valued at price, not $01079 // Reconciled over the whole window sudo shows, not only the days the
1080 // bill was read for: it reads only what is already kept, so a change
1081 // in how a day is valued reaches every day shown at the next run.
1082 let window = day_before(&until, costs::BACKFILL_DAYS - 1);
1083 let reconcile_from = if window < since { window } else { since.clone() };
1084 run.days = self.reconcile_range(&reconcile_from, &until).await?;
Merge costs and margin review: gateway query, own spend, discount meters, superseded rises1085 let drift = self.find_drift(&until, &gateway).await?;
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1086 run.proposals = self.measure_units(&until).await?;
1087 self.apply_due_versions().await?;
1088 run.alerts = self.raise_alerts(env, &until, &drift).await?;
1089 if let Some(identity) = &self.identity
1090 && let Err(error) = self.tell_owners_of_rises(identity).await
1091 {
1092 run.problems.push(format!("owners could not be told of a price rise: {error}"));
1093 }
1094 for problem in &run.problems {
1095 worker::console_warn!("costs: {problem}");
1096 }
1097 Ok(run)
1098 }
1099
1100 /// What each month-end source had come to by the end of `day`.
1101 async fn snapshot_pending(&self, day: &str) -> Result<()> {
1102 self.db
1103 .prepare(
1104 "INSERT INTO pending_days (day, workspace, source, cost_micros, charge_micros)
1105 SELECT ?1, workspace, source, COALESCE(cost_micros, 0), COALESCE(charge_micros, 0) FROM pending_usage WHERE month = ?2
1106 ON CONFLICT (day, workspace, source) DO UPDATE SET cost_micros = excluded.cost_micros, charge_micros = excluded.charge_micros",
1107 )
1108 .bind(&[day.into(), day[..7].into()])?
1109 .run()
1110 .await?;
1111 Ok(())
1112 }
1113
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb971114 /// What customers were charged on the days, by workspace and key: every
1115 /// workspace's, or only `only`'s.
1116 async fn usage_rows(&self, since: &str, until: &str, only: Option<&str>) -> Result<Vec<UsageRow>> {
1117 let only_sql = only.unwrap_or("");
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1118 #[derive(Deserialize)]
1119 struct Row {
1120 day: String,
1121 workspace: String,
1122 key: String,
1123 internal: i64,
1124 own_provider: i64,
1125 cash: Option<i64>,
1126 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 running1127 trial: Option<i64>,
1128 oss: Option<i64>,
1129 covered: Option<i64>,
Merge branch 'worktree-agent-a633ac0f7f66d419d'1130 discount: Option<i64>,
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1131 cost: Option<i64>,
1132 }
1133 let charged_here = crate::storage::CHARGED_HERE.iter().map(|s| format!("'{s}'")).collect::<Vec<_>>().join(", ");
1134 let end = format!("{until}T23:59:59.999Z");
1135 let rows = self
1136 .db
1137 .prepare(format!(
1138 "SELECT substr(created_at, 1, 10) AS day, workspace,
Merge costs and margin review: gateway query, own spend, discount meters, superseded rises1139 CASE WHEN task = 'deployments' AND reference LIKE 'deploy/%' THEN 'builds'
1140 WHEN task = 'plan' THEN '{planning}' ELSE COALESCE(task, 'other') END AS key,
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1141 CASE WHEN workspace IN ({internal}) THEN 1 ELSE 0 END AS internal,
1142 CASE WHEN billed_to = 'workspace' THEN 1 ELSE 0 END AS own_provider,
1143 -SUM(amount_micros) AS cash,
1144 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 running1145 SUM(COALESCE(trial_micros, 0)) AS trial,
1146 SUM(COALESCE(oss_micros, 0)) AS oss,
1147 SUM(COALESCE(given_micros, 0)) AS covered,
Merge branch 'worktree-agent-a633ac0f7f66d419d'1148 SUM(COALESCE(discount_micros, 0)) AS discount,
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1149 SUM(COALESCE(cost_micros, 0)) AS cost
1150 FROM ledger
1151 WHERE kind = 'usage' AND created_at >= ?1 AND created_at <= ?2 AND COALESCE(task, '') NOT IN ({charged_here})
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb971152 AND (?3 = '' OR workspace = ?3)
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1153 GROUP BY 1, 2, 3, 4, 5",
Merge costs and margin review: gateway query, own spend, discount meters, superseded rises1154 internal = crate::sales::INTERNAL_SQL,
1155 planning = PLANNING_KEY
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1156 ))
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb971157 .bind(&[since.into(), end.as_str().into(), only_sql.into()])?
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1158 .all()
1159 .await?
1160 .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 it1161 let mut internal = BTreeSet::new();
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1162 let mut out: Vec<UsageRow> = rows
1163 .into_iter()
1164 .map(|r| {
1165 // A workspace's own model provider was paid there: no cost
1166 // to g1t. g1t's own workspaces are valued at price.
1167 let cost = if r.own_provider == 1 { 0 } else { r.cost.unwrap_or(0) };
1168 let cash = r.cash.unwrap_or(0);
Merge branch 'worktree-agent-a633ac0f7f66d419d'1169 // A discount took its part below cost plus the margin: it is
1170 // valued at price and that part counted as given, so a
1171 // discounted sale never reads as margin lost.
1172 let discount = r.discount.unwrap_or(0).max(0);
1173 let paid = cash + r.drawn.unwrap_or(0) + discount;
Models' margin read -14%: usage nothing paid for is valued at price, not $01174 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 running1175 let given = if r.internal == 1 {
1176 Given { comped: value, ..Given::default() }
1177 } else if paid == 0 && cost > 0 {
1178 Given { free: value, ..Given::default() }
1179 } else {
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging1180 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 running1181 };
Costs: margin is measured on what was sold; comped workspaces, free periods, the trial and the pools are given away, a budget shown beside it1182 if r.internal == 1 {
1183 internal.insert(r.workspace.clone());
1184 }
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb971185 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 daily1186 })
1187 .collect();
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging1188 // Credits from g1t: what promotional and goodwill credit paid for
1189 // is given, not money in; a refund gives money back on its day.
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb971190 let (mut draws, mut refunds) = self.credit_effects(since, until).await?;
1191 if let Some(only) = only {
1192 draws.retain(|(workspace, _)| workspace == only);
1193 refunds.retain(|r| r.workspace == only);
1194 }
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging1195 apply_credits(&mut out, &draws, &refunds);
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1196 // Month-end sources, from their daily snapshots.
1197 #[derive(Deserialize)]
1198 struct Snap {
1199 day: String,
1200 workspace: String,
1201 source: String,
1202 cost_micros: i64,
1203 charge_micros: i64,
1204 }
1205 let snaps = self
1206 .db
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb971207 .prepare("SELECT day, workspace, source, cost_micros, charge_micros FROM pending_days WHERE day >= ?1 AND day <= ?2 AND (?3 = '' OR workspace = ?3)")
1208 .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 daily1209 .all()
1210 .await?
1211 .results::<Snap>()?
1212 .into_iter()
1213 .filter(|s| crate::storage::CHARGED_HERE.contains(&s.source.as_str()) || s.source == "domains")
1214 .map(|s| (s.day, s.workspace, s.source, s.cost_micros, s.charge_micros))
1215 .collect::<Vec<_>>();
Merge costs and margin review: gateway query, own spend, discount meters, superseded rises1216 out.extend(
1217 pending_deltas(&snaps)
1218 .into_iter()
1219 .filter(|u| u.day.as_str() >= since)
1220 .map(|u| if internal.contains(&u.workspace) { comped_meter(u) } else { u }),
1221 );
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1222 // The plan's price, spread over the 30 days it pays for, so a month's
1223 // payment does not read as one very good day and 29 bad ones.
1224 #[derive(Deserialize)]
1225 struct Plan {
1226 day: String,
1227 workspace: String,
1228 micros: Option<i64>,
1229 }
1230 let plans = self
1231 .db
1232 .prepare(
1233 "SELECT substr(paid_at, 1, 10) AS day, workspace, SUM(amount_micros) AS micros FROM plan_payments
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb971234 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 daily1235 )
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb971236 .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 daily1237 .all()
1238 .await?
1239 .results::<Plan>()?;
1240 for p in plans {
1241 for (day, micros) in spread(&p.day, p.micros.unwrap_or(0), PLAN_DAYS) {
1242 if day.as_str() >= since && day.as_str() <= until {
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb971243 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 daily1244 }
1245 }
1246 }
1247 Ok(out)
1248 }
1249
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb971250 /// Which bucket each ledger key (and month-end source) is revenue of.
1251 async fn revenue_map(&self) -> Result<BTreeMap<String, String>> {
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1252 #[derive(Deserialize)]
1253 struct Map {
1254 key: String,
1255 bucket: String,
1256 }
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb971257 Ok(self
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1258 .db
1259 .prepare("SELECT key, bucket FROM revenue_map")
1260 .all()
1261 .await?
1262 .results::<Map>()?
1263 .into_iter()
1264 .map(|m| (m.key, m.bucket))
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb971265 .collect())
1266 }
1267
1268 /// What a testing reset of `workspace` is about to wipe that g1t paid
1269 /// for, a row per day and bucket: its whole ledger and month-end
1270 /// snapshots, valued as the reconciliation values them.
1271 pub(crate) async fn wiped_by_reset(&self, workspace: &str) -> Result<Vec<Wiped>> {
1272 let today = rfc3339(now_ms())[..10].to_owned();
1273 let rows = self.usage_rows(RESET_HISTORY_FROM, &today, Some(workspace)).await?;
1274 Ok(wiped(&rows, &self.revenue_map().await?, self.margin_percent))
1275 }
1276
1277 /// What testing resets kept for the days, as usage rows.
1278 async fn reset_rows(&self, since: &str, until: &str) -> Result<Vec<UsageRow>> {
1279 #[derive(Deserialize)]
1280 struct Kept {
1281 day: String,
1282 workspace: String,
1283 bucket: String,
1284 cost: Option<i64>,
1285 value: Option<i64>,
1286 }
1287 let kept = self
1288 .db
1289 .prepare(
1290 "SELECT day, workspace, bucket, SUM(cost_micros) AS cost, SUM(value_micros) AS value FROM reset_costs
1291 WHERE day >= ?1 AND day <= ?2 AND bucket <> '' GROUP BY day, workspace, bucket",
1292 )
1293 .bind(&[since.into(), until.into()])?
1294 .all()
1295 .await?
1296 .results::<Kept>()?;
1297 Ok(reset_usage(
1298 &kept.into_iter().map(|k| (k.day, k.workspace, k.bucket, k.cost.unwrap_or(0), k.value.unwrap_or(0))).collect::<Vec<_>>(),
1299 ))
1300 }
1301
1302 /// Testing resets on or after `since` (the day they wiped usage up to
1303 /// is their own, so one before it wiped nothing in the days): those
1304 /// that kept what they wiped (`reset_costs`) and those from before
1305 /// resets did, known only from the audit log.
1306 async fn resets_since(&self, since: &str, until: &str) -> Result<Vec<ResetNote>> {
1307 let end = format!("{until}T23:59:59.999Z");
1308 #[derive(Deserialize)]
1309 struct Audit {
1310 account: String,
1311 created_at: String,
1312 }
1313 let audits = self
1314 .db
1315 .prepare("SELECT account, created_at FROM admin_actions WHERE action = 'reset' AND created_at >= ?1 AND created_at <= ?2")
1316 .bind(&[since.into(), end.as_str().into()])?
1317 .all()
1318 .await?
1319 .results::<Audit>()?;
1320 #[derive(Deserialize)]
1321 struct Kept {
1322 workspace: String,
1323 reset_at: String,
1324 models: Option<i64>,
1325 }
1326 let kept = self
1327 .db
1328 .prepare(
1329 "SELECT workspace, reset_at, SUM(CASE WHEN bucket = ?3 AND day >= ?1 THEN cost_micros ELSE 0 END) AS models
1330 FROM reset_costs WHERE reset_at >= ?1 AND reset_at <= ?2 GROUP BY workspace, reset_at",
1331 )
1332 .bind(&[since.into(), end.as_str().into(), NOT_CLOUDFLARE[0].into()])?
1333 .all()
1334 .await?
1335 .results::<Kept>()?;
1336 Ok(reset_notes(
1337 &audits.into_iter().map(|a| (a.account, a.created_at)).collect::<Vec<_>>(),
1338 &kept.into_iter().map(|k| (k.workspace, k.reset_at, k.models.unwrap_or(0))).collect::<Vec<_>>(),
1339 ))
1340 }
1341
1342 /// Reconciles the days and writes `margin_days` and `workspace_costs`.
1343 async fn reconcile_range(&self, since: &str, until: &str) -> Result<u32> {
1344 let rules = self.rules().await?;
1345 let revenue_map = self.revenue_map().await?;
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1346 let lines = self
1347 .db
1348 .prepare("SELECT day, source, product, meter, quantity, cost_usd FROM cost_lines WHERE day >= ?1 AND day <= ?2")
1349 .bind(&[since.into(), until.into()])?
1350 .all()
1351 .await?
1352 .results::<LineRow>()?;
1353 let own = self
1354 .db
1355 .prepare("SELECT day, meter, workspace, quantity FROM own_counts WHERE day >= ?1 AND day <= ?2")
1356 .bind(&[since.into(), until.into()])?
1357 .all()
1358 .await?
1359 .results::<OwnRow>()?;
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb971360 let mut usage = self.usage_rows(since, until, None).await?;
1361 // What testing resets wiped: still paid for, now given away.
1362 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 running1363 #[derive(Deserialize)]
1364 struct Internal {
1365 workspace: String,
1366 }
1367 let internal: BTreeSet<String> = self
1368 .db
1369 .prepare(format!("WITH i(workspace) AS ({}) SELECT DISTINCT workspace FROM i", crate::sales::INTERNAL_SQL))
1370 .all()
1371 .await?
1372 .results::<Internal>()?
1373 .into_iter()
1374 .map(|i| i.workspace)
1375 .collect();
1376 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 daily1377 let now = rfc3339(now_ms());
1378 self.db
1379 .batch(vec![
1380 self.db.prepare("DELETE FROM margin_days WHERE day >= ?1 AND day <= ?2").bind(&[since.into(), until.into()])?,
1381 self.db.prepare("DELETE FROM workspace_costs WHERE day >= ?1 AND day <= ?2").bind(&[since.into(), until.into()])?,
1382 ])
1383 .await?;
1384 for chunk in days.chunks(50) {
1385 let mut statements = Vec::with_capacity(chunk.len());
1386 for d in chunk {
1387 statements.push(
1388 self.db
1389 .prepare(
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb971390 "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)
1391 VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1392 )
1393 .bind(&[
1394 d.day.as_str().into(),
1395 d.bucket.as_str().into(),
1396 (d.cf_cost_micros as f64).into(),
1397 (d.own_cost_micros as f64).into(),
1398 (d.value_micros as f64).into(),
1399 (d.cash_micros as f64).into(),
1400 d.cf_quantity.into(),
1401 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 running1402 (d.given.total() as f64).into(),
1403 (d.given.comped as f64).into(),
1404 (d.given.free as f64).into(),
1405 (d.given.trial as f64).into(),
1406 (d.given.pool as f64).into(),
Merge branch 'worktree-agent-a633ac0f7f66d419d'1407 (d.given.discount as f64).into(),
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging1408 (d.given.credit_promotional as f64).into(),
1409 (d.given.credit_goodwill as f64).into(),
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb971410 (d.given.reset as f64).into(),
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1411 now.as_str().into(),
1412 ])?,
1413 );
1414 }
1415 self.db.batch(statements).await?;
1416 }
1417 for chunk in workspaces.chunks(50) {
1418 let mut statements = Vec::with_capacity(chunk.len());
1419 for w in chunk {
1420 statements.push(
1421 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 it1422 .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 daily1423 .bind(&[
1424 w.day.as_str().into(),
1425 w.workspace.as_str().into(),
1426 w.bucket.as_str().into(),
1427 (w.cost as f64).into(),
1428 (w.revenue as f64).into(),
Margin alerts measure what is sold, and say dollars when a percentage would mislead1429 (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 running1430 (w.given.total() as f64).into(),
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1431 ])?,
1432 );
1433 }
1434 self.db.batch(statements).await?;
1435 }
1436 Ok(costs::days_between(since, until).len() as u32)
1437 }
1438
1439 async fn margin_days(&self, since: &str, until: &str) -> Result<Vec<ProductDay>> {
1440 Ok(self
1441 .db
1442 .prepare("SELECT * FROM margin_days WHERE day >= ?1 AND day <= ?2 ORDER BY day, bucket")
1443 .bind(&[since.into(), until.into()])?
1444 .all()
1445 .await?
1446 .results::<MarginRow>()?
1447 .into_iter()
1448 .map(ProductDay::from)
1449 .collect())
1450 }
1451
Merge branch 'worktree-agent-a633ac0f7f66d419d'1452 /// What AI Gateway's lines over the days, and the runs settled in them,
1453 /// say about whether its cost is what the providers bill.
1454 async fn gateway_caveats(&self, since: &str, until: &str) -> Result<costs::GatewayCaveats> {
1455 #[derive(Deserialize)]
1456 struct Line {
1457 meter: String,
1458 quantity: f64,
1459 cost_usd: f64,
1460 }
1461 let lines: Vec<(String, f64, f64)> = self
1462 .db
1463 .prepare("SELECT meter, quantity, cost_usd FROM cost_lines WHERE source = ?1 AND day >= ?2 AND day <= ?3")
1464 .bind(&[costs::SOURCE_GATEWAY.into(), since.into(), until.into()])?
1465 .all()
1466 .await?
1467 .results::<Line>()?
1468 .into_iter()
1469 .map(|l| (l.meter, l.quantity, l.cost_usd))
1470 .collect();
1471 let mut caveats = costs::gateway_caveats(&lines);
1472 #[derive(Deserialize)]
1473 struct Short {
1474 n: Option<f64>,
1475 }
1476 caveats.short_runs = self
1477 .db
1478 .prepare("SELECT COUNT(*) AS n FROM runs WHERE gateway_note IS NOT NULL AND settled_at >= ?1 AND settled_at <= ?2")
1479 .bind(&[since.into(), format!("{until}T23:59:59.999Z").into()])?
1480 .first::<Short>(None)
1481 .await?
1482 .and_then(|s| s.n)
1483 .unwrap_or(0.0) as u32;
1484 Ok(caveats)
1485 }
1486
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1487 /// Drift over the last week, written to `cost_drift` (replacing the
1488 /// last run's), with unmapped Cloudflare meters as leaks.
Merge costs and margin review: gateway query, own spend, discount meters, superseded rises1489 async fn find_drift(&self, until: &str, read: &costs::GatewayRead) -> Result<Vec<(Drift, String)>> {
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1490 let since = day_before(until, DRIFT_DAYS - 1);
1491 let settings = self.cost_settings().await?;
1492 let rules = self.rules().await?;
1493 let days = self.margin_days(&since, until).await?;
1494 let mut by: BTreeMap<String, Vec<ProductDay>> = BTreeMap::new();
1495 for d in days {
1496 by.entry(d.bucket.clone()).or_default().push(d);
1497 }
Merge branch 'worktree-agent-a633ac0f7f66d419d'1498 let caveats = self.gateway_caveats(&since, until).await?;
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb971499 let resets = self.resets_since(&since, until).await?;
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1500 let mut found = Vec::new();
Merge branch 'worktree-agent-a633ac0f7f66d419d'1501 if let Some(drift) = unpriced_drift(&caveats) {
1502 found.push(drift);
1503 }
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1504 for (bucket, days) in &by {
1505 let bucket_rules: Vec<&Rule> = rules.iter().filter(|r| &r.bucket == bucket).collect();
1506 let threshold = bucket_rules.iter().map(|r| r.drift_percent).fold(f64::INFINITY, f64::min);
1507 let threshold = if threshold.is_finite() { threshold } else { 10.0 };
1508 let counted = bucket_rules.iter().any(|r| r.own_meter.is_some());
1509 for drift in drifts(bucket, days, threshold, counted, settings.min_daily_cost_micros) {
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb971510 if wiped_not_leaked(&drift, &resets) {
1511 continue;
1512 }
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1513 let title = costs::bucket_title(bucket);
1514 let detail = match drift.kind {
Merge costs and margin review: gateway query, own spend, discount meters, superseded rises1515 DriftKind::Cost if NOT_CLOUDFLARE.contains(&bucket.as_str()) => models_detail(&drift, &caveats, &resets, read),
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1516 DriftKind::Count => format!(
One operation mapping, owned by repos; billing reads it instead of keeping its own1517 "{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 daily1518 crate::features::thousands(drift.ours.max(0.0).round() as u64),
1519 crate::features::thousands(drift.cloudflare.max(0.0).round() as u64),
1520 drift.delta_percent.unwrap_or(0.0)
1521 ),
1522 DriftKind::Cost => format!(
1523 "{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.",
1524 dollars(drift.cloudflare as i64),
1525 dollars(drift.ours as i64),
1526 drift.delta_percent.unwrap_or(0.0)
1527 ),
1528 DriftKind::Leak if bucket == UNMAPPED => {
1529 format!("Cloudflare charged {} for meters no mapping claims. Map them on Costs & margin.", dollars(drift.cloudflare as i64))
1530 }
Merge branch 'worktree-agent-a633ac0f7f66d419d'1531 DriftKind::Leak if NOT_CLOUDFLARE.contains(&bucket.as_str()) => format!(
1532 "{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).",
1533 dollars(drift.cloudflare as i64)
1534 ),
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1535 DriftKind::Leak => format!(
1536 "{title}: Cloudflare charged {} over the last {DRIFT_DAYS} days and customers were charged nothing for it.",
1537 dollars(drift.cloudflare as i64)
1538 ),
Merge branch 'worktree-agent-a633ac0f7f66d419d'1539 // Raised from the gateway's lines, not per bucket.
1540 DriftKind::Unpriced => unpriced_detail(&caveats),
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1541 };
1542 found.push((drift, detail));
1543 }
1544 }
1545 let now = rfc3339(now_ms());
1546 let mut statements = vec![self.db.prepare("DELETE FROM cost_drift")];
1547 for (drift, detail) in &found {
1548 statements.push(
1549 self.db
1550 .prepare("INSERT OR REPLACE INTO cost_drift (bucket, kind, ours, cloudflare, delta_percent, detail, found_at) VALUES (?, ?, ?, ?, ?, ?, ?)")
1551 .bind(&[
1552 drift.bucket.as_str().into(),
1553 drift.kind.as_str().into(),
1554 drift.ours.into(),
1555 drift.cloudflare.into(),
1556 drift.delta_percent.map_or(JsValue::NULL, JsValue::from),
1557 detail.as_str().into(),
1558 now.as_str().into(),
1559 ])?,
1560 );
1561 }
1562 self.db.batch(statements).await?;
1563 Ok(found)
1564 }
1565
1566 /// Unit costs from the bill for mappings that scale to g1t's own count
1567 /// (git operations), proposed to the price book.
1568 async fn measure_units(&self, until: &str) -> Result<u32> {
1569 #[derive(Deserialize)]
1570 struct Scaled {
1571 product: String,
1572 meter: String,
1573 price_meter: String,
1574 own_meter: String,
1575 unit: Option<String>,
1576 }
1577 let scaled = self
1578 .db
1579 .prepare(
1580 "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
1581 WHERE m.scale_to_own = 1 AND m.price_meter IS NOT NULL AND m.own_meter IS NOT NULL",
1582 )
1583 .all()
1584 .await?
1585 .results::<Scaled>()?;
1586 let since = day_before(until, MEASURE_DAYS - 1);
1587 let rules = self.rules().await?;
1588 let mut proposed = 0;
1589 for s in scaled {
1590 #[derive(Deserialize)]
1591 struct Day {
1592 product: String,
1593 meter: String,
1594 quantity: f64,
1595 cost_usd: f64,
1596 }
1597 let lines = self
1598 .db
1599 .prepare("SELECT product, meter, quantity, cost_usd FROM cost_lines WHERE source = ?1 AND product = ?2 AND day >= ?3 AND day <= ?4")
1600 .bind(&[SOURCE_BILLABLE.into(), s.product.as_str().into(), since.as_str().into(), until.into()])?
1601 .all()
1602 .await?
1603 .results::<Day>()?;
1604 // Only the lines this very mapping claims.
1605 let mine: Vec<(f64, f64)> = lines
1606 .iter()
1607 .filter(|l| costs::classify(&rules, &l.product, &l.meter).is_some_and(|r| r.product == s.product && r.meter == s.meter))
1608 .map(|l| (l.quantity, l.cost_usd))
1609 .collect();
1610 let Some(rate) = billed_rate(&mine) else { continue };
1611 let cf_units: f64 = mine.iter().map(|(q, _)| q).sum();
1612 #[derive(Deserialize)]
1613 struct Own {
1614 total: Option<f64>,
1615 }
1616 let own_units = self
1617 .db
1618 .prepare("SELECT SUM(quantity) AS total FROM own_counts WHERE meter = ?1 AND day >= ?2 AND day <= ?3")
1619 .bind(&[s.own_meter.as_str().into(), since.as_str().into(), until.into()])?
1620 .first::<Own>(None)
1621 .await?
1622 .and_then(|o| o.total)
1623 .unwrap_or(0.0);
1624 let Some(per_unit) = derived_unit_cost(rate, cf_units, own_units) else { continue };
1625 let size = unit_size(s.unit.as_deref().unwrap_or("1"));
1626 let measured = per_unit * size * 1_000_000.0;
1627 let reason = format!(
1628 "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 {})",
1629 rate * 1000.0,
1630 cf_units / own_units,
1631 crate::features::thousands(cf_units.round() as u64),
1632 crate::features::thousands(own_units.round() as u64)
1633 );
1634 if self.propose(&s.price_meter, measured, &reason, "reconciler").await?.is_some() {
1635 proposed += 1;
1636 }
1637 }
1638 Ok(proposed)
1639 }
1640
1641 /// Opens, updates and closes margin alerts, and emails staff about new
1642 /// ones (and open ones each week).
1643 async fn raise_alerts(&self, env: &Env, until: &str, drift: &[(Drift, String)]) -> Result<u32> {
1644 let settings = self.cost_settings().await?;
1645 let since = day_before(until, u64::from(settings.alert_days.max(1)) - 1);
1646 let days = self.margin_days(&since, until).await?;
1647 let mut conditions: Vec<(String, String, String, String)> = Vec::new();
1648 // Each product under the floor.
1649 let mut by: BTreeMap<String, Vec<(String, i64, i64)>> = BTreeMap::new();
1650 let mut all: BTreeMap<String, (i64, i64)> = BTreeMap::new();
1651 for d in &days {
1652 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 it1653 // What g1t gave away (comped workspaces, free periods, the
1654 // trial and the pools) is a budget it chose to spend, watched on
1655 // 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 daily1656 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 running1657 overall.1 += (d.cost() - d.given.total()).max(0);
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1658 if !OVERHEAD.contains(&d.bucket.as_str()) && d.bucket != UNMAPPED {
1659 by.entry(d.bucket.clone()).or_default().push((d.day.clone(), d.value_micros, d.cost()));
1660 }
1661 }
1662 let floor = settings.margin_floor_percent;
1663 let n = settings.alert_days as usize;
1664 for (bucket, series) in &by {
1665 if let Some((from, worst)) = breach(series, floor, n, settings.min_daily_cost_micros) {
1666 conditions.push((
1667 "margin".into(),
1668 bucket.clone(),
1669 format!("{}: margin under {floor:.0}% for {n} days running, as low as {worst:.1}%.", costs::bucket_title(bucket)),
1670 from,
1671 ));
Margin alerts measure what is sold, and say dollars when a percentage would mislead1672 }
1673 }
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1674 let series: Vec<(String, i64, i64)> = all.into_iter().map(|(day, (revenue, cost))| (day, revenue, cost)).collect();
1675 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 mislead1676 let tail = &series[series.len().saturating_sub(n)..];
1677 let (took, spent) = tail.iter().fold((0i64, 0i64), |(r, c), (_, revenue, cost)| (r + revenue, c + cost));
1678 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 daily1679 }
1680 for (d, detail) in drift {
1681 let kind = if d.kind == DriftKind::Leak { "leak" } else { "drift" };
1682 conditions.push((kind.into(), format!("{}:{}", d.bucket, d.kind.as_str()), detail.clone(), until.to_owned()));
1683 }
1684 // Workspaces costing more than they pay.
1685 for (workspace, cost, revenue) in self.workspace_anomalies(until, &settings).await? {
1686 conditions.push((
1687 "workspace".into(),
1688 workspace.clone(),
Margin alerts measure what is sold, and say dollars when a percentage would mislead1689 format!(
1690 "{workspace} cost g1t {} on Cloudflare over {ANOMALY_DAYS} days, and its usage was priced at {}: its prices are below cost.",
1691 dollars(cost),
1692 dollars(revenue)
1693 ),
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1694 day_before(until, ANOMALY_DAYS - 1),
1695 ));
1696 }
1697
1698 let open = self
1699 .db
1700 .prepare("SELECT * FROM margin_alerts WHERE resolved_at IS NULL")
1701 .all()
1702 .await?
1703 .results::<AlertRow>()?;
1704 let now = now_ms();
1705 let stamp = rfc3339(now);
1706 let mut to_email: Vec<String> = Vec::new();
1707 let mut kept: BTreeSet<String> = BTreeSet::new();
1708 for (kind, subject, detail, from) in &conditions {
1709 match open.iter().find(|a| &a.kind == kind && &a.subject == subject) {
1710 Some(alert) => {
1711 kept.insert(alert.id.clone());
1712 self.db
1713 .prepare("UPDATE margin_alerts SET detail = ? WHERE id = ?")
1714 .bind(&[detail.as_str().into(), alert.id.as_str().into()])?
1715 .run()
1716 .await?;
1717 let stale = alert
1718 .emailed_at
1719 .as_deref()
1720 .and_then(g1t_contracts::time::parse_rfc3339)
1721 .is_none_or(|at| now.saturating_sub(at) >= REMIND_MS);
1722 if stale && kind != "workspace" {
1723 to_email.push(format!("Still open: {detail}"));
1724 kept.insert(format!("email:{}", alert.id));
1725 }
1726 }
1727 None => {
1728 let id = new_id("mal", now);
1729 self.db
1730 .prepare("INSERT INTO margin_alerts (id, kind, subject, detail, since, opened_at) VALUES (?, ?, ?, ?, ?, ?)")
1731 .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()])?
1732 .run()
1733 .await?;
1734 kept.insert(id.clone());
Margin alerts measure what is sold, and say dollars when a percentage would mislead1735 // A workspace's is for Reach out, not the inbox.
1736 if kind != "workspace" {
1737 to_email.push(detail.clone());
1738 }
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1739 kept.insert(format!("email:{id}"));
1740 }
1741 }
1742 }
1743 for alert in &open {
1744 if !kept.contains(&alert.id) {
1745 self.db
1746 .prepare("UPDATE margin_alerts SET resolved_at = ? WHERE id = ?")
1747 .bind(&[stamp.as_str().into(), alert.id.as_str().into()])?
1748 .run()
1749 .await?;
1750 }
1751 }
1752 let to = env.var("COSTS_ALERT_EMAIL").map(|v| v.to_string()).unwrap_or_default();
1753 if !to_email.is_empty() && !to.trim().is_empty() {
1754 let subject = format!("[g1t costs] {} margin alert{}", to_email.len(), if to_email.len() == 1 { "" } else { "s" });
1755 match email_staff(env, to.trim(), &subject, &to_email).await {
1756 Ok(()) => {
1757 for marker in kept.iter().filter_map(|k| k.strip_prefix("email:")) {
1758 self.db
1759 .prepare("UPDATE margin_alerts SET emailed_at = ? WHERE id = ?")
1760 .bind(&[stamp.as_str().into(), marker.into()])?
1761 .run()
1762 .await?;
1763 }
1764 }
1765 Err(error) => worker::console_error!("could not email the margin alerts: {error}"),
1766 }
1767 }
1768 Ok(conditions.len() as u32)
1769 }
1770
1771 /// 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 mislead1772 /// Each day's cost shared out to comped workspaces.
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1773 async fn workspace_anomalies(&self, until: &str, settings: &CostSettings) -> Result<Vec<(String, i64, i64)>> {
1774 #[derive(Deserialize)]
1775 struct Row {
1776 workspace: String,
1777 cost: Option<i64>,
1778 revenue: Option<i64>,
1779 }
1780 let rows = self
1781 .db
1782 .prepare(format!(
Margin alerts measure what is sold, and say dollars when a percentage would mislead1783 // Against what its usage was priced at, not the cash it
1784 // paid: a trial or a gift paying for usage is not a price
1785 // below cost.
The workspace cost alert compares only days that carry their value, not the days before it was kept1786 // Days from before value_micros was kept have none: only days
1787 // since the first one that does are compared.
Margin alerts measure what is sold, and say dollars when a percentage would mislead1788 "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 kept1789 WHERE day >= ?1 AND day <= ?2 AND workspace NOT IN ({})
1790 AND day >= (SELECT MIN(day) FROM workspace_costs WHERE value_micros > 0)
1791 GROUP BY workspace",
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1792 crate::sales::INTERNAL_SQL
1793 ))
1794 .bind(&[day_before(until, ANOMALY_DAYS - 1).into(), until.into()])?
1795 .all()
1796 .await?
1797 .results::<Row>()?;
1798 let rows: Vec<(String, i64, i64)> = rows.into_iter().map(|r| (r.workspace, r.cost.unwrap_or(0), r.revenue.unwrap_or(0))).collect();
1799 Ok(anomalies(&rows, settings.anomaly_factor, settings.anomaly_floor_micros))
1800 }
1801
1802 /// For Reach out: workspaces with an open cost-over-revenue alert,
1803 /// each with its detail and cost.
1804 pub(crate) async fn costing_more_than_they_pay(&self) -> Result<Vec<(String, String, i64)>> {
1805 let alerts = self
1806 .db
1807 .prepare("SELECT * FROM margin_alerts WHERE resolved_at IS NULL AND kind = 'workspace' ORDER BY opened_at DESC LIMIT 50")
1808 .all()
1809 .await?
1810 .results::<AlertRow>()?;
1811 let mut out = Vec::new();
1812 for alert in alerts {
1813 #[derive(Deserialize)]
1814 struct Cost {
1815 cost: Option<i64>,
1816 }
1817 let cost = self
1818 .db
1819 .prepare("SELECT SUM(cost_micros) AS cost FROM workspace_costs WHERE workspace = ? AND day >= ?")
1820 .bind(&[alert.subject.as_str().into(), alert.since.as_str().into()])?
1821 .first::<Cost>(None)
1822 .await?
1823 .and_then(|c| c.cost)
1824 .unwrap_or(0);
1825 out.push((alert.subject, alert.detail, cost));
1826 }
1827 Ok(out)
1828 }
1829
1830 /// `admin_cost_alerts`: what sudo's banner says.
1831 pub(crate) async fn admin_cost_alerts(&self, _: AdminCostAlertsArgs) -> Result<Vec<MarginAlert>> {
1832 Ok(self
1833 .db
1834 .prepare("SELECT * FROM margin_alerts WHERE resolved_at IS NULL ORDER BY opened_at DESC LIMIT 50")
1835 .all()
1836 .await?
1837 .results::<AlertRow>()?
1838 .into_iter()
1839 .map(MarginAlert::from)
1840 .collect())
1841 }
1842
1843 /// `admin_run_costs`: the daily run, now.
1844 pub(crate) async fn admin_run_costs(&self, env: &Env, a: AdminRunCostsArgs) -> Result<Outcome<CostsRun>> {
1845 let keeper = crate::keeper::Keeper::from_env(env);
1846 let run = self.costs_daily(env, &keeper).await?;
1847 if !a.by.is_empty() {
1848 self.audit(
1849 "costs",
1850 "costs_run",
1851 &format!("{} lines, {} days, {} proposals, {} alerts", run.lines, run.days, run.proposals, run.alerts),
1852 &a.by,
1853 )
1854 .await?;
1855 }
1856 Ok(Outcome::Ok(run))
1857 }
1858
1859 /// `admin_set_cost_mapping`.
1860 pub(crate) async fn admin_set_cost_mapping(&self, a: AdminSetCostMappingArgs) -> Result<Outcome<CostMapping>> {
1861 let product = costs::slug(&a.product);
1862 let meter = if a.meter.trim() == "*" { "*".to_owned() } else { costs::slug(&a.meter) };
1863 if product.is_empty() || meter.is_empty() {
1864 return Ok(Outcome::fail(FailureCode::Invalid, "Name Cloudflare's product and a meter (or * for all of it)."));
1865 }
1866 let now = rfc3339(now_ms());
1867 if a.remove {
1868 self.db
1869 .prepare("DELETE FROM cost_map WHERE product = ? AND meter = ?")
1870 .bind(&[product.as_str().into(), meter.as_str().into()])?
1871 .run()
1872 .await?;
1873 self.audit("costs", "cost_mapping_removed", &format!("{product}/{meter}"), &a.by).await?;
1874 return Ok(Outcome::Ok(CostMapping {
1875 product,
1876 meter,
1877 bucket: String::new(),
1878 price_meter: None,
1879 own_meter: None,
1880 scale_to_own: false,
1881 drift_percent: 0.0,
1882 note: String::new(),
1883 updated_at: now,
1884 updated_by: a.by,
1885 }));
1886 }
1887 let bucket = costs::slug(&a.bucket);
1888 if bucket.is_empty() {
1889 return Ok(Outcome::fail(FailureCode::Invalid, "Say which of g1t's products it is a cost of."));
1890 }
1891 let clean = |v: Option<String>| v.map(|v| v.trim().to_owned()).filter(|v| !v.is_empty());
1892 let (price_meter, own_meter) = (clean(a.price_meter), clean(a.own_meter));
1893 let drift = a.drift_percent.filter(|d| d.is_finite() && *d > 0.0).unwrap_or(10.0);
1894 self.db
1895 .prepare(
1896 "INSERT INTO cost_map (product, meter, bucket, price_meter, own_meter, scale_to_own, drift_percent, note, updated_at, updated_by)
1897 VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10)
1898 ON CONFLICT (product, meter) DO UPDATE SET bucket = ?3, price_meter = ?4, own_meter = ?5, scale_to_own = ?6,
1899 drift_percent = ?7, note = ?8, updated_at = ?9, updated_by = ?10",
1900 )
1901 .bind(&[
1902 product.as_str().into(),
1903 meter.as_str().into(),
1904 bucket.as_str().into(),
1905 crate::optional(price_meter.as_deref()),
1906 crate::optional(own_meter.as_deref()),
1907 i32::from(a.scale_to_own).into(),
1908 drift.into(),
1909 a.note.trim().into(),
1910 now.as_str().into(),
1911 a.by.as_str().into(),
1912 ])?
1913 .run()
1914 .await?;
1915 self.audit("costs", "cost_mapping", &format!("{product}/{meter} → {bucket}"), &a.by).await?;
1916 Ok(Outcome::Ok(CostMapping {
1917 product,
1918 meter,
1919 bucket,
1920 price_meter,
1921 own_meter,
1922 scale_to_own: a.scale_to_own,
1923 drift_percent: drift,
1924 note: a.note.trim().to_owned(),
1925 updated_at: now,
1926 updated_by: a.by,
1927 }))
1928 }
1929
1930 /// `admin_costs`: the Costs & margin page.
1931 pub(crate) async fn admin_costs(&self, a: AdminCostsArgs, configured: bool) -> Result<CostsReport> {
1932 let until = rfc3339(now_ms())[..10].to_owned();
1933 let span = u64::from(a.days.unwrap_or(30).clamp(7, 90));
1934 let since = day_before(&until, span - 1);
1935 let days = self.margin_days(&since, &until).await?;
1936 let rules = self.rules().await?;
1937
1938 let mut products: BTreeMap<String, ProductMargin> = BTreeMap::new();
1939 let mut overall = OverallMargin::default();
1940 for d in &days {
1941 let p = products.entry(d.bucket.clone()).or_insert_with(|| ProductMargin {
1942 bucket: d.bucket.clone(),
1943 title: costs::bucket_title(&d.bucket),
1944 cost_source: if NOT_CLOUDFLARE.contains(&d.bucket.as_str()) { "ledger" } else { "cloudflare" }.into(),
1945 overhead: OVERHEAD.contains(&d.bucket.as_str()),
1946 ..ProductMargin::default()
1947 });
1948 p.cf_cost_micros += d.cf_cost_micros;
1949 p.own_cost_micros += d.own_cost_micros;
1950 p.value_micros += d.value_micros;
1951 p.cost_micros += d.cost();
1952 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 running1953 overall.given_micros += d.given.total();
1954 if NOT_CLOUDFLARE.contains(&d.bucket.as_str()) {
1955 overall.models_cost_micros += d.cost();
1956 } else {
1957 overall.cloudflare_cost_micros += d.cost();
1958 }
1959 overall.given_comped_micros += d.given.comped;
1960 overall.given_free_micros += d.given.free;
1961 overall.given_trial_micros += d.given.trial;
1962 overall.given_pool_micros += d.given.pool;
Merge branch 'worktree-agent-a633ac0f7f66d419d'1963 overall.given_discount_micros += d.given.discount;
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging1964 overall.given_credit_promotional_micros += d.given.credit_promotional;
1965 overall.given_credit_goodwill_micros += d.given.credit_goodwill;
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb971966 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 running1967 let sold = (d.cost() - d.given.total()).max(0);
1968 if OVERHEAD.contains(&d.bucket.as_str()) {
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1969 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 running1970 overall.running_cost_micros += sold;
1971 } else if d.bucket == UNMAPPED {
1972 overall.usage_micros += d.cash_micros;
1973 overall.unmapped_cost_micros += sold;
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1974 } else {
1975 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 running1976 overall.usage_cost_micros += sold;
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1977 }
1978 }
1979 for p in products.values_mut() {
1980 p.margin_micros = p.value_micros - p.cost_micros;
1981 p.margin_percent = margin_percent(p.value_micros, p.cost_micros);
1982 }
1983 let revenue = overall.usage_micros + overall.plans_micros;
1984 overall.margin_micros = revenue - overall.cost_micros;
1985 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 it1986 let sold = (overall.cost_micros - overall.given_micros).max(0);
1987 overall.sold_margin_micros = revenue - sold;
1988 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)1989 // The plan's included usage was paid for by the plan's price: it is
1990 // money in for the usage it covered, and out of what the plans
1991 // leave for running g1t.
1992 #[derive(Deserialize)]
1993 struct Included {
1994 micros: Option<i64>,
1995 }
1996 overall.included_micros = self
1997 .db
1998 .prepare(format!(
1999 "SELECT SUM(COALESCE(credit_micros, 0)) AS micros FROM ledger
2000 WHERE kind = 'usage' AND created_at >= ?1 AND created_at <= ?2 AND workspace NOT IN ({})",
2001 crate::sales::INTERNAL_SQL
2002 ))
2003 .bind(&[since.as_str().into(), format!("{until}T23:59:59.999Z").into()])?
2004 .first::<Included>(None)
2005 .await?
2006 .and_then(|r| r.micros)
2007 .unwrap_or(0);
2008 let usage_in = overall.usage_micros + overall.included_micros;
2009 overall.usage_margin_micros = usage_in - overall.usage_cost_micros;
2010 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 charging2011 // Credits from g1t over the range: given, spent, and refunds' money
2012 // given back.
2013 overall.credits_given_micros = self
2014 .db
2015 .prepare("SELECT SUM(amount_micros) AS micros FROM credit_grants WHERE created_at >= ?1 AND created_at <= ?2")
2016 .bind(&[since.as_str().into(), format!("{until}T23:59:59.999Z").into()])?
2017 .first::<Included>(None)
2018 .await?
2019 .and_then(|r| r.micros)
2020 .unwrap_or(0);
2021 let (draws, refunds) = self.credit_effects(&since, &until).await?;
2022 overall.credits_used_micros = draws.iter().map(|(_, d)| d.micros).sum();
2023 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 person2024 // Tax and card fees came in with payments but are neither cash nor
2025 // revenue: balances and plan payments are credited without them
2026 // (tax.rs), so cash above never holds them. Shown apart.
2027 (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 daily2028 let mut products: Vec<ProductMargin> = products.into_values().collect();
2029 products.sort_by_key(|p| std::cmp::Reverse(p.cost_micros.max(p.value_micros)));
2030
2031 #[derive(Deserialize)]
2032 struct DriftRow {
2033 bucket: String,
2034 kind: String,
2035 ours: f64,
2036 cloudflare: f64,
2037 delta_percent: Option<f64>,
2038 detail: String,
2039 found_at: String,
2040 }
2041 let drift = self
2042 .db
2043 .prepare("SELECT * FROM cost_drift ORDER BY kind, bucket")
2044 .all()
2045 .await?
2046 .results::<DriftRow>()?
2047 .into_iter()
2048 .map(|r| CostDrift {
2049 title: costs::bucket_title(&r.bucket),
2050 bucket: r.bucket,
2051 kind: r.kind,
2052 ours: r.ours,
2053 cloudflare: r.cloudflare,
2054 delta_percent: r.delta_percent,
2055 detail: r.detail,
2056 found_at: r.found_at,
2057 })
2058 .collect();
2059
2060 #[derive(Deserialize)]
2061 struct Top {
2062 workspace: String,
2063 cost: Option<i64>,
2064 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 it2065 given: Option<i64>,
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily2066 internal: i64,
2067 }
2068 let top_workspaces = self
2069 .db
2070 .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 it2071 "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 daily2072 CASE WHEN workspace IN ({}) THEN 1 ELSE 0 END AS internal
2073 FROM workspace_costs WHERE day >= ?1 AND day <= ?2 GROUP BY workspace ORDER BY cost DESC LIMIT 15",
2074 crate::sales::INTERNAL_SQL
2075 ))
2076 .bind(&[since.as_str().into(), until.as_str().into()])?
2077 .all()
2078 .await?
2079 .results::<Top>()?
2080 .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 it2081 .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 daily2082 .collect();
2083
2084 #[derive(Deserialize)]
2085 struct Summary {
2086 source: String,
2087 product: String,
2088 meter: String,
2089 raw_name: String,
2090 unit: String,
2091 quantity: f64,
2092 cost_usd: f64,
2093 }
2094 let lines = self
2095 .db
2096 .prepare(
2097 "SELECT source, product, meter, MAX(raw_name) AS raw_name, MAX(unit) AS unit, SUM(quantity) AS quantity, SUM(cost_usd) AS cost_usd
2098 FROM cost_lines WHERE day >= ?1 AND day <= ?2 GROUP BY source, product, meter ORDER BY cost_usd DESC, product, meter LIMIT 200",
2099 )
2100 .bind(&[since.as_str().into(), until.as_str().into()])?
2101 .all()
2102 .await?
2103 .results::<Summary>()?
2104 .into_iter()
2105 .map(|l| CostLineSummary {
2106 bucket: costs::classify(&rules, &l.product, &l.meter).map(|r| r.bucket.clone()),
2107 product: l.product,
2108 meter: l.meter,
2109 raw_name: l.raw_name,
2110 unit: l.unit,
2111 source: l.source,
2112 quantity: l.quantity,
2113 cost_micros: micros(l.cost_usd),
2114 })
2115 .collect();
2116
2117 #[derive(Deserialize)]
2118 struct MapRow {
2119 product: String,
2120 meter: String,
2121 bucket: String,
2122 price_meter: Option<String>,
2123 own_meter: Option<String>,
2124 scale_to_own: i64,
2125 drift_percent: f64,
2126 note: String,
2127 updated_at: String,
2128 updated_by: String,
2129 }
2130 let mappings = self
2131 .db
2132 .prepare("SELECT * FROM cost_map ORDER BY product, meter")
2133 .all()
2134 .await?
2135 .results::<MapRow>()?
2136 .into_iter()
2137 .map(|m| CostMapping {
2138 product: m.product,
2139 meter: m.meter,
2140 bucket: m.bucket,
2141 price_meter: m.price_meter,
2142 own_meter: m.own_meter,
2143 scale_to_own: m.scale_to_own == 1,
2144 drift_percent: m.drift_percent,
2145 note: m.note,
2146 updated_at: m.updated_at,
2147 updated_by: m.updated_by,
2148 })
2149 .collect();
2150
2151 #[derive(Deserialize)]
2152 struct Fetched {
2153 at: Option<String>,
2154 }
2155 let fetched_at = self.db.prepare("SELECT MAX(fetched_at) AS at FROM cost_lines").first::<Fetched>(None).await?.and_then(|f| f.at);
2156
2157 Ok(CostsReport {
2158 configured,
2159 fetched_at,
2160 days: days
2161 .iter()
2162 .map(|d| CostDay {
2163 day: d.day.clone(),
2164 bucket: d.bucket.clone(),
2165 cf_cost_micros: d.cf_cost_micros,
2166 own_cost_micros: d.own_cost_micros,
2167 value_micros: d.value_micros,
2168 cash_micros: d.cash_micros,
2169 })
2170 .collect(),
2171 since,
2172 until,
2173 products,
2174 overall,
2175 drift,
2176 alerts: self.admin_cost_alerts(AdminCostAlertsArgs {}).await?,
2177 proposals: self.proposals().await?,
2178 versions: self.versions().await?,
2179 top_workspaces,
2180 lines,
2181 mappings,
2182 settings: self.cost_settings().await?,
Spend caps: a monthly budget for comped workspaces and a daily breaker on what g1t pays2183 caps: self.spend_caps().await?,
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily2184 })
2185 }
2186}
2187
2188#[cfg(test)]
2189mod tests {
2190 use super::*;
2191
Margin alerts measure what is sold, and say dollars when a percentage would mislead2192 #[test]
Models' margin read -14%: usage nothing paid for is valued at price, not $02193 fn usage_nothing_paid_for_is_valued_at_price_and_paid_usage_at_what_was_paid() {
2194 // A free period: charged nothing, drawn from nothing.
2195 assert_eq!(usage_value(false, 1_000_000, 0, 20), 1_200_000);
2196 // Charged, or drawn from a trial: what was paid.
2197 assert_eq!(usage_value(false, 1_000_000, 1_200_000, 20), 1_200_000);
2198 assert_eq!(usage_value(false, 1_000_000, 900_000, 20), 900_000);
2199 // g1t's own: at price.
2200 assert_eq!(usage_value(true, 1_000_000, 0, 20), 1_200_000);
2201 // No cost, nothing paid: nothing.
2202 assert_eq!(usage_value(false, 0, 0, 20), 0);
2203 }
2204
2205 #[test]
Margin alerts measure what is sold, and say dollars when a percentage would mislead2206 fn the_overall_alert_says_dollars_while_little_comes_in() {
2207 let small = overall_detail(90_000, 7_500_000, 3, 10.0, -8239.7);
2208 assert!(small.contains("took in $0.09 against $7.50"), "{small}");
2209 assert!(!small.contains('%'), "{small}");
2210 let real = overall_detail(30_000_000, 40_000_000, 3, 10.0, -33.3);
2211 assert!(real.contains("as low as -33.3%"), "{real}");
2212 }
2213
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily2214 fn rule(product: &str, meter: &str, bucket: &str, own: Option<&str>) -> Rule {
2215 Rule { product: product.into(), meter: meter.into(), bucket: bucket.into(), price_meter: None, own_meter: own.map(Into::into), drift_percent: 10.0 }
2216 }
2217
2218 fn rules() -> Vec<Rule> {
2219 vec![
2220 rule("containers", "*", "sandboxes", None),
2221 rule("workers", "*", "platform", None),
2222 rule("artifacts", "*", "git", Some("git_operations")),
2223 rule("artifacts", "events_", "git", Some("git_operations")),
2224 ]
2225 }
2226
2227 fn revenue_map() -> BTreeMap<String, String> {
2228 [("sandbox", "sandboxes"), ("git", "git"), ("plan", "platform")].iter().map(|(k, v)| (k.to_string(), v.to_string())).collect()
2229 }
2230
2231 fn line(day: &str, source: &str, product: &str, meter: &str, quantity: f64, cost: f64) -> LineRow {
2232 LineRow { day: day.into(), source: source.into(), product: product.into(), meter: meter.into(), quantity, cost_usd: cost }
2233 }
2234
2235 fn usage(day: &str, workspace: &str, key: &str, value: i64, cash: i64, cost: i64) -> UsageRow {
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb972236 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 daily2237 }
2238
2239 #[test]
2240 fn a_day_puts_the_bill_g1ts_counts_and_charges_side_by_side() {
2241 let lines = vec![
2242 line("2026-10-15", SOURCE_BILLABLE, "containers", "container_memory", 1000.0, 2.00),
2243 line("2026-10-15", SOURCE_BILLABLE, "artifacts", "artifacts_operations", 30_000.0, 3.00),
2244 // Artifacts' own events: not used while the bill has a count.
2245 line("2026-10-15", SOURCE_ARTIFACTS, "artifacts", "events_pull", 29_000.0, 0.0),
2246 line("2026-10-15", SOURCE_BILLABLE, "workers", "workers_cpu_ms", 1.0, 0.50),
2247 line("2026-10-15", SOURCE_BILLABLE, "browser_rendering", "browser_hours", 2.0, 0.25),
2248 ];
2249 let own = vec![
2250 OwnRow { day: "2026-10-15".into(), meter: "git_operations".into(), workspace: "acme".into(), quantity: 7_500.0 },
2251 OwnRow { day: "2026-10-15".into(), meter: "git_operations".into(), workspace: "beta".into(), quantity: 2_500.0 },
2252 ];
2253 let usage = vec![
2254 usage("2026-10-15", "acme", "sandbox", 2_400_000, 1_000_000, 2_000_000),
2255 usage("2026-10-15", "beta", "sandbox", 1_200_000, 1_200_000, 1_000_000),
2256 usage("2026-10-15", "acme", "git", 600_000, 600_000, 500_000),
2257 usage("2026-10-15", "acme", "implement", 120_000, 120_000, 100_000),
2258 usage("2026-10-15", "beta", "plan", 20_000_000, 20_000_000, 0),
2259 ];
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 running2260 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 daily2261 let get = |bucket: &str| days.iter().find(|d| d.bucket == bucket).unwrap();
2262 let sandboxes = get("sandboxes");
2263 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));
2264 let git = get("git");
2265 assert_eq!(git.cf_cost_micros, 3_000_000);
2266 assert_eq!((git.cf_quantity, git.own_quantity), (30_000.0, 10_000.0));
2267 assert_eq!(get("platform").value_micros, 20_000_000);
2268 // Not mapped: a leak until someone maps it.
2269 assert_eq!(get(UNMAPPED).cf_cost_micros, 250_000);
2270 // Models: no Cloudflare line, their cost is g1t's own.
2271 assert_eq!(get("models").cost(), 100_000);
2272 // Git's cost shared by g1t's own counts (Cloudflare gave none per
2273 // workspace here): three quarters to acme.
2274 let share = |ws: &str, bucket: &str| workspaces.iter().find(|w| w.workspace == ws && w.bucket == bucket).map(|w| (w.cost, w.revenue));
2275 assert_eq!(share("acme", "git"), Some((2_250_000, 600_000)));
2276 assert_eq!(share("beta", "git"), Some((750_000, 0)));
2277 // Every bucket's cost is shared out exactly.
2278 for d in &days {
2279 let shared: i64 = workspaces.iter().filter(|w| w.bucket == d.bucket).map(|w| w.cost).sum();
2280 assert_eq!(shared, d.cost(), "{}", d.bucket);
2281 }
2282 }
2283
2284 #[test]
Merge costs and margin review: gateway query, own spend, discount meters, superseded rises2285 fn a_planning_run_is_model_cost_not_running_g1t() {
2286 // flagon-io's planning run on 2026-10-07 cost $0.0748 of model
2287 // calls; read under the ledger's task, `plan`, it went to running
2288 // g1t, where Cloudflare's bill is the cost, and the model cost was
2289 // lost from the statement and the drift.
2290 let usage = vec![
2291 usage("2026-10-07", "flagon-io", PLANNING_KEY, 89_741, 0, 74_784),
2292 usage("2026-10-07", "acme", "plan", 666_666, 666_666, 0),
2293 ];
2294 let (days, _) = fold(&rules(), &revenue_map(), &[], &[], &usage, &BTreeSet::new());
2295 let get = |bucket: &str| days.iter().find(|d| d.bucket == bucket).unwrap();
2296 assert_eq!((get("models").cost(), get("models").value_micros), (74_784, 89_741));
2297 assert_eq!((get("platform").own_cost_micros, get("platform").cash_micros), (0, 666_666));
2298 assert!(!revenue_map().contains_key(PLANNING_KEY));
2299 }
2300
2301 #[test]
2302 fn a_comped_workspaces_month_end_meters_are_given_never_money_in() {
2303 let snap = |day: &str, cost: i64, charge: i64| (day.to_string(), "flagon-io".to_string(), "cache".to_string(), cost, charge);
2304 let rows: Vec<UsageRow> = pending_deltas(&[snap("2026-10-07", 1, 2), snap("2026-10-08", 473, 568)]).into_iter().map(comped_meter).collect();
2305 assert_eq!(rows.iter().map(|r| (r.cash, r.value, r.given.comped)).collect::<Vec<_>>(), vec![(0, 2, 2), (0, 566, 566)]);
2306 let internal: BTreeSet<String> = ["flagon-io".to_string()].into();
2307 let (days, workspaces) = fold(&rules(), &revenue_map(), &[], &[], &rows, &internal);
2308 assert!(days.iter().all(|d| d.cash_micros == 0));
2309 assert!(workspaces.iter().all(|w| w.revenue == 0));
2310 }
2311
2312 #[test]
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily2313 fn artifacts_events_count_when_the_bill_does_not() {
2314 let lines = vec![
2315 line("2026-10-05", SOURCE_ARTIFACTS, "artifacts", "events_pull", 120.0, 0.0),
2316 line("2026-10-05", SOURCE_ARTIFACTS, "artifacts", "events_push", 30.0, 0.0),
2317 line("2026-10-05", SOURCE_ARTIFACTS, "artifacts", "events_ratelimited", 9.0, 0.0),
2318 ];
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 running2319 let (days, _) = fold(&rules(), &revenue_map(), &lines, &[], &[], &BTreeSet::new());
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily2320 assert_eq!(days[0].cf_quantity, 150.0);
2321 assert_eq!(days[0].cf_cost_micros, 0);
2322 }
2323
2324 #[test]
2325 fn month_end_meters_are_told_by_the_day_from_snapshots() {
2326 let snap = |day: &str, cost: i64, charge: i64| (day.to_string(), "acme".to_string(), "git".to_string(), cost, charge);
2327 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)]);
2328 assert_eq!(
2329 rows.iter().map(|r| (r.day.as_str(), r.cost, r.value)).collect::<Vec<_>>(),
2330 vec![("2026-10-30", 100, 120), ("2026-10-31", 150, 180), ("2026-11-01", 40, 48)]
2331 );
2332 }
2333
2334 #[test]
2335 fn a_plan_payment_is_spread_over_the_month_it_pays_for() {
2336 let days = spread("2026-10-01T00:00:00.000Z", 20_000_000, 30);
2337 assert_eq!(days.len(), 30);
2338 assert_eq!(days[0], ("2026-10-01".to_string(), 666_667));
2339 assert_eq!(days[29], ("2026-10-30".to_string(), 666_666));
2340 assert_eq!(days.iter().map(|d| d.1).sum::<i64>(), 20_000_000);
2341 assert!(spread("2026-10-01", 0, 30).is_empty());
2342 assert_eq!(dollars(17_024_000), "$17.02");
2343 assert_eq!(dollars(-27_668_620), "-$27.67");
2344 assert_eq!(dollars(63_000), "$0.063");
2345 }
2346
2347 #[test]
2348 fn margins_and_deltas() {
2349 assert_eq!(margin_percent(1_200_000, 1_000_000).map(|m| (m * 100.0).round() / 100.0), Some(16.67));
2350 assert_eq!(margin_percent(0, 5), None);
2351 assert_eq!(delta_percent(110.0, 100.0), Some(10.0));
2352 assert_eq!(delta_percent(1.0, 0.0), None);
2353 }
2354
2355 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 running2356 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 daily2357 }
2358
2359 #[test]
2360 fn counts_more_than_the_threshold_apart_are_drift() {
2361 // Cloudflare counted 30,000 operations where g1t counted 10,000:
2362 // binding reads, perhaps. -66.7%.
2363 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);
2364 assert_eq!(drift.iter().map(|d| d.kind).collect::<Vec<_>>(), vec![DriftKind::Count, DriftKind::Cost]);
2365 assert!((drift[0].delta_percent.unwrap() + 66.666).abs() < 0.01);
2366 // 9% apart: within 10%.
2367 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());
2368 // Uncounted products have no count drift.
2369 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());
2370 }
2371
2372 #[test]
2373 fn cost_with_no_revenue_is_a_leak_but_not_for_running_g1t() {
2374 let leak = drifts("actions_cache", &[day("actions_cache", 400_000, 0, 0, 0.0, 0.0)], 10.0, false, 100_000);
2375 assert_eq!(leak.len(), 1);
2376 assert_eq!(leak[0].kind, DriftKind::Leak);
2377 assert!(drifts("platform", &[day("platform", 5_000_000, 0, 0, 0.0, 0.0)], 10.0, false, 100_000).is_empty());
2378 // Pennies say nothing.
2379 assert!(drifts("actions_cache", &[day("actions_cache", 50_000, 0, 0, 0.0, 0.0)], 10.0, false, 100_000).is_empty());
2380 assert!(drifts(UNMAPPED, &[day(UNMAPPED, 250_000, 0, 0, 0.0, 0.0)], 10.0, false, 100_000)[0].kind == DriftKind::Leak);
2381 }
2382
2383 #[test]
2384 fn a_margin_alert_needs_n_days_in_a_row_under_the_floor() {
2385 let s = |d: &str, revenue: i64, cost: i64| (d.to_string(), revenue, cost);
2386 // 5%, 0%, -20%: three days under 10%.
2387 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)];
2388 let (from, worst) = breach(&series, 10.0, 3, 100_000).unwrap();
2389 assert_eq!(from, "10-14");
2390 assert!((worst + 20.0).abs() < 1e-9);
2391 // A good day in the window clears it.
2392 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)];
2393 assert!(breach(&mended, 10.0, 3, 100_000).is_none());
2394 // Cost with no revenue at all is the worst margin there is.
2395 assert_eq!(breach(&[s("10-16", 0, 500_000)], 10.0, 1, 100_000).unwrap().1, -100.0);
2396 // Too little cost to judge.
2397 assert!(breach(&[s("10-16", 0, 5_000)], 10.0, 1, 100_000).is_none());
2398 assert!(breach(&series, 10.0, 9, 100_000).is_none());
2399 }
2400
2401 #[test]
2402 fn shared_costs_add_up_to_the_bill() {
2403 let w = |k: &str, v: f64| (k.to_string(), v);
2404 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)]);
2405 assert_eq!(attribute(10, &[w("a", 3.0), w("b", 1.0), w("a", 0.0)]), vec![("a".into(), 8), ("b".into(), 2)]);
2406 assert!(attribute(10, &[w("a", 0.0)]).is_empty());
2407 assert!(attribute(0, &[w("a", 1.0)]).is_empty());
2408 }
2409
2410 #[test]
Costs: counts are compared with Cloudflare's from the day g1t started counting, so days before a meter existed are not drift2411 fn counts_are_compared_from_the_day_g1t_started_counting() {
2412 let on = |day: &str, cf: f64, own: f64| ProductDay { day: day.into(), bucket: "git".into(), cf_quantity: cf, own_quantity: own, ..ProductDay::default() };
2413 // Five days of Cloudflare's count before g1t's meter, then two that match.
2414 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)];
2415 assert!(drifts("git", &days, 10.0, true, 0).iter().all(|d| d.kind != DriftKind::Count));
2416 // A real gap on the days both counted still shows.
2417 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)];
2418 let found = drifts("git", &days, 10.0, true, 0);
2419 let count = found.iter().find(|d| d.kind == DriftKind::Count).unwrap();
2420 assert_eq!((count.ours, count.cloudflare), (531.0, 1000.0));
2421 // A meter that never counted is compared over every day.
2422 let days = vec![on("2026-10-06", 400.0, 0.0)];
2423 assert!(drifts("git", &days, 10.0, true, 0).iter().any(|d| d.kind == DriftKind::Count));
2424 }
2425
2426 #[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 it2427 fn what_g1t_gives_away_is_kept_apart_from_what_it_sells() {
2428 let map = BTreeMap::new();
2429 // A comped workspace (all of it given), one in its trial (half paid
2430 // 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 running2431 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 it2432 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 running2433 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 it2434 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 running2435 // Nothing priced that day: free use.
2436 let free = usage("2026-10-15", "gamma", "agent", 0, 0, 1_000_000);
2437 let internal = BTreeSet::from(["flagon".to_string()]);
2438 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 it2439 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 running2440 assert_eq!(models.cost(), 4_000_000);
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging2441 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 running2442 let given = |w: &str| workspaces.iter().find(|x| x.workspace == w).unwrap().given.total();
2443 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 it2444 }
2445
2446 #[test]
Merge branch 'worktree-agent-a633ac0f7f66d419d'2447 fn a_discounted_sale_keeps_its_margin_and_counts_the_discount_as_given() {
2448 // $1 of model cost at 20%, sold to an account with 30% off: charged
2449 // $0.84, and $0.36 below cost plus the margin given (as usage_rows
2450 // reads the ledger: value at price, the discount part given).
2451 let mut sale = usage("2026-10-15", "acme", "agent", 1_200_000, 840_000, 1_000_000);
2452 sale.given = Given { discount: 360_000, ..Given::default() };
2453 let (days, _) = fold(&[], &BTreeMap::new(), &[], &[], &[sale], &BTreeSet::new());
2454 let models = days.iter().find(|d| d.bucket == "models").unwrap();
2455 assert_eq!(models.value_micros, 1_200_000);
2456 assert_eq!(models.given, Given { discount: 300_000, ..Given::default() });
2457 // What was sold (cost less given) still makes the margin.
2458 let sold = models.cost() - models.given.total();
2459 assert_eq!(margin_percent(models.cash_micros, sold).map(|m| m.round()), Some(17.0));
2460 }
2461
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging2462 fn draw(kind: CreditKind, reference: &str, task: Option<&str>, at: &str, micros: i64) -> (String, crate::grants::Draw) {
2463 let draw = crate::grants::Draw { grant: "crd_a".into(), kind, reference: reference.into(), task: task.map(Into::into), at: at.into(), micros };
2464 ("acme".to_owned(), draw)
2465 }
2466
2467 #[test]
2468 fn usage_paid_for_with_credit_is_given_not_money_in() {
2469 // $1.20 of usage on $1 of cost, all of it paid with promotional credit.
2470 let mut rows = vec![usage("2026-10-15", "acme", "implement", 1_200_000, 1_200_000, 1_000_000)];
2471 apply_credits(&mut rows, &[draw(CreditKind::Promotional, "run_1", Some("implement"), "2026-10-15T10:00:00Z", 1_200_000)], &[]);
2472 assert_eq!(rows[0].cash, 0);
2473 assert_eq!(rows[0].given, Given { credit_promotional: 1_200_000, ..Given::default() });
2474 let (days, workspaces) = fold(&[], &BTreeMap::new(), &[], &[], &rows, &BTreeSet::new());
2475 let models = days.iter().find(|d| d.bucket == "models").unwrap();
2476 // Valued at its price, none of it money in, all of its cost given:
2477 // the margin on what was sold is untouched by it.
2478 assert_eq!((models.value_micros, models.cash_micros), (1_200_000, 0));
2479 assert_eq!(models.given, Given { credit_promotional: 1_000_000, ..Given::default() });
2480 assert_eq!(models.cost() - models.given.total(), 0);
2481 assert_eq!(workspaces[0].given.total(), 1_000_000);
2482 // Half paid with goodwill credit: half the cost given, half sold.
2483 let mut rows = vec![usage("2026-10-15", "acme", "implement", 1_200_000, 1_200_000, 1_000_000)];
2484 apply_credits(&mut rows, &[draw(CreditKind::Goodwill, "run_1", Some("implement"), "2026-10-15T10:00:00Z", 600_000)], &[]);
2485 let (days, _) = fold(&[], &BTreeMap::new(), &[], &[], &rows, &BTreeSet::new());
2486 let models = days.iter().find(|d| d.bucket == "models").unwrap();
2487 assert_eq!(models.cash_micros, 600_000);
2488 assert_eq!(models.given, Given { credit_goodwill: 500_000, ..Given::default() });
2489 let sold = models.cost() - models.given.total();
2490 assert_eq!(margin_percent(models.cash_micros, sold).map(|m| m.round()), Some(17.0));
2491 }
2492
2493 #[test]
2494 fn what_a_refund_pays_for_is_paid_for_and_the_refund_comes_off_its_day() {
2495 // A refund's credit pays for usage: still money in, nothing given.
2496 let mut rows = vec![usage("2026-10-15", "acme", "implement", 1_200_000, 1_200_000, 1_000_000)];
2497 apply_credits(&mut rows, &[draw(CreditKind::Refund, "run_9", Some("implement"), "2026-10-15T10:00:00Z", 1_200_000)], &[]);
2498 assert_eq!((rows[0].cash, rows[0].given), (1_200_000, Given::default()));
2499 // The $3 refunded for Oct 2 comes off that day's money in, shared
2500 // over what was paid that day.
2501 let mut rows = vec![
2502 usage("2026-10-02", "acme", "implement", 4_000_000, 4_000_000, 3_000_000),
2503 usage("2026-10-02", "acme", "sandbox", 2_000_000, 2_000_000, 1_500_000),
2504 usage("2026-10-02", "beta", "implement", 9_000_000, 9_000_000, 7_000_000),
2505 ];
2506 let refund = crate::grants::Refunded { workspace: "acme".into(), day: "2026-10-02".into(), micros: 3_000_000 };
2507 apply_credits(&mut rows, &[], std::slice::from_ref(&refund));
2508 assert_eq!((rows[0].cash, rows[1].cash, rows[2].cash), (2_000_000, 1_000_000, 9_000_000));
2509 assert!(rows.iter().all(|r| r.given == Given::default()));
2510 // Nothing paid that day: a line of its own, money in less than nothing.
2511 let mut rows = vec![];
2512 apply_credits(&mut rows, &[], &[refund]);
2513 assert_eq!((rows[0].key.as_str(), rows[0].cash, rows[0].value), ("other", -3_000_000, 0));
2514 }
2515
2516 #[test]
2517 fn credit_spent_on_month_end_meters_is_a_line_of_its_own() {
2518 // Storage is reconciled from snapshots, not its ledger line: what
2519 // credit paid of it is its own row on the day it was charged.
2520 let mut rows = vec![usage("2026-10-01", "acme", "implement", 1_000, 1_000, 800)];
2521 apply_credits(&mut rows, &[draw(CreditKind::Goodwill, "storage/2026-09", Some("storage"), "2026-10-01T00:05:00Z", 2_000_000)], &[]);
2522 assert_eq!(rows.len(), 2);
2523 assert_eq!((rows[1].key.as_str(), rows[1].cash, rows[1].value), ("storage", -2_000_000, 0));
2524 assert_eq!(rows[1].given.credit_goodwill, 2_000_000);
2525 assert_eq!(rows[0].cash, 1_000);
2526 }
2527
Merge branch 'worktree-agent-a633ac0f7f66d419d'2528 #[test]
2529 fn the_gateways_total_against_the_ledgers_model_cost_is_drift() {
2530 // The gateway priced $5 of g1t's own traffic; the ledger has $3.
2531 let short = drifts("models", &[day("models", 5_000_000, 3_000_000, 3_600_000, 0.0, 0.0)], 10.0, false, 100_000);
2532 assert_eq!(short.iter().map(|d| d.kind).collect::<Vec<_>>(), vec![DriftKind::Cost]);
2533 assert!((short[0].delta_percent.unwrap() + 40.0).abs() < 1e-9);
2534 // Gateway traffic with nothing on the ledger at all: cost drift and a leak.
2535 let none = drifts("models", &[day("models", 2_000_000, 0, 0, 0.0, 0.0)], 10.0, false, 100_000);
2536 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 said2537 // Within the threshold: nothing.
Merge branch 'worktree-agent-a633ac0f7f66d419d'2538 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 said2539 // The gateway priced nothing against a ledger that has model cost:
2540 // not agreement (a token that cannot see AI Gateway reads as no
2541 // rows), so it is said. Under the minimum, or no model cost: nothing.
2542 let silent = drifts("models", &[day("models", 0, 1_000_000, 1_200_000, 0.0, 0.0)], 10.0, false, 100_000);
2543 assert_eq!(silent, vec![Drift { bucket: "models".into(), kind: DriftKind::Cost, ours: 1_000_000.0, cloudflare: 0.0, delta_percent: None }]);
Merge costs and margin review: gateway query, own spend, discount meters, superseded rises2544 // Why it is empty, as far as the run could tell.
2545 let why = |caveats: &costs::GatewayCaveats, read: costs::GatewayRead| models_detail(&silent[0], caveats, &[], &read);
2546 let none = costs::GatewayCaveats::default();
2547 let said = why(&none, costs::GatewayRead::Empty { visible: Some(false) });
2548 assert!(said.contains("$1.00") && said.contains("priced nothing") && said.contains("cannot see the gateway") && said.contains("AI Gateway Read"), "{said}");
2549 let said = why(&none, costs::GatewayRead::Empty { visible: Some(true) });
2550 assert!(said.contains("logged no requests") && said.contains("went around it"), "{said}");
2551 let said = why(&none, costs::GatewayRead::Failed("Cloudflare answered 500".into()));
2552 assert!(said.contains("could not be read: Cloudflare answered 500"), "{said}");
2553 assert!(why(&none, costs::GatewayRead::NotRead).contains("not read on this run"));
2554 assert!(why(&none, costs::GatewayRead::Empty { visible: None }).contains("could not be told"));
2555 // Requests with no price: neither the token nor a bypass.
2556 let unpriced = costs::GatewayCaveats { requests: 42.0, unpriced: vec!["anthropic_claude_new_1".into()], ..Default::default() };
2557 let said = why(&unpriced, costs::GatewayRead::Rows);
2558 assert!(said.contains("logged 42 requests") && said.contains("no price for the models used (anthropic_claude_new_1)"), "{said}");
Billing: AI Gateway's analytics are read with the token that can see them, and a gateway that priced nothing is said2559 assert!(drifts("models", &[day("models", 0, 50_000, 60_000, 0.0, 0.0)], 10.0, false, 100_000).is_empty());
2560 assert!(drifts("models", &[day("models", 0, 0, 0, 0.0, 0.0)], 10.0, false, 100_000).is_empty());
Merge branch 'worktree-agent-a633ac0f7f66d419d'2561 // The detail says which way and why it may be off.
2562 let caveats = costs::GatewayCaveats { cache_read_tokens: 3_000_000.0, unpriced: vec!["anthropic_claude_new_1".into()], ..Default::default() };
Merge costs and margin review: gateway query, own spend, discount meters, superseded rises2563 let detail = models_detail(&short[0], &caveats, &[], &costs::GatewayRead::default());
Merge branch 'worktree-agent-a633ac0f7f66d419d'2564 assert!(detail.contains("$5.00") && detail.contains("$3.00") && detail.contains("were not charged"), "{detail}");
2565 assert!(detail.contains("3,000,000 prompt-cache read") && detail.contains("no price for anthropic_claude_new_1"), "{detail}");
2566 }
2567
2568 #[test]
2569 fn model_usage_the_gateway_cannot_price_is_drift_even_when_the_totals_agree() {
2570 assert!(unpriced_drift(&costs::GatewayCaveats::default()).is_none());
2571 // Cache tokens alone are a note on the cost drift, not drift.
2572 assert!(unpriced_drift(&costs::GatewayCaveats { cache_write_tokens: 10.0, ..Default::default() }).is_none());
2573 let (drift, detail) = unpriced_drift(&costs::GatewayCaveats { unpriced: vec!["anthropic_claude_new_1".into()], short_runs: 2, ..Default::default() }).unwrap();
2574 assert_eq!((drift.bucket.as_str(), drift.kind.as_str()), ("models", "unpriced"));
2575 assert!(detail.contains("no price for anthropic_claude_new_1") && detail.contains("2 runs were settled"), "{detail}");
2576 }
2577
2578 #[test]
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily2579 fn a_workspace_that_costs_more_than_it_pays_is_flagged() {
2580 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)];
2581 let found = anomalies(&rows, 1.0, 1_000_000);
2582 assert_eq!(found, vec![("acme".to_string(), 5_000_000, 1_000_000)]);
2583 // At twice its revenue as the threshold, $5 against $3 is fine.
2584 assert!(anomalies(&[("acme".to_string(), 5_000_000, 3_000_000)], 2.0, 1_000_000).is_empty());
2585 }
2586
2587 #[test]
2588 fn a_git_operation_costs_what_cloudflare_counts_for_it() {
2589 // $0.15 per 1,000 of Cloudflare's operations, on the charged days.
2590 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();
2591 assert!((rate - 0.000_15).abs() < 1e-12);
2592 // Cloudflare counted 3 for every 1 g1t did: binding reads count.
2593 let per_op = derived_unit_cost(rate, 300_000.0, 100_000.0).unwrap();
2594 let per_thousand_micros = per_op * unit_size("1,000 operations") * 1e6;
2595 assert!((per_thousand_micros - 450_000.0).abs() < 1e-6, "{per_thousand_micros}");
2596 // Too few of g1t's units to say.
2597 assert!(derived_unit_cost(rate, 3_000.0, 500.0).is_none());
2598 assert!(billed_rate(&[(10_000.0, 0.0)]).is_none());
2599 assert_eq!(unit_size("million requests"), 1e6);
2600 assert_eq!(unit_size("second"), 1.0);
2601 }
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb972602
2603 /// The case that started it: syntaqx's ~$8.62 of model usage was wiped
2604 /// by a testing reset, AI Gateway still priced all $11.11, and the
2605 /// ledger had $2.49 left.
2606 fn gateway_and_ledger(kept: bool) -> (Vec<ProductDay>, Vec<WorkspaceDay>) {
2607 let rules = vec![rule("ai_gateway_requests", "*", "models", None), rule("containers", "*", "sandboxes", None)];
2608 let lines = vec![
2609 line("2026-10-05", costs::SOURCE_GATEWAY, "ai_gateway_requests", "anthropic_claude_opus_5_5", 1.0, 11.11),
2610 line("2026-10-05", SOURCE_BILLABLE, "containers", "container_memory", 10.0, 0.30),
2611 ];
2612 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)];
2613 if kept {
2614 // What the reset kept (reset_costs), read back for the day.
2615 usage.extend(reset_usage(&[
2616 ("2026-10-05".into(), "syntaqx".into(), "models".into(), 8_620_000, 10_344_000),
2617 ("2026-10-05".into(), "syntaqx".into(), "sandboxes".into(), 100_000, 120_000),
2618 // The reset's own row is not usage.
2619 ("2026-10-07".into(), "syntaqx".into(), String::new(), 0, 0),
2620 ]));
2621 }
2622 fold(&rules, &revenue_map(), &lines, &[], &usage, &BTreeSet::new())
2623 }
2624
2625 #[test]
2626 fn what_a_reset_kept_is_on_the_ledgers_side_of_the_models_drift() {
2627 let models = |days: &[ProductDay]| days.iter().find(|d| d.bucket == "models").cloned().unwrap();
2628 // Without it: AI Gateway's $11.11 against the ledger's $2.49.
2629 let (days, _) = gateway_and_ledger(false);
2630 let drift = drifts("models", &[models(&days)], 10.0, false, 100_000);
2631 assert_eq!(drift.iter().map(|d| d.kind).collect::<Vec<_>>(), vec![DriftKind::Cost]);
2632 assert_eq!((drift[0].ours, drift[0].cloudflare), (2_490_000.0, 11_110_000.0));
2633 // With it: the ledger's model cost and the reset's add up to the gateway's.
2634 let (days, _) = gateway_and_ledger(true);
2635 let m = models(&days);
2636 assert_eq!(m.own_cost_micros, 11_110_000);
2637 assert!(drifts("models", &[m], 10.0, false, 100_000).is_empty());
2638 // The reset's own row makes no bucket of its own.
2639 assert!(!days.iter().any(|d| d.bucket.is_empty()));
2640 }
2641
2642 #[test]
2643 fn what_a_reset_kept_is_given_away_as_testing_resets() {
2644 let (days, workspaces) = gateway_and_ledger(true);
2645 let models = days.iter().find(|d| d.bucket == "models").unwrap();
2646 // All of syntaqx's model cost is given, none of it money in.
2647 assert_eq!(models.given, Given { reset: 8_620_000, ..Given::default() });
2648 assert_eq!(models.cash_micros, 2_988_000);
2649 // Cloudflare's sandbox cost is shared by what each workspace's usage
2650 // cost: syntaqx's half is given too.
2651 let sandboxes = days.iter().find(|d| d.bucket == "sandboxes").unwrap();
2652 assert_eq!((sandboxes.cost(), sandboxes.given.reset), (300_000, 150_000));
2653 // Who g1t paid: syntaqx is still on it, all of its cost given.
2654 let syntaqx: Vec<&WorkspaceDay> = workspaces.iter().filter(|w| w.workspace == "syntaqx").collect();
2655 assert_eq!(syntaqx.iter().map(|w| w.cost).sum::<i64>(), 8_770_000);
2656 assert!(syntaqx.iter().all(|w| w.given.reset == w.cost && w.given.total() == w.cost && w.revenue == 0));
2657 // The statement reads it back from margin_days by why.
2658 let row = MarginRow {
2659 day: models.day.clone(),
2660 bucket: models.bucket.clone(),
2661 cf_cost_micros: models.cf_cost_micros,
2662 own_cost_micros: models.own_cost_micros,
2663 value_micros: models.value_micros,
2664 cash_micros: models.cash_micros,
2665 cf_quantity: 0.0,
2666 own_quantity: 0.0,
2667 given_comped_micros: Some(0),
2668 given_free_micros: Some(0),
2669 given_trial_micros: Some(0),
2670 given_pool_micros: Some(0),
2671 given_discount_micros: Some(0),
2672 given_credit_promotional_micros: Some(0),
2673 given_credit_goodwill_micros: Some(0),
2674 given_reset_micros: Some(models.given.reset),
2675 };
2676 assert_eq!(ProductDay::from(row).given, models.given);
2677 }
2678
2679 #[test]
2680 fn reconciling_again_gives_the_same_answer() {
2681 assert_eq!(gateway_and_ledger(true), gateway_and_ledger(true));
2682 // A reset's kept rows are read back exactly as kept: running it
2683 // again cannot count them twice.
2684 let kept = [("2026-10-05".to_string(), "syntaqx".to_string(), "models".to_string(), 8_620_000, 10_344_000)];
2685 assert_eq!(reset_usage(&kept), reset_usage(&kept));
2686 assert_eq!(reset_usage(&kept).len(), 1);
2687 }
2688
2689 #[test]
2690 fn a_reset_from_before_resets_kept_their_cost_is_said_not_called_a_leak() {
2691 let notes = reset_notes(
2692 &[("ws_syntaqx".into(), "2026-10-07T09:41:00.000Z".into()), ("ws_acme".into(), "2026-10-08T01:00:00.000Z".into())],
2693 &[("acme".into(), "2026-10-08T01:00:00.000Z".into(), 1_500_000)],
2694 );
2695 assert_eq!(
2696 notes,
2697 vec![
2698 ResetNote { workspace: "syntaqx".into(), day: "2026-10-07".into(), recorded: false, models_micros: 0 },
2699 ResetNote { workspace: "acme".into(), day: "2026-10-08".into(), recorded: true, models_micros: 1_500_000 },
2700 ]
2701 );
2702 let drift = Drift { bucket: "models".into(), kind: DriftKind::Cost, ours: 2_490_000.0, cloudflare: 11_110_000.0, delta_percent: Some(-77.6) };
Merge costs and margin review: gateway query, own spend, discount meters, superseded rises2703 let detail = models_detail(&drift, &costs::GatewayCaveats::default(), &notes, &costs::GatewayRead::default());
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb972704 assert!(detail.contains("includes model usage wiped by a testing reset of syntaqx on 2026-10-07"), "{detail}");
2705 assert!(detail.contains("not a leak") && detail.contains("leaves the 7 days on 2026-10-14"), "{detail}");
2706 assert!(!detail.contains("a gap that stays is a leak"), "{detail}");
2707 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}");
2708 // The models leak is not raised while such a reset is in the window.
2709 let leak = Drift { bucket: "models".into(), kind: DriftKind::Leak, ours: 0.0, cloudflare: 11_110_000.0, delta_percent: None };
2710 assert!(wiped_not_leaked(&leak, &notes));
2711 assert!(!wiped_not_leaked(&leak, &notes[1..]));
2712 assert!(!wiped_not_leaked(&Drift { bucket: "actions_cache".into(), ..leak }, &notes));
2713 // No reset: the detail is as before.
Merge costs and margin review: gateway query, own spend, discount meters, superseded rises2714 assert!(models_detail(&drift, &costs::GatewayCaveats::default(), &[], &costs::GatewayRead::default()).contains("a gap that stays is a leak"));
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb972715 }
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily2716}

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