Skip to content
3,008 linesCodeBlameRaw

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

Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1//! What g1t earns on each thing it sells, measured against what
2//! Cloudflare actually charged for it.
3//!
4//! Once a day, after `costs` has read Cloudflare's bill, the reconciler
5//! puts three figures side by side for every day and each of g1t's
6//! products (a "bucket": sandboxes, deployments, git, repository storage,
7//! …):
8//!
9//! 1. **What Cloudflare charged**: the day's cost lines `cost_map` gives
10//! the bucket.
11//! 2. **What g1t's meters recorded**: the cost on the ledger's entries for
12//! it (the price book's cost at the time) and, where a mapping names
13//! one, g1t's own count of the same units (git operations).
14//! 3. **What customers were charged**: the entries' value at price, before
15//! the plan's included usage, a trial or a pool paid part of it; and of
16//! that, what workspaces paid. Month-end meters (git, storage, scans,
17//! embeddings, the actions cache) come from daily snapshots of what they
18//! had come to (`pending_days`). The plan's price is the `platform`
19//! bucket's: the plan pays for running g1t.
20//!
21//! From those: margin per product (value against cost) and for all of g1t
22//! (money in against every cost); drift (counts or costs that disagree past
23//! a mapping's threshold, and leaks: cost with no revenue, or a Cloudflare
24//! meter no one mapped); each workspace's cost, Cloudflare's figure shared
25//! out by each workspace's own meters; and price proposals when a unit's
26//! real cost has moved (`pricing`). Alerts go to staff by email and as a
27//! banner in sudo. See docs/BILLING_OPERATIONS.md.
28
29use std::collections::{BTreeMap, BTreeSet};
30
31use g1t_contracts::billing::*;
32use g1t_contracts::{FailureCode, Outcome, new_id};
33use g1t_contracts::time::rfc3339;
34use g1t_kit::now_ms;
35use serde::{Deserialize, Serialize};
36use worker::wasm_bindgen::JsValue;
37use worker::{Env, Result};
38
39use crate::Billing;
40use crate::costs::{self, ARTIFACTS_OPERATIONS, DAY_MS, Rule, SOURCE_ARTIFACTS, SOURCE_BILLABLE, UNMAPPED};
41
42/// Buckets that are the cost of running g1t, paid by the plan rather than
43/// sold by the unit: never a leak for having no revenue of their own.
44pub(crate) const OVERHEAD: [&str; 1] = ["platform"];
45/// Buckets Cloudflare does not bill: their cost is g1t's own figure.
46pub(crate) const NOT_CLOUDFLARE: [&str; 1] = ["models"];
Merge costs and margin review: gateway query, own spend, discount meters, superseded rises47/// The key an agent's planning run is reconciled under. Its ledger task
48/// is `plan`, which is also the plan's payments' key (`revenue_map`: the
49/// platform bucket), so read as `plan` its model cost went to running g1t,
50/// where Cloudflare's bill is the cost, and was lost. No `revenue_map`
51/// row: models, like every agent run.
52pub(crate) const PLANNING_KEY: &str = "planning";
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily53/// The days drift is judged over.
54const DRIFT_DAYS: u64 = 7;
55/// The days a workspace's cost is set against its revenue.
56const ANOMALY_DAYS: u64 = 30;
57/// The days a unit's cost is measured over.
58const MEASURE_DAYS: u64 = 30;
59/// Fewer of g1t's units than this say nothing about cost per unit.
60const MIN_UNITS: f64 = 1_000.0;
61/// An open alert is emailed again after this long.
62const REMIND_MS: u64 = 7 * DAY_MS;
63
64// ---------------------------------------------------------------------
65// The arithmetic, apart from the database so it can be tested.
66// ---------------------------------------------------------------------
67
68/// One of g1t's products on one day.
69#[derive(Clone, Debug, Default, PartialEq)]
70pub(crate) struct ProductDay {
71 pub day: String,
72 pub bucket: String,
73 /// What Cloudflare charged g1t, in millionths of a dollar.
74 pub cf_cost_micros: i64,
75 /// What g1t's meters recorded it cost (the price book's cost).
76 pub own_cost_micros: i64,
77 /// What customers were charged for it at price, before what paid.
78 pub value_micros: i64,
79 /// Of that, what workspaces paid themselves.
80 pub cash_micros: i64,
81 /// Units Cloudflare counted and units g1t counted, where a mapping
82 /// says they are the same units.
83 pub cf_quantity: f64,
84 pub own_quantity: f64,
Costs: margin is measured on what was sold; comped workspaces, free periods, the trial and the pools are given away, a budget shown beside it85 /// Of `cost()`, what went on usage g1t gave away (the workspaces'
86 /// `WorkspaceDay::given`, added up).
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running87 pub given: Given,
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily88}
89
90impl ProductDay {
91 /// What it cost: Cloudflare's figure where Cloudflare bills it, else
92 /// g1t's own (models are billed by their providers, through the gateway).
93 pub fn cost(&self) -> i64 {
94 if NOT_CLOUDFLARE.contains(&self.bucket.as_str()) { self.own_cost_micros } else { self.cf_cost_micros }
95 }
96}
97
98/// A line of Cloudflare's bill, as stored.
99#[derive(Clone, Debug, Deserialize)]
100pub(crate) struct LineRow {
101 pub day: String,
102 pub source: String,
103 pub product: String,
104 pub meter: String,
105 pub quantity: f64,
106 pub cost_usd: f64,
107}
108
109/// A count of g1t's own, as stored.
110#[derive(Clone, Debug, Deserialize)]
111pub(crate) struct OwnRow {
112 pub day: String,
113 pub meter: String,
114 pub workspace: String,
115 pub quantity: f64,
116}
117
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running118/// What g1t gave away, by why: its own comped workspaces, free use (a
119/// free period, free allowances, overruns g1t covered), the trial, and the
Merge branch 'worktree-agent-a633ac0f7f66d419d'120/// open-source pool, and discounts on an account's terms (what they took
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging121/// below cost plus the margin, `ledger.discount_micros`), and credits g1t
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97122/// staff gave, promotional and goodwill, when spent (`grants`), and usage a
Merge Cloudflare's usage over its billing cycle: every page read, included amounts once a cycle, a projection, test-mode charges never money in (billing 0052)123/// testing reset wiped (`reset_costs`): g1t paid for it and nobody will;
124/// and what was charged while payments were not live (Stripe's test mode),
125/// which brought in no real money (`without_real_money`).
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97126/// The Team plan's included usage is paid for by the plan's price, so it is
127/// sold, not given; so is what a refund pays for.
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running128#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
129pub(crate) struct Given {
130 pub comped: i64,
131 pub free: i64,
132 pub trial: i64,
133 pub pool: i64,
Merge branch 'worktree-agent-a633ac0f7f66d419d'134 pub discount: i64,
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging135 pub credit_promotional: i64,
136 pub credit_goodwill: i64,
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97137 pub reset: i64,
Merge Cloudflare's usage over its billing cycle: every page read, included amounts once a cycle, a projection, test-mode charges never money in (billing 0052)138 pub unpaid: i64,
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running139}
140
141impl Given {
142 pub fn total(&self) -> i64 {
Merge Cloudflare's usage over its billing cycle: every page read, included amounts once a cycle, a projection, test-mode charges never money in (billing 0052)143 self.comped + self.free + self.trial + self.pool + self.discount + self.credit() + self.reset + self.unpaid
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging144 }
145
146 /// Credits from g1t, both kinds.
147 pub fn credit(&self) -> i64 {
148 self.credit_promotional + self.credit_goodwill
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running149 }
150
151 fn add(&mut self, other: &Given) {
152 self.comped += other.comped;
153 self.free += other.free;
154 self.trial += other.trial;
155 self.pool += other.pool;
Merge branch 'worktree-agent-a633ac0f7f66d419d'156 self.discount += other.discount;
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging157 self.credit_promotional += other.credit_promotional;
158 self.credit_goodwill += other.credit_goodwill;
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97159 self.reset += other.reset;
Merge Cloudflare's usage over its billing cycle: every page read, included amounts once a cycle, a projection, test-mode charges never money in (billing 0052)160 self.unpaid += other.unpaid;
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running161 }
162
163 /// The same shares of `cost` as these are of `value`, at most all of it.
164 fn of(&self, cost: i64, value: i64) -> Given {
165 let total = self.total();
166 if value <= 0 || cost <= 0 || total <= 0 {
167 return Given::default();
168 }
169 let given = cost as i128 * total.min(value) as i128 / value as i128;
170 let part = |x: i64| (given * x.max(0) as i128 / total as i128) as i64;
Merge branch 'worktree-agent-a633ac0f7f66d419d'171 Given {
172 comped: part(self.comped),
173 free: part(self.free),
174 trial: part(self.trial),
175 pool: part(self.pool),
176 discount: part(self.discount),
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging177 credit_promotional: part(self.credit_promotional),
178 credit_goodwill: part(self.credit_goodwill),
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97179 reset: part(self.reset),
Merge Cloudflare's usage over its billing cycle: every page read, included amounts once a cycle, a projection, test-mode charges never money in (billing 0052)180 unpaid: part(self.unpaid),
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging181 }
182 }
183}
184
185/// Credits from g1t in the reconciliation (`grants`): usage paid for with
186/// promotional or goodwill credit is given, not money in; a refund comes
187/// off money in on the day it refunds, shared over that day's paid usage.
188/// What credit paid for that is not among `rows` (month-end meters, or
189/// what was owed from before) is a row of its own on its day.
190pub(crate) fn apply_credits(rows: &mut Vec<UsageRow>, draws: &[(String, crate::grants::Draw)], refunds: &[crate::grants::Refunded]) {
191 let mut paid: BTreeMap<(String, String, String), Given> = BTreeMap::new();
192 for (workspace, draw) in draws {
193 let given = paid
194 .entry((draw.at[..10].to_owned(), workspace.clone(), crate::grants::usage_key(draw.task.as_deref(), &draw.reference)))
195 .or_default();
196 match draw.kind {
197 CreditKind::Promotional => given.credit_promotional += draw.micros,
198 CreditKind::Goodwill => given.credit_goodwill += draw.micros,
199 // Money already paid: what it pays for is paid for.
200 CreditKind::Refund | CreditKind::Purchased => {}
201 }
202 }
203 for ((day, workspace, key), given) in paid {
204 if given.credit() == 0 {
205 continue;
Merge branch 'worktree-agent-a633ac0f7f66d419d'206 }
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging207 match rows.iter_mut().find(|r| r.day == day && r.workspace == workspace && r.key == key) {
208 Some(row) => {
209 row.cash -= given.credit();
210 row.given.add(&given);
211 }
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97212 None => rows.push(UsageRow { day, workspace, key, bucket: None, value: 0, cash: -given.credit(), cost: 0, given }),
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging213 }
214 }
215 for refund in refunds {
216 let weights: Vec<(String, f64)> = rows
217 .iter()
218 .enumerate()
219 .filter(|(_, r)| r.day == refund.day && r.workspace == refund.workspace && r.cash > 0)
220 .map(|(i, r)| (format!("{i:08}"), r.cash as f64))
221 .collect();
222 let shares = attribute(refund.micros, &weights);
223 if shares.is_empty() {
224 rows.push(UsageRow {
225 day: refund.day.clone(),
226 workspace: refund.workspace.clone(),
227 key: "other".into(),
228 cash: -refund.micros,
229 ..UsageRow::default()
230 });
231 }
232 for (index, micros) in shares {
233 if let Ok(i) = index.parse::<usize>() {
234 rows[i].cash -= micros;
235 }
236 }
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running237 }
238}
239
Merge Cloudflare's usage over its billing cycle: every page read, included amounts once a cycle, a projection, test-mode charges never money in (billing 0052)240/// Money in only where it is real. Charges made while payments were not
241/// live (Stripe's test mode) brought in nothing: a row's cash from before
242/// `live_since` (all of it while payments are not live, `None`) is taken
243/// out of cash and counted as given away (`unpaid`), so it is never money
244/// in, never margin, and never what a workspace paid.
245pub(crate) fn without_real_money(rows: &mut [UsageRow], live_since: Option<&str>) {
246 for row in rows {
247 if live_since.is_some_and(|since| row.day.as_str() >= since) || row.cash == 0 {
248 continue;
249 }
250 // What else gave it away already (a 100% discount) stays that.
251 row.given.unpaid += row.cash.min(row.value - row.given.total()).max(0);
252 row.cash = 0;
253 }
254}
255
256/// The day payments went live, from `cost_settings` (`payments_live_since`)
257/// as a value read there: None while they are not live, the day kept when
258/// they are, else `today` (the first time they are seen live).
259pub(crate) fn live_since(live: bool, kept: Option<&str>, today: &str) -> Option<String> {
260 if !live {
261 return None;
262 }
263 Some(kept.filter(|d| d.len() >= 10).map_or_else(|| today.to_owned(), |d| d[..10].to_owned()))
264}
265
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily266/// What a workspace was charged for one key on one day.
267#[derive(Clone, Debug, Default, PartialEq)]
268pub(crate) struct UsageRow {
269 pub day: String,
270 pub workspace: String,
271 /// A ledger task (or `builds`), a month-end source, or `plan`.
272 pub key: String,
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97273 /// The bucket, where it is known already (what a testing reset kept,
274 /// `reset_costs`); else `key`'s, from `revenue_map`.
275 pub bucket: Option<String>,
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily276 pub value: i64,
277 pub cash: i64,
278 pub cost: i64,
Costs: margin is measured on what was sold; comped workspaces, free periods, the trial and the pools are given away, a budget shown beside it279 /// Of `value`, what g1t gave away: all of it for g1t's own (comped)
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running280 /// workspaces and in a free period, else what the trial and the pool
281 /// paid and the overruns g1t covered.
282 pub given: Given,
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily283}
284
285/// One workspace's share of a product's cost on one day.
286#[derive(Clone, Debug, PartialEq)]
287pub(crate) struct WorkspaceDay {
288 pub day: String,
289 pub workspace: String,
290 pub bucket: String,
291 pub cost: i64,
Margin alerts measure what is sold, and say dollars when a percentage would mislead292 /// What the workspace paid in cash.
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily293 pub revenue: i64,
Margin alerts measure what is sold, and say dollars when a percentage would mislead294 /// What its usage was priced at, whoever paid for it.
295 pub value: i64,
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running296 /// Of `cost`, the part g1t gave away: all of it for a comped workspace
297 /// or one with nothing priced that day (free use), else the cost times
298 /// the shares of its usage that day that g1t paid for.
299 pub given: Given,
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily300}
301
302fn micros(dollars: f64) -> i64 {
303 (dollars * 1_000_000.0).round() as i64
304}
305
306/// Puts the day's bill, g1t's counts and what customers were charged side
307/// by side, a row per day and bucket, and shares each bucket's cost out
308/// to workspaces.
309pub(crate) fn fold(
310 rules: &[Rule],
311 revenue_map: &BTreeMap<String, String>,
312 lines: &[LineRow],
313 own: &[OwnRow],
314 usage: &[UsageRow],
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running315 internal: &BTreeSet<String>,
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily316) -> (Vec<ProductDay>, Vec<WorkspaceDay>) {
317 let mut days: BTreeMap<(String, String), ProductDay> = BTreeMap::new();
318 let entry = |day: &str, bucket: &str| -> ProductDay {
319 ProductDay { day: day.to_owned(), bucket: bucket.to_owned(), ..ProductDay::default() }
320 };
321 // Which of g1t's own meters count each bucket's units.
322 let mut own_meters: BTreeMap<&str, BTreeSet<&str>> = BTreeMap::new();
323 for rule in rules {
324 if let Some(meter) = &rule.own_meter {
325 own_meters.entry(rule.bucket.as_str()).or_default().insert(meter.as_str());
326 }
327 }
328 let mut events: BTreeMap<(String, String), f64> = BTreeMap::new();
329 for line in lines {
330 let rule = costs::classify(rules, &line.product, &line.meter);
331 let bucket = rule.map_or(UNMAPPED, |r| r.bucket.as_str());
332 let key = (line.day.clone(), bucket.to_owned());
333 if line.source == SOURCE_ARTIFACTS {
334 // What Artifacts counted: operations only, and only where the
335 // bill does not count them itself.
336 if ARTIFACTS_OPERATIONS.contains(&line.meter.as_str()) {
337 *events.entry(key).or_default() += line.quantity;
338 }
339 continue;
340 }
341 let row = days.entry(key.clone()).or_insert_with(|| entry(&key.0, &key.1));
342 row.cf_cost_micros += micros(line.cost_usd);
343 if line.source == SOURCE_BILLABLE && rule.is_some_and(|r| r.own_meter.is_some()) {
344 row.cf_quantity += line.quantity;
345 }
346 }
347 for (key, quantity) in events {
348 let row = days.entry(key.clone()).or_insert_with(|| entry(&key.0, &key.1));
349 if row.cf_quantity == 0.0 {
350 row.cf_quantity = quantity;
351 }
352 }
353 // g1t's own counts of the same units, by bucket and by workspace.
354 let mut own_by: BTreeMap<(String, String), Vec<(String, f64)>> = BTreeMap::new();
355 // Cloudflare's own count by workspace, where it gives one
356 // (`cloudflare_<bucket>`): the best way to share its cost.
357 let mut cf_by: BTreeMap<(String, String), Vec<(String, f64)>> = BTreeMap::new();
358 for count in own {
359 if let Some(bucket) = count.meter.strip_prefix("cloudflare_") {
360 cf_by.entry((count.day.clone(), bucket.to_owned())).or_default().push((count.workspace.clone(), count.quantity));
361 continue;
362 }
363 for (bucket, meters) in &own_meters {
364 if meters.contains(count.meter.as_str()) {
365 let key = (count.day.clone(), (*bucket).to_owned());
366 days.entry(key.clone()).or_insert_with(|| entry(&key.0, &key.1)).own_quantity += count.quantity;
367 own_by.entry(key).or_default().push((count.workspace.clone(), count.quantity));
368 }
369 }
370 }
371 // What customers were charged.
372 let bucket_of = |key: &str| revenue_map.get(key).cloned().unwrap_or_else(|| "models".to_owned());
373 let mut value_by: BTreeMap<(String, String), Vec<(String, f64)>> = BTreeMap::new();
374 let mut cost_by: BTreeMap<(String, String), Vec<(String, f64)>> = BTreeMap::new();
375 let mut revenue: BTreeMap<(String, String, String), i64> = BTreeMap::new();
Margin alerts measure what is sold, and say dollars when a percentage would mislead376 let mut valued: BTreeMap<(String, String, String), i64> = BTreeMap::new();
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily377 let mut active: BTreeMap<String, Vec<(String, f64)>> = BTreeMap::new();
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running378 let mut gave: BTreeMap<(String, String), (Given, i64)> = BTreeMap::new();
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily379 for u in usage {
Costs: margin is measured on what was sold; comped workspaces, free periods, the trial and the pools are given away, a budget shown beside it380 let g = gave.entry((u.day.clone(), u.workspace.clone())).or_default();
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running381 g.0.add(&u.given);
Costs: margin is measured on what was sold; comped workspaces, free periods, the trial and the pools are given away, a budget shown beside it382 g.1 += u.value;
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97383 let bucket = u.bucket.clone().unwrap_or_else(|| bucket_of(&u.key));
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily384 let key = (u.day.clone(), bucket.clone());
385 let row = days.entry(key.clone()).or_insert_with(|| entry(&key.0, &key.1));
386 row.own_cost_micros += u.cost;
387 row.value_micros += u.value;
388 row.cash_micros += u.cash;
389 value_by.entry(key.clone()).or_default().push((u.workspace.clone(), u.value as f64));
390 cost_by.entry(key).or_default().push((u.workspace.clone(), u.cost as f64));
Margin alerts measure what is sold, and say dollars when a percentage would mislead391 *revenue.entry((u.day.clone(), u.workspace.clone(), bucket.clone())).or_default() += u.cash;
392 *valued.entry((u.day.clone(), u.workspace.clone(), bucket)).or_default() += u.value;
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily393 active.entry(u.day.clone()).or_default().push((u.workspace.clone(), u.value.max(u.cost) as f64));
394 }
395 // Each bucket's cost shared out: by Cloudflare's own count per
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running396 // workspace, else by g1t's own count of its units, else by what its
397 // usage cost (so free use carries its own cost), else by what it was
398 // charged; running g1t, and what no one mapped, by each workspace's
399 // share of all usage that day.
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily400 let mut shares: BTreeMap<(String, String, String), i64> = BTreeMap::new();
401 for ((day, bucket), row) in &days {
402 let key = (day.clone(), bucket.clone());
403 let weigh = |m: &BTreeMap<(String, String), Vec<(String, f64)>>| m.get(&key).filter(|w| w.iter().any(|(_, v)| *v > 0.0)).cloned();
404 let weights = if OVERHEAD.contains(&bucket.as_str()) || bucket == UNMAPPED {
405 active.get(day).cloned()
406 } else {
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running407 weigh(&cf_by).or_else(|| weigh(&own_by)).or_else(|| weigh(&cost_by)).or_else(|| weigh(&value_by)).or_else(|| active.get(day).cloned())
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily408 };
409 for (workspace, micros) in attribute(row.cost(), &weights.unwrap_or_default()) {
410 *shares.entry((day.clone(), workspace, bucket.clone())).or_default() += micros;
411 }
412 }
413 let keys: BTreeSet<(String, String, String)> = shares.keys().chain(revenue.keys()).cloned().collect();
Costs: margin is measured on what was sold; comped workspaces, free periods, the trial and the pools are given away, a budget shown beside it414 let workspaces: Vec<WorkspaceDay> = keys
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily415 .into_iter()
Costs: margin is measured on what was sold; comped workspaces, free periods, the trial and the pools are given away, a budget shown beside it416 .map(|(day, workspace, bucket)| {
417 let cost = shares.get(&(day.clone(), workspace.clone(), bucket.clone())).copied().unwrap_or(0);
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running418 // The day's shares given away apply to every bucket, so a
419 // comped workspace's part of running g1t is given too. A
420 // workspace with nothing priced that day used g1t for free.
421 let given = if internal.contains(&workspace) {
422 Given { comped: cost, ..Given::default() }
423 } else {
424 match gave.get(&(day.clone(), workspace.clone())) {
425 Some((given, value)) if *value > 0 => given.of(cost, *value),
426 _ => Given { free: cost.max(0), ..Given::default() },
427 }
Costs: margin is measured on what was sold; comped workspaces, free periods, the trial and the pools are given away, a budget shown beside it428 };
429 WorkspaceDay {
430 cost,
431 revenue: revenue.get(&(day.clone(), workspace.clone(), bucket.clone())).copied().unwrap_or(0),
432 value: valued.get(&(day.clone(), workspace.clone(), bucket.clone())).copied().unwrap_or(0),
433 given,
434 day,
435 workspace,
436 bucket,
437 }
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily438 })
439 .collect();
Costs: margin is measured on what was sold; comped workspaces, free periods, the trial and the pools are given away, a budget shown beside it440 for w in &workspaces {
441 if let Some(row) = days.get_mut(&(w.day.clone(), w.bucket.clone())) {
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running442 row.given.add(&w.given);
Costs: margin is measured on what was sold; comped workspaces, free periods, the trial and the pools are given away, a budget shown beside it443 }
444 }
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily445 (days.into_values().collect(), workspaces)
446}
447
448/// A month-end source's day, from the snapshots of what it had come to:
449/// each day's figure less the day before's in the same month (the first
450/// day of a month, or the first snapshot, is its own).
451pub(crate) fn pending_deltas(snapshots: &[(String, String, String, i64, i64)]) -> Vec<UsageRow> {
452 // (day, workspace, source, cost, charge), any order.
453 let mut sorted = snapshots.to_vec();
454 sorted.sort_by(|a, b| (&a.1, &a.2, &a.0).cmp(&(&b.1, &b.2, &b.0)));
455 let mut out = Vec::new();
456 let mut previous: Option<&(String, String, String, i64, i64)> = None;
457 for snap in &sorted {
458 let (day, workspace, source, cost, charge) = snap;
459 let (before_cost, before_charge) = match previous {
460 Some(p) if p.1 == *workspace && p.2 == *source && p.0[..7] == day[..7] => (p.3, p.4),
461 _ => (0, 0),
462 };
463 let (cost, charge) = ((cost - before_cost).max(0), (charge - before_charge).max(0));
464 if cost > 0 || charge > 0 {
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97465 out.push(UsageRow { day: day.clone(), workspace: workspace.clone(), key: source.clone(), bucket: None, value: charge, cash: charge, cost, given: Given::default() });
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily466 }
467 previous = Some(snap);
468 }
469 out
470}
471
Merge costs and margin review: gateway query, own spend, discount meters, superseded rises472/// A month-end meter's day on a 100%-discount workspace: all of it given
473/// (comped) and none of it money in. The snapshot holds what the month
474/// would charge before the discount, which the month's close takes off in
475/// full; counted as paid, flagon-io's cache, embeddings and scans read as
476/// money in ($0.0023 on 2026-10-08).
477pub(crate) fn comped_meter(mut u: UsageRow) -> UsageRow {
478 u.given = Given { comped: u.value, ..Given::default() };
479 u.cash = 0;
480 u
481}
482
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily483/// Margin as a share of what was charged, in percent; None when nothing was.
484pub(crate) fn margin_percent(revenue_micros: i64, cost_micros: i64) -> Option<f64> {
485 (revenue_micros > 0).then(|| (revenue_micros - cost_micros) as f64 * 100.0 / revenue_micros as f64)
486}
487
488/// How far `ours` is from `theirs`, in percent of theirs; None when theirs
489/// is nothing.
490pub(crate) fn delta_percent(ours: f64, theirs: f64) -> Option<f64> {
491 (theirs > 0.0).then(|| (ours - theirs) * 100.0 / theirs)
492}
493
494#[derive(Clone, Copy, Debug, PartialEq, Eq)]
495pub(crate) enum DriftKind {
496 /// g1t counted a different number of units than Cloudflare did.
497 Count,
498 /// What Cloudflare charged differs from what the price book says the
499 /// same usage cost.
500 Cost,
501 /// Cloudflare charged for something nothing charges customers for.
502 Leak,
Merge branch 'worktree-agent-a633ac0f7f66d419d'503 /// Model usage AI Gateway put no price on: its cost is not what the
504 /// provider bills, so neither the ledger nor the gateway total has it.
505 Unpriced,
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily506}
507
508impl DriftKind {
509 pub fn as_str(self) -> &'static str {
510 match self {
511 DriftKind::Count => "count",
512 DriftKind::Cost => "cost",
513 DriftKind::Leak => "leak",
Merge branch 'worktree-agent-a633ac0f7f66d419d'514 DriftKind::Unpriced => "unpriced",
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily515 }
516 }
517}
518
519#[derive(Clone, Debug, PartialEq)]
520pub(crate) struct Drift {
521 pub bucket: String,
522 pub kind: DriftKind,
523 pub ours: f64,
524 pub cloudflare: f64,
525 pub delta_percent: Option<f64>,
526}
527
528/// Drift over a window for one bucket: counts more than `threshold`
529/// percent apart, a bill that far from the price book's cost of the same
530/// usage, and cost with nothing charged for it. Under `min_cost_micros`
531/// in all, cost says nothing.
532pub(crate) fn drifts(bucket: &str, days: &[ProductDay], threshold: f64, counted: bool, min_cost_micros: i64) -> Vec<Drift> {
533 let overhead = OVERHEAD.contains(&bucket);
534 let sum = |f: &dyn Fn(&ProductDay) -> f64| days.iter().map(f).sum::<f64>();
535 let cf_cost = sum(&|d| d.cf_cost_micros as f64);
536 let own_cost = sum(&|d| d.own_cost_micros as f64);
537 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 drift538 // Counts are compared from the first day g1t counted: before its meter
539 // was deployed there is only Cloudflare's side. A meter that never
540 // counted anything is compared over every day, so it still shows.
541 let first_counted = days.iter().filter(|d| d.own_quantity > 0.0).map(|d| d.day.as_str()).min();
542 let compared = |d: &&ProductDay| first_counted.is_none_or(|from| d.day.as_str() >= from);
543 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 daily544 let mut out = Vec::new();
545 if counted && cf_quantity > 0.0 {
546 let delta = delta_percent(own_quantity, cf_quantity);
547 if delta.is_some_and(|d| d.abs() > threshold) {
548 out.push(Drift { bucket: bucket.into(), kind: DriftKind::Count, ours: own_quantity, cloudflare: cf_quantity, delta_percent: delta });
549 }
550 }
551 let enough = cf_cost.max(own_cost) >= min_cost_micros as f64;
Merge branch 'worktree-agent-a633ac0f7f66d419d'552 // Models: what AI Gateway priced g1t's own provider traffic at (its
553 // lines, as "Cloudflare's" side) against the ledger's model cost. Only
554 // once the gateway has been read; then the ledger having none of it is
555 // drift too (traffic no run was charged for).
556 let models = NOT_CLOUDFLARE.contains(&bucket) && cf_cost > 0.0;
557 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 daily558 let delta = delta_percent(own_cost, cf_cost);
559 if delta.is_some_and(|d| d.abs() > threshold) {
560 out.push(Drift { bucket: bucket.into(), kind: DriftKind::Cost, ours: own_cost, cloudflare: cf_cost, delta_percent: delta });
561 }
562 }
Billing: AI Gateway's analytics are read with the token that can see them, and a gateway that priced nothing is said563 // The ledger has model cost and the gateway priced none of it: a token
564 // that cannot see AI Gateway reads as no rows, never an error, so this
565 // is not agreement. Said, rather than left as no row at all.
566 if NOT_CLOUDFLARE.contains(&bucket) && cf_cost <= 0.0 && own_cost >= min_cost_micros as f64 && own_cost > 0.0 {
567 out.push(Drift { bucket: bucket.into(), kind: DriftKind::Cost, ours: own_cost, cloudflare: 0.0, delta_percent: None });
568 }
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily569 if !overhead && cf_cost >= min_cost_micros as f64 && value <= 0.0 {
570 out.push(Drift { bucket: bucket.into(), kind: DriftKind::Leak, ours: value, cloudflare: cf_cost, delta_percent: None });
571 }
572 out
573}
574
Merge branch 'worktree-agent-a633ac0f7f66d419d'575/// What can make AI Gateway's cost differ from what the providers bill,
576/// said for staff: cache tokens (priced by the gateway at its own rates for
577/// them, which may lag the provider's), requests Cloudflare billed itself,
578/// models it has no price for, and runs settled short.
579fn caveat_notes(c: &costs::GatewayCaveats) -> Vec<String> {
580 let mut notes = Vec::new();
581 if c.cache_read_tokens > 0.0 || c.cache_write_tokens > 0.0 {
582 notes.push(format!(
583 "{} 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",
584 crate::features::thousands(c.cache_read_tokens.round() as u64),
585 crate::features::thousands(c.cache_write_tokens.round() as u64)
586 ));
587 }
588 if c.wholesale_usd > 0.0 {
589 notes.push(format!(
590 "{} of it Cloudflare billed itself (unified billing): that part is on Cloudflare's bill, not a provider's",
591 dollars(micros(c.wholesale_usd))
592 ));
593 }
594 if !c.unpriced.is_empty() {
595 notes.push(format!("it has no price for {} (tokens used, $0)", c.unpriced.join(", ")));
596 }
597 if c.short_runs > 0 {
598 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));
599 }
600 notes
601}
602
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97603/// Where a testing reset's history starts: all of a workspace's ledger.
604pub(crate) const RESET_HISTORY_FROM: &str = "2000-01-01";
605
606/// What a testing reset wiped that g1t paid for, on one day for one
607/// bucket (`reset_costs`).
608#[derive(Clone, Debug, PartialEq)]
609pub(crate) struct Wiped {
610 pub day: String,
611 pub bucket: String,
612 pub cost: i64,
613 pub value: i64,
614}
615
616/// A workspace's usage rows, about to be wiped, as what g1t paid for: the
617/// rows with a cost, by day and bucket, valued as the reconciliation
618/// valued them (at price where nothing paid). Plan payments, credits and
619/// a workspace's own model provider cost g1t nothing and are left out.
620pub(crate) fn wiped(rows: &[UsageRow], revenue_map: &BTreeMap<String, String>, margin_percent: u32) -> Vec<Wiped> {
621 let mut by: BTreeMap<(String, String), (i64, i64)> = BTreeMap::new();
622 for u in rows.iter().filter(|u| u.cost > 0) {
623 let bucket = u.bucket.clone().unwrap_or_else(|| revenue_map.get(&u.key).cloned().unwrap_or_else(|| NOT_CLOUDFLARE[0].to_owned()));
624 let sums = by.entry((u.day.clone(), bucket)).or_default();
625 sums.0 += u.cost;
626 sums.1 += u.value.max(0);
627 }
628 by.into_iter()
629 .map(|((day, bucket), (cost, value))| Wiped {
630 day,
631 bucket,
632 cost,
633 value: if value > 0 { value } else { crate::credits::with_margin(cost, margin_percent) },
634 })
635 .collect()
636}
637
638/// What testing resets kept, each (day, workspace, bucket, cost, value),
639/// as usage rows: valued as before, nothing paid, all of it given away
640/// (why "testing resets"). A reset's own row (bucket '') is not usage.
641pub(crate) fn reset_usage(kept: &[(String, String, String, i64, i64)]) -> Vec<UsageRow> {
642 kept.iter()
643 .filter(|(_, _, bucket, cost, value)| !bucket.is_empty() && (*cost != 0 || *value != 0))
644 .map(|(day, workspace, bucket, cost, value)| UsageRow {
645 day: day.clone(),
646 workspace: workspace.clone(),
647 key: "reset".into(),
648 bucket: Some(bucket.clone()),
649 value: *value,
650 cash: 0,
651 cost: *cost,
652 given: Given { reset: *value, ..Given::default() },
653 })
654 .collect()
655}
656
657/// A testing reset inside the drift window.
658#[derive(Clone, Debug, PartialEq)]
659pub(crate) struct ResetNote {
660 pub workspace: String,
661 /// The UTC day it was reset.
662 pub day: String,
663 /// Whether it kept what it wiped (`reset_costs`, migration 0046):
664 /// then the ledger's side has it, given away. A reset from before
665 /// that wiped model usage the gateway still counts.
666 pub recorded: bool,
667 /// Of what it kept, model cost on the window's days.
668 pub models_micros: i64,
669}
670
671/// The resets: each audit entry (account `ws_<slug>`, when) and each kept
672/// reset (workspace, reset_at, its model cost in the window). An audit
673/// entry with no kept reset at the same instant is from before resets kept
674/// what they wiped.
675pub(crate) fn reset_notes(audits: &[(String, String)], kept: &[(String, String, i64)]) -> Vec<ResetNote> {
676 let mut notes: Vec<(String, ResetNote)> = kept
677 .iter()
678 .map(|(workspace, at, models)| {
679 (at.clone(), ResetNote { workspace: workspace.clone(), day: at[..10.min(at.len())].to_owned(), recorded: true, models_micros: *models })
680 })
681 .collect();
682 for (account, at) in audits {
683 let workspace = account.strip_prefix("ws_").unwrap_or(account);
684 if !kept.iter().any(|(w, a, _)| w == workspace && a == at) {
685 notes.push((at.clone(), ResetNote { workspace: workspace.to_owned(), day: at[..10.min(at.len())].to_owned(), recorded: false, models_micros: 0 }));
686 }
687 }
688 notes.sort_by(|a, b| a.0.cmp(&b.0).then(a.1.workspace.cmp(&b.1.workspace)));
689 notes.into_iter().map(|(_, n)| n).collect()
690}
691
692/// Model usage a reset wiped before resets kept it is not a leak: while
693/// such a reset is in the window the models leak is not raised, and the
694/// models cost drift says what the gap is.
695pub(crate) fn wiped_not_leaked(drift: &Drift, resets: &[ResetNote]) -> bool {
696 drift.kind == DriftKind::Leak && NOT_CLOUDFLARE.contains(&drift.bucket.as_str()) && resets.iter().any(|r| !r.recorded)
697}
698
699/// What the models drift says about resets in the window.
700fn reset_sentences(resets: &[ResetNote]) -> Vec<String> {
701 resets
702 .iter()
703 .filter_map(|r| {
704 if !r.recorded {
705 Some(format!(
706 "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 {}.",
707 r.workspace,
708 r.day,
709 day_after(&r.day, DRIFT_DAYS)
710 ))
711 } else if r.models_micros > 0 {
712 Some(format!(
713 "The ledger's figure includes {} of model cost wiped by a testing reset of {} on {}, counted as given away (testing resets).",
714 dollars(r.models_micros),
715 r.workspace,
716 r.day
717 ))
718 } else {
719 None
720 }
721 })
722 .collect()
723}
724
725/// The models drift's detail: the gateway's total against the ledger's,
726/// and any testing reset in the window.
Merge costs and margin review: gateway query, own spend, discount meters, superseded rises727pub(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 said728 if drift.cloudflare <= 0.0 {
Merge costs and margin review: gateway query, own spend, discount meters, superseded rises729 let head = format!(
730 "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 said731 dollars(drift.ours as i64)
732 );
Merge costs and margin review: gateway query, own spend, discount meters, superseded rises733 let why = if caveats.requests > 0.0 {
734 format!(
735 "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.",
736 crate::features::thousands(caveats.requests.round() as u64),
737 if caveats.unpriced.is_empty() { String::new() } else { format!(" ({})", caveats.unpriced.join(", ")) }
738 )
739 } else {
740 match read {
741 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(),
742 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(),
743 costs::GatewayRead::Failed(error) => format!("AI Gateway's analytics could not be read: {error}"),
744 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(),
745 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(),
746 }
747 };
748 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 said749 }
Merge branch 'worktree-agent-a633ac0f7f66d419d'750 let lower = drift.ours < drift.cloudflare;
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97751 let wiped = resets.iter().any(|r| !r.recorded);
Merge branch 'worktree-agent-a633ac0f7f66d419d'752 let mut detail = format!(
753 "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}%). {}",
754 dollars(drift.cloudflare as i64),
755 dollars(drift.ours as i64),
756 drift.delta_percent.unwrap_or(0.0),
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97757 if lower && wiped {
758 "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."
759 } else if lower {
Merge branch 'worktree-agent-a633ac0f7f66d419d'760 "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)."
761 } else {
762 "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."
763 }
764 );
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97765 for sentence in reset_sentences(resets) {
766 detail.push(' ');
767 detail.push_str(&sentence);
768 }
Merge branch 'worktree-agent-a633ac0f7f66d419d'769 let notes = caveat_notes(caveats);
770 if !notes.is_empty() {
771 detail.push_str(" The gateway's cost may be off: ");
772 detail.push_str(&notes.join("; "));
773 detail.push('.');
774 }
775 detail
776}
777
778/// The unpriced drift's detail.
779pub(crate) fn unpriced_detail(caveats: &costs::GatewayCaveats) -> String {
780 format!(
781 "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.",
782 caveat_notes(&costs::GatewayCaveats { cache_read_tokens: 0.0, cache_write_tokens: 0.0, wholesale_usd: 0.0, ..caveats.clone() }).join("; ")
783 )
784}
785
786/// Model usage AI Gateway could not price over the window, as drift on
787/// the models bucket: models with tokens and no cost, or runs settled
788/// short. None when there is none.
789pub(crate) fn unpriced_drift(caveats: &costs::GatewayCaveats) -> Option<(Drift, String)> {
790 if caveats.unpriced.is_empty() && caveats.short_runs == 0 {
791 return None;
792 }
793 let drift = Drift {
794 bucket: NOT_CLOUDFLARE[0].into(),
795 kind: DriftKind::Unpriced,
796 ours: f64::from(caveats.short_runs),
797 cloudflare: caveats.unpriced.len() as f64,
798 delta_percent: None,
799 };
800 Some((drift, unpriced_detail(caveats)))
801}
802
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily803/// When the last `days` in a row (each with enough cost to say something)
804/// were all under the floor: the first of them and the worst margin.
805/// Each item is a day's (day, revenue, cost).
806pub(crate) fn breach(series: &[(String, i64, i64)], floor_percent: f64, days: usize, min_cost_micros: i64) -> Option<(String, f64)> {
807 if days == 0 || series.len() < days {
808 return None;
809 }
810 let tail = &series[series.len() - days..];
811 let mut worst = f64::INFINITY;
812 for (_, revenue, cost) in tail {
813 if *cost < min_cost_micros {
814 return None;
815 }
816 let margin = margin_percent(*revenue, *cost).unwrap_or(-100.0);
817 if margin >= floor_percent {
818 return None;
819 }
820 worst = worst.min(margin);
821 }
822 Some((tail[0].0.clone(), worst))
823}
824
825/// `total` shared out in proportion to `weights`, in whole millionths that
826/// add up to it exactly (largest remainder first). Nothing to share, or no
827/// weight, shares nothing.
828pub(crate) fn attribute(total: i64, weights: &[(String, f64)]) -> Vec<(String, i64)> {
829 let mut merged: BTreeMap<String, f64> = BTreeMap::new();
830 for (key, w) in weights {
831 *merged.entry(key.clone()).or_default() += w.max(0.0);
832 }
833 let sum: f64 = merged.values().sum();
834 if total <= 0 || sum <= 0.0 {
835 return Vec::new();
836 }
837 let mut shares: Vec<(String, i64, f64)> = merged
838 .into_iter()
839 .map(|(key, w)| {
840 let exact = total as f64 * w / sum;
841 (key, exact.floor() as i64, exact - exact.floor())
842 })
843 .collect();
844 let mut left = total - shares.iter().map(|s| s.1).sum::<i64>();
845 let mut order: Vec<usize> = (0..shares.len()).collect();
846 order.sort_by(|a, b| shares[*b].2.total_cmp(&shares[*a].2).then(shares[*a].0.cmp(&shares[*b].0)));
847 for index in order {
848 if left <= 0 {
849 break;
850 }
851 shares[index].1 += 1;
852 left -= 1;
853 }
854 shares.into_iter().filter(|s| s.1 > 0).map(|(key, micros, _)| (key, micros)).collect()
855}
856
857/// Workspaces that cost g1t more than `factor` times what they paid, with
858/// at least `floor_micros` of cost: each (workspace, cost, revenue), the
859/// biggest gap first.
Models' margin read -14%: usage nothing paid for is valued at price, not $0860/// What a day's usage was worth at price. g1t's own workspaces are valued
861/// at price. So is usage nothing paid for, neither charged nor drawn from
862/// the plan, a trial, a pool or a gift (a free period): it was given away at
863/// its price, not sold for nothing. Anything paid keeps what it was paid, so
864/// a discount still shows as one.
865pub(crate) fn usage_value(internal: bool, cost: i64, paid: i64, margin_percent: u32) -> i64 {
866 if internal || (paid == 0 && cost > 0) {
867 return crate::credits::with_margin(cost, margin_percent);
868 }
869 paid
870}
871
Margin alerts measure what is sold, and say dollars when a percentage would mislead872/// What the overall alert says: the money as money, and a percentage only
873/// while there is enough coming in for one to mean something (a few cents
874/// against dollars of cost reads as -8000%).
875pub(crate) fn overall_detail(took: i64, spent: i64, days: usize, floor: f64, worst: f64) -> String {
876 if took < 1_000_000 * days as i64 {
877 return format!(
878 "All of g1t, comped workspaces left out: took in {} against {} of Cloudflare's bill over {days} days.",
879 dollars(took),
880 dollars(spent)
881 );
882 }
883 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}%.")
884}
885
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily886pub(crate) fn anomalies(rows: &[(String, i64, i64)], factor: f64, floor_micros: i64) -> Vec<(String, i64, i64)> {
887 let mut out: Vec<(String, i64, i64)> = rows
888 .iter()
889 .filter(|(_, cost, revenue)| *cost >= floor_micros && *cost as f64 > *revenue as f64 * factor)
890 .cloned()
891 .collect();
892 out.sort_by(|a, b| (b.1 - b.2).cmp(&(a.1 - a.2)).then(a.0.cmp(&b.0)));
893 out
894}
895
896/// Cloudflare's marginal rate for one of its units: the median over the
897/// charged days of cost over quantity, in dollars. None while the included
898/// amounts still cover it. Each item is a day's (quantity, cost).
899pub(crate) fn billed_rate(days: &[(f64, f64)]) -> Option<f64> {
900 let mut rates: Vec<f64> = days.iter().filter(|(q, c)| *q > 0.0 && *c > 0.0).map(|(q, c)| c / q).collect();
901 if rates.is_empty() {
902 return None;
903 }
904 rates.sort_by(f64::total_cmp);
905 Some(rates[rates.len() / 2])
906}
907
908/// What one of g1t's units costs, from Cloudflare's rate per its own unit
909/// and how many of Cloudflare's units each of g1t's took: if Cloudflare
910/// counts three operations for every git operation g1t counts, a git
911/// operation costs three of Cloudflare's. None without enough of g1t's
912/// units to say.
913pub(crate) fn derived_unit_cost(rate_per_cf_unit: f64, cf_units: f64, own_units: f64) -> Option<f64> {
914 (own_units >= MIN_UNITS && cf_units > 0.0 && rate_per_cf_unit > 0.0).then(|| rate_per_cf_unit * cf_units / own_units)
915}
916
917/// How many units a price is per: `1,000 operations` → 1,000, `million
918/// requests` → 1,000,000, `second` → 1.
919pub(crate) fn unit_size(unit: &str) -> f64 {
920 let first = unit.split_whitespace().next().unwrap_or_default().replace(',', "");
921 match first.as_str() {
922 "million" => 1_000_000.0,
923 "thousand" => 1_000.0,
924 n => n.parse().unwrap_or(1.0),
925 }
926}
927
928fn day_before(day: &str, days: u64) -> String {
929 let ms = g1t_contracts::time::parse_rfc3339(&format!("{day}T00:00:00Z")).unwrap_or(0);
930 rfc3339(ms.saturating_sub(days * DAY_MS))[..10].to_owned()
931}
932
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97933fn day_after(day: &str, days: u64) -> String {
934 let ms = g1t_contracts::time::parse_rfc3339(&format!("{day}T00:00:00Z")).unwrap_or(0);
935 rfc3339(ms + days * DAY_MS)[..10].to_owned()
936}
937
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily938/// Dollars to the cent from a dollar up, finer below: `$17.02`, `$0.063`.
939fn dollars(micros: i64) -> String {
940 if micros.abs() >= 1_000_000 {
941 let cents = (micros as f64 / 10_000.0).round() as i64;
942 format!("{}${}.{:02}", if cents < 0 { "-" } else { "" }, cents.abs() / 100, cents.abs() % 100)
943 } else {
944 crate::features::dollars(micros)
945 }
946}
947
948/// The days a plan payment is spread over.
949const PLAN_DAYS: u64 = 30;
950
951/// `micros` paid on `day` spread evenly over `days` days from it, in
952/// whole micros that add up to it (the first days take the remainder).
953pub(crate) fn spread(day: &str, micros: i64, days: u64) -> Vec<(String, i64)> {
954 if micros <= 0 || days == 0 {
955 return Vec::new();
956 }
957 let start = g1t_contracts::time::parse_rfc3339(&format!("{}T00:00:00Z", &day[..10.min(day.len())])).unwrap_or(0);
958 let each = micros / days as i64;
959 let rest = micros % days as i64;
960 (0..days)
961 .map(|n| (rfc3339(start + n * DAY_MS)[..10].to_owned(), each + i64::from((n as i64) < rest)))
962 .collect()
963}
964
965// ---------------------------------------------------------------------
966// The daily run, and what sudo reads.
967// ---------------------------------------------------------------------
968
969#[derive(Serialize)]
970struct Mail<'a> {
971 to: &'a str,
972 from: &'a str,
973 subject: &'a str,
974 text: String,
975 html: String,
976}
977
978fn escape(text: &str) -> String {
979 text.replace('&', "&amp;").replace('<', "&lt;").replace('>', "&gt;").replace('"', "&quot;")
980}
981
982/// Emails staff through Cloudflare Email Sending, the `EMAIL` binding.
Spend caps: a monthly budget for comped workspaces and a daily breaker on what g1t pays983pub(crate) async fn email_staff(env: &Env, to: &str, subject: &str, lines: &[String]) -> Result<()> {
Merge branch 'main' into actions-toolkit-oidc-artifacts984 email_staff_page(env, to, subject, lines, ("Costs & margin", "https://sudo.g1t.sh/costs"), "g1t-billing's margin guard").await
985}
986
987/// Emails staff, linking to a page of sudo (`page`: its name and address)
988/// and saying what sent it.
989pub(crate) async fn email_staff_page(env: &Env, to: &str, subject: &str, lines: &[String], page: (&str, &str), sender: &str) -> Result<()> {
990 let (name, link) = page;
991 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 daily992 let mut html = String::from("<div style=\"font-family:system-ui,sans-serif;max-width:560px;margin:0 auto;padding:24px 16px;color:#16150f\">");
993 for line in lines {
994 html.push_str(&format!("<p style=\"font-size:15px;line-height:1.6\">{}</p>", escape(line)));
995 }
996 html.push_str(&format!(
Merge branch 'main' into actions-toolkit-oidc-artifacts997 "<p><a href=\"{link}\">Open {} in sudo</a></p><p style=\"font-size:13px;color:#6e6a5e\">Sent by {} (COSTS_ALERT_EMAIL).</p></div>",
998 escape(name),
999 escape(sender)
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1000 ));
1001 let mail = Mail { to, from: "g1t <noreply@g1t.sh>", subject, text, html };
1002 let binding = g1t_kit::js::binding(env, "EMAIL")?;
1003 g1t_kit::js::call(&binding, "send", &[g1t_kit::js::to_js(&mail)?]).await?;
1004 Ok(())
1005}
1006
1007#[derive(Deserialize)]
1008struct AlertRow {
1009 id: String,
1010 kind: String,
1011 subject: String,
1012 detail: String,
1013 since: String,
1014 opened_at: String,
1015 emailed_at: Option<String>,
1016}
1017
1018impl From<AlertRow> for MarginAlert {
1019 fn from(r: AlertRow) -> Self {
1020 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 }
1021 }
1022}
1023
1024#[derive(Deserialize)]
1025struct MarginRow {
1026 day: String,
1027 bucket: String,
1028 cf_cost_micros: i64,
1029 own_cost_micros: i64,
1030 value_micros: i64,
1031 cash_micros: i64,
1032 cf_quantity: f64,
1033 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 it1034 #[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 running1035 given_comped_micros: Option<i64>,
1036 #[serde(default)]
1037 given_free_micros: Option<i64>,
1038 #[serde(default)]
1039 given_trial_micros: Option<i64>,
1040 #[serde(default)]
1041 given_pool_micros: Option<i64>,
Merge branch 'worktree-agent-a633ac0f7f66d419d'1042 #[serde(default)]
1043 given_discount_micros: Option<i64>,
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging1044 #[serde(default)]
1045 given_credit_promotional_micros: Option<i64>,
1046 #[serde(default)]
1047 given_credit_goodwill_micros: Option<i64>,
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb971048 #[serde(default)]
1049 given_reset_micros: Option<i64>,
Merge Cloudflare's usage over its billing cycle: every page read, included amounts once a cycle, a projection, test-mode charges never money in (billing 0052)1050 #[serde(default)]
1051 given_unpaid_micros: Option<i64>,
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1052}
1053
1054impl From<MarginRow> for ProductDay {
1055 fn from(r: MarginRow) -> Self {
1056 ProductDay {
1057 day: r.day,
1058 bucket: r.bucket,
1059 cf_cost_micros: r.cf_cost_micros,
1060 own_cost_micros: r.own_cost_micros,
1061 value_micros: r.value_micros,
1062 cash_micros: r.cash_micros,
1063 cf_quantity: r.cf_quantity,
1064 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 running1065 given: Given {
1066 comped: r.given_comped_micros.unwrap_or(0),
1067 free: r.given_free_micros.unwrap_or(0),
1068 trial: r.given_trial_micros.unwrap_or(0),
1069 pool: r.given_pool_micros.unwrap_or(0),
Merge branch 'worktree-agent-a633ac0f7f66d419d'1070 discount: r.given_discount_micros.unwrap_or(0),
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging1071 credit_promotional: r.given_credit_promotional_micros.unwrap_or(0),
1072 credit_goodwill: r.given_credit_goodwill_micros.unwrap_or(0),
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb971073 reset: r.given_reset_micros.unwrap_or(0),
Merge Cloudflare's usage over its billing cycle: every page read, included amounts once a cycle, a projection, test-mode charges never money in (billing 0052)1074 unpaid: r.given_unpaid_micros.unwrap_or(0),
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running1075 },
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1076 }
1077 }
1078}
1079
1080impl Billing {
1081 /// The day's work: read Cloudflare's bill and g1t's own counts,
1082 /// reconcile, look for drift, measure unit costs, apply prices whose
1083 /// day has come, and raise or clear alerts.
1084 pub(crate) async fn costs_daily(&self, env: &Env, keeper: &crate::keeper::Keeper) -> Result<CostsRun> {
1085 let mut run = CostsRun::default();
Merge costs and margin review: gateway query, own spend, discount meters, superseded rises1086 let mut gateway = costs::GatewayRead::default();
Merge Cloudflare's usage over its billing cycle: every page read, included amounts once a cycle, a projection, test-mode charges never money in (billing 0052)1087 // The subscriptions first: they say when the billing cycle starts,
1088 // which the bill is priced by. Not a problem for the run: the last
1089 // read, or the estimate, stays.
1090 if keeper.can_read_bill()
1091 && let Err(error) = self.read_subscriptions(keeper).await
1092 {
1093 worker::console_error!("Cloudflare's subscriptions were not read: {error}");
1094 }
1095 let (since, until, bill_since) = match self.read_cloudflare(keeper, &mut run.problems, &mut gateway).await? {
1096 Some((since, until, lines, bill_since)) => {
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1097 run.lines = lines;
Merge Cloudflare's usage over its billing cycle: every page read, included amounts once a cycle, a projection, test-mode charges never money in (billing 0052)1098 (since, until, Some(bill_since))
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1099 }
1100 // Without the bill, still reconcile what g1t knows itself, over
1101 // the same days the bill would be read for.
1102 None => {
1103 #[derive(Deserialize)]
1104 struct Last {
1105 day: Option<String>,
1106 }
1107 let last = self.db.prepare("SELECT MAX(day) AS day FROM margin_days").first::<Last>(None).await?.and_then(|l| l.day);
Merge Cloudflare's usage over its billing cycle: every page read, included amounts once a cycle, a projection, test-mode charges never money in (billing 0052)1108 let (since, until) = costs::window(last.as_deref(), now_ms());
1109 (since, until, None)
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1110 }
1111 };
1112 if let Err(error) = self.count_own(&since, &until).await {
1113 run.problems.push(format!("g1t's own counts could not be read: {error}"));
1114 }
1115 self.snapshot_pending(&until).await?;
Models' margin read -14%: usage nothing paid for is valued at price, not $01116 // Reconciled over the whole window sudo shows, not only the days the
1117 // bill was read for: it reads only what is already kept, so a change
1118 // in how a day is valued reaches every day shown at the next run.
1119 let window = day_before(&until, costs::BACKFILL_DAYS - 1);
Merge Cloudflare's usage over its billing cycle: every page read, included amounts once a cycle, a projection, test-mode charges never money in (billing 0052)1120 let reconcile_from = [Some(window), Some(since.clone()), bill_since].into_iter().flatten().min().unwrap_or_default();
Models' margin read -14%: usage nothing paid for is valued at price, not $01121 run.days = self.reconcile_range(&reconcile_from, &until).await?;
Merge costs and margin review: gateway query, own spend, discount meters, superseded rises1122 let drift = self.find_drift(&until, &gateway).await?;
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1123 run.proposals = self.measure_units(&until).await?;
1124 self.apply_due_versions().await?;
1125 run.alerts = self.raise_alerts(env, &until, &drift).await?;
1126 if let Some(identity) = &self.identity
1127 && let Err(error) = self.tell_owners_of_rises(identity).await
1128 {
1129 run.problems.push(format!("owners could not be told of a price rise: {error}"));
1130 }
1131 for problem in &run.problems {
1132 worker::console_warn!("costs: {problem}");
1133 }
1134 Ok(run)
1135 }
1136
1137 /// What each month-end source had come to by the end of `day`.
1138 async fn snapshot_pending(&self, day: &str) -> Result<()> {
1139 self.db
1140 .prepare(
1141 "INSERT INTO pending_days (day, workspace, source, cost_micros, charge_micros)
1142 SELECT ?1, workspace, source, COALESCE(cost_micros, 0), COALESCE(charge_micros, 0) FROM pending_usage WHERE month = ?2
1143 ON CONFLICT (day, workspace, source) DO UPDATE SET cost_micros = excluded.cost_micros, charge_micros = excluded.charge_micros",
1144 )
1145 .bind(&[day.into(), day[..7].into()])?
1146 .run()
1147 .await?;
1148 Ok(())
1149 }
1150
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb971151 /// What customers were charged on the days, by workspace and key: every
1152 /// workspace's, or only `only`'s.
1153 async fn usage_rows(&self, since: &str, until: &str, only: Option<&str>) -> Result<Vec<UsageRow>> {
1154 let only_sql = only.unwrap_or("");
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1155 #[derive(Deserialize)]
1156 struct Row {
1157 day: String,
1158 workspace: String,
1159 key: String,
1160 internal: i64,
1161 own_provider: i64,
1162 cash: Option<i64>,
1163 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 running1164 trial: Option<i64>,
1165 oss: Option<i64>,
1166 covered: Option<i64>,
Merge branch 'worktree-agent-a633ac0f7f66d419d'1167 discount: Option<i64>,
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1168 cost: Option<i64>,
1169 }
1170 let charged_here = crate::storage::CHARGED_HERE.iter().map(|s| format!("'{s}'")).collect::<Vec<_>>().join(", ");
1171 let end = format!("{until}T23:59:59.999Z");
1172 let rows = self
1173 .db
1174 .prepare(format!(
1175 "SELECT substr(created_at, 1, 10) AS day, workspace,
Merge costs and margin review: gateway query, own spend, discount meters, superseded rises1176 CASE WHEN task = 'deployments' AND reference LIKE 'deploy/%' THEN 'builds'
1177 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 daily1178 CASE WHEN workspace IN ({internal}) THEN 1 ELSE 0 END AS internal,
1179 CASE WHEN billed_to = 'workspace' THEN 1 ELSE 0 END AS own_provider,
1180 -SUM(amount_micros) AS cash,
1181 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 running1182 SUM(COALESCE(trial_micros, 0)) AS trial,
1183 SUM(COALESCE(oss_micros, 0)) AS oss,
1184 SUM(COALESCE(given_micros, 0)) AS covered,
Merge branch 'worktree-agent-a633ac0f7f66d419d'1185 SUM(COALESCE(discount_micros, 0)) AS discount,
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1186 SUM(COALESCE(cost_micros, 0)) AS cost
1187 FROM ledger
1188 WHERE kind = 'usage' AND created_at >= ?1 AND created_at <= ?2 AND COALESCE(task, '') NOT IN ({charged_here})
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb971189 AND (?3 = '' OR workspace = ?3)
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1190 GROUP BY 1, 2, 3, 4, 5",
Merge costs and margin review: gateway query, own spend, discount meters, superseded rises1191 internal = crate::sales::INTERNAL_SQL,
1192 planning = PLANNING_KEY
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1193 ))
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb971194 .bind(&[since.into(), end.as_str().into(), only_sql.into()])?
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1195 .all()
1196 .await?
1197 .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 it1198 let mut internal = BTreeSet::new();
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1199 let mut out: Vec<UsageRow> = rows
1200 .into_iter()
1201 .map(|r| {
1202 // A workspace's own model provider was paid there: no cost
1203 // to g1t. g1t's own workspaces are valued at price.
1204 let cost = if r.own_provider == 1 { 0 } else { r.cost.unwrap_or(0) };
1205 let cash = r.cash.unwrap_or(0);
Merge branch 'worktree-agent-a633ac0f7f66d419d'1206 // A discount took its part below cost plus the margin: it is
1207 // valued at price and that part counted as given, so a
1208 // discounted sale never reads as margin lost.
1209 let discount = r.discount.unwrap_or(0).max(0);
1210 let paid = cash + r.drawn.unwrap_or(0) + discount;
Models' margin read -14%: usage nothing paid for is valued at price, not $01211 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 running1212 let given = if r.internal == 1 {
1213 Given { comped: value, ..Given::default() }
1214 } else if paid == 0 && cost > 0 {
1215 Given { free: value, ..Given::default() }
1216 } else {
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging1217 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 running1218 };
Costs: margin is measured on what was sold; comped workspaces, free periods, the trial and the pools are given away, a budget shown beside it1219 if r.internal == 1 {
1220 internal.insert(r.workspace.clone());
1221 }
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb971222 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 daily1223 })
1224 .collect();
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging1225 // Credits from g1t: what promotional and goodwill credit paid for
1226 // is given, not money in; a refund gives money back on its day.
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb971227 let (mut draws, mut refunds) = self.credit_effects(since, until).await?;
1228 if let Some(only) = only {
1229 draws.retain(|(workspace, _)| workspace == only);
1230 refunds.retain(|r| r.workspace == only);
1231 }
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging1232 apply_credits(&mut out, &draws, &refunds);
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1233 // Month-end sources, from their daily snapshots.
1234 #[derive(Deserialize)]
1235 struct Snap {
1236 day: String,
1237 workspace: String,
1238 source: String,
1239 cost_micros: i64,
1240 charge_micros: i64,
1241 }
1242 let snaps = self
1243 .db
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb971244 .prepare("SELECT day, workspace, source, cost_micros, charge_micros FROM pending_days WHERE day >= ?1 AND day <= ?2 AND (?3 = '' OR workspace = ?3)")
1245 .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 daily1246 .all()
1247 .await?
1248 .results::<Snap>()?
1249 .into_iter()
1250 .filter(|s| crate::storage::CHARGED_HERE.contains(&s.source.as_str()) || s.source == "domains")
1251 .map(|s| (s.day, s.workspace, s.source, s.cost_micros, s.charge_micros))
1252 .collect::<Vec<_>>();
Merge costs and margin review: gateway query, own spend, discount meters, superseded rises1253 out.extend(
1254 pending_deltas(&snaps)
1255 .into_iter()
1256 .filter(|u| u.day.as_str() >= since)
1257 .map(|u| if internal.contains(&u.workspace) { comped_meter(u) } else { u }),
1258 );
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1259 // The plan's price, spread over the 30 days it pays for, so a month's
1260 // payment does not read as one very good day and 29 bad ones.
1261 #[derive(Deserialize)]
1262 struct Plan {
1263 day: String,
1264 workspace: String,
1265 micros: Option<i64>,
1266 }
1267 let plans = self
1268 .db
1269 .prepare(
1270 "SELECT substr(paid_at, 1, 10) AS day, workspace, SUM(amount_micros) AS micros FROM plan_payments
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb971271 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 daily1272 )
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb971273 .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 daily1274 .all()
1275 .await?
1276 .results::<Plan>()?;
1277 for p in plans {
1278 for (day, micros) in spread(&p.day, p.micros.unwrap_or(0), PLAN_DAYS) {
1279 if day.as_str() >= since && day.as_str() <= until {
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb971280 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 daily1281 }
1282 }
1283 }
Merge Cloudflare's usage over its billing cycle: every page read, included amounts once a cycle, a projection, test-mode charges never money in (billing 0052)1284 // Charges without real money behind them are not money in.
1285 without_real_money(&mut out, self.payments_live_since().await?.as_deref());
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1286 Ok(out)
1287 }
1288
Merge Cloudflare's usage over its billing cycle: every page read, included amounts once a cycle, a projection, test-mode charges never money in (billing 0052)1289 /// The day payments went live (`live_since`): kept in `cost_settings`
1290 /// the first time they are seen live, so charges from before it stay
1291 /// test money after the switch.
1292 pub(crate) async fn payments_live_since(&self) -> Result<Option<String>> {
1293 #[derive(Deserialize)]
1294 struct Row {
1295 value: String,
1296 }
1297 let live = self.stripe.as_ref().is_some_and(crate::stripe::Stripe::live);
1298 let kept = self
1299 .db
1300 .prepare("SELECT value FROM cost_settings WHERE key = 'payments_live_since'")
1301 .first::<Row>(None)
1302 .await?
1303 .map(|r| r.value)
1304 .filter(|v| v.len() >= 10);
1305 let today = rfc3339(now_ms())[..10].to_owned();
1306 let since = live_since(live, kept.as_deref(), &today);
1307 if let Some(day) = since.as_deref().filter(|_| kept.is_none()) {
1308 self.db
1309 .prepare(
1310 "INSERT INTO cost_settings (key, value, updated_at, updated_by) VALUES ('payments_live_since', ?1, ?2, 'billing')
1311 ON CONFLICT (key) DO UPDATE SET value = ?1, updated_at = ?2, updated_by = 'billing'",
1312 )
1313 .bind(&[day.into(), rfc3339(now_ms()).into()])?
1314 .run()
1315 .await?;
1316 }
1317 Ok(since)
1318 }
1319
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb971320 /// Which bucket each ledger key (and month-end source) is revenue of.
1321 async fn revenue_map(&self) -> Result<BTreeMap<String, String>> {
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1322 #[derive(Deserialize)]
1323 struct Map {
1324 key: String,
1325 bucket: String,
1326 }
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb971327 Ok(self
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1328 .db
1329 .prepare("SELECT key, bucket FROM revenue_map")
1330 .all()
1331 .await?
1332 .results::<Map>()?
1333 .into_iter()
1334 .map(|m| (m.key, m.bucket))
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb971335 .collect())
1336 }
1337
1338 /// What a testing reset of `workspace` is about to wipe that g1t paid
1339 /// for, a row per day and bucket: its whole ledger and month-end
1340 /// snapshots, valued as the reconciliation values them.
1341 pub(crate) async fn wiped_by_reset(&self, workspace: &str) -> Result<Vec<Wiped>> {
1342 let today = rfc3339(now_ms())[..10].to_owned();
1343 let rows = self.usage_rows(RESET_HISTORY_FROM, &today, Some(workspace)).await?;
1344 Ok(wiped(&rows, &self.revenue_map().await?, self.margin_percent))
1345 }
1346
1347 /// What testing resets kept for the days, as usage rows.
1348 async fn reset_rows(&self, since: &str, until: &str) -> Result<Vec<UsageRow>> {
1349 #[derive(Deserialize)]
1350 struct Kept {
1351 day: String,
1352 workspace: String,
1353 bucket: String,
1354 cost: Option<i64>,
1355 value: Option<i64>,
1356 }
1357 let kept = self
1358 .db
1359 .prepare(
1360 "SELECT day, workspace, bucket, SUM(cost_micros) AS cost, SUM(value_micros) AS value FROM reset_costs
1361 WHERE day >= ?1 AND day <= ?2 AND bucket <> '' GROUP BY day, workspace, bucket",
1362 )
1363 .bind(&[since.into(), until.into()])?
1364 .all()
1365 .await?
1366 .results::<Kept>()?;
1367 Ok(reset_usage(
1368 &kept.into_iter().map(|k| (k.day, k.workspace, k.bucket, k.cost.unwrap_or(0), k.value.unwrap_or(0))).collect::<Vec<_>>(),
1369 ))
1370 }
1371
1372 /// Testing resets on or after `since` (the day they wiped usage up to
1373 /// is their own, so one before it wiped nothing in the days): those
1374 /// that kept what they wiped (`reset_costs`) and those from before
1375 /// resets did, known only from the audit log.
1376 async fn resets_since(&self, since: &str, until: &str) -> Result<Vec<ResetNote>> {
1377 let end = format!("{until}T23:59:59.999Z");
1378 #[derive(Deserialize)]
1379 struct Audit {
1380 account: String,
1381 created_at: String,
1382 }
1383 let audits = self
1384 .db
1385 .prepare("SELECT account, created_at FROM admin_actions WHERE action = 'reset' AND created_at >= ?1 AND created_at <= ?2")
1386 .bind(&[since.into(), end.as_str().into()])?
1387 .all()
1388 .await?
1389 .results::<Audit>()?;
1390 #[derive(Deserialize)]
1391 struct Kept {
1392 workspace: String,
1393 reset_at: String,
1394 models: Option<i64>,
1395 }
1396 let kept = self
1397 .db
1398 .prepare(
1399 "SELECT workspace, reset_at, SUM(CASE WHEN bucket = ?3 AND day >= ?1 THEN cost_micros ELSE 0 END) AS models
1400 FROM reset_costs WHERE reset_at >= ?1 AND reset_at <= ?2 GROUP BY workspace, reset_at",
1401 )
1402 .bind(&[since.into(), end.as_str().into(), NOT_CLOUDFLARE[0].into()])?
1403 .all()
1404 .await?
1405 .results::<Kept>()?;
1406 Ok(reset_notes(
1407 &audits.into_iter().map(|a| (a.account, a.created_at)).collect::<Vec<_>>(),
1408 &kept.into_iter().map(|k| (k.workspace, k.reset_at, k.models.unwrap_or(0))).collect::<Vec<_>>(),
1409 ))
1410 }
1411
1412 /// Reconciles the days and writes `margin_days` and `workspace_costs`.
1413 async fn reconcile_range(&self, since: &str, until: &str) -> Result<u32> {
1414 let rules = self.rules().await?;
1415 let revenue_map = self.revenue_map().await?;
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1416 let lines = self
1417 .db
1418 .prepare("SELECT day, source, product, meter, quantity, cost_usd FROM cost_lines WHERE day >= ?1 AND day <= ?2")
1419 .bind(&[since.into(), until.into()])?
1420 .all()
1421 .await?
1422 .results::<LineRow>()?;
1423 let own = self
1424 .db
1425 .prepare("SELECT day, meter, workspace, quantity FROM own_counts WHERE day >= ?1 AND day <= ?2")
1426 .bind(&[since.into(), until.into()])?
1427 .all()
1428 .await?
1429 .results::<OwnRow>()?;
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb971430 let mut usage = self.usage_rows(since, until, None).await?;
1431 // What testing resets wiped: still paid for, now given away.
1432 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 running1433 #[derive(Deserialize)]
1434 struct Internal {
1435 workspace: String,
1436 }
1437 let internal: BTreeSet<String> = self
1438 .db
1439 .prepare(format!("WITH i(workspace) AS ({}) SELECT DISTINCT workspace FROM i", crate::sales::INTERNAL_SQL))
1440 .all()
1441 .await?
1442 .results::<Internal>()?
1443 .into_iter()
1444 .map(|i| i.workspace)
1445 .collect();
1446 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 daily1447 let now = rfc3339(now_ms());
1448 self.db
1449 .batch(vec![
1450 self.db.prepare("DELETE FROM margin_days WHERE day >= ?1 AND day <= ?2").bind(&[since.into(), until.into()])?,
1451 self.db.prepare("DELETE FROM workspace_costs WHERE day >= ?1 AND day <= ?2").bind(&[since.into(), until.into()])?,
1452 ])
1453 .await?;
1454 for chunk in days.chunks(50) {
1455 let mut statements = Vec::with_capacity(chunk.len());
1456 for d in chunk {
1457 statements.push(
1458 self.db
1459 .prepare(
Merge Cloudflare's usage over its billing cycle: every page read, included amounts once a cycle, a projection, test-mode charges never money in (billing 0052)1460 "INSERT OR REPLACE INTO margin_days (day, bucket, cf_cost_micros, own_cost_micros, value_micros, cash_micros, cf_quantity, own_quantity, given_micros, given_comped_micros, given_free_micros, given_trial_micros, given_pool_micros, given_discount_micros, given_credit_promotional_micros, given_credit_goodwill_micros, given_reset_micros, given_unpaid_micros, computed_at)
1461 VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1462 )
1463 .bind(&[
1464 d.day.as_str().into(),
1465 d.bucket.as_str().into(),
1466 (d.cf_cost_micros as f64).into(),
1467 (d.own_cost_micros as f64).into(),
1468 (d.value_micros as f64).into(),
1469 (d.cash_micros as f64).into(),
1470 d.cf_quantity.into(),
1471 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 running1472 (d.given.total() as f64).into(),
1473 (d.given.comped as f64).into(),
1474 (d.given.free as f64).into(),
1475 (d.given.trial as f64).into(),
1476 (d.given.pool as f64).into(),
Merge branch 'worktree-agent-a633ac0f7f66d419d'1477 (d.given.discount as f64).into(),
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging1478 (d.given.credit_promotional as f64).into(),
1479 (d.given.credit_goodwill as f64).into(),
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb971480 (d.given.reset as f64).into(),
Merge Cloudflare's usage over its billing cycle: every page read, included amounts once a cycle, a projection, test-mode charges never money in (billing 0052)1481 (d.given.unpaid as f64).into(),
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1482 now.as_str().into(),
1483 ])?,
1484 );
1485 }
1486 self.db.batch(statements).await?;
1487 }
1488 for chunk in workspaces.chunks(50) {
1489 let mut statements = Vec::with_capacity(chunk.len());
1490 for w in chunk {
1491 statements.push(
1492 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 it1493 .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 daily1494 .bind(&[
1495 w.day.as_str().into(),
1496 w.workspace.as_str().into(),
1497 w.bucket.as_str().into(),
1498 (w.cost as f64).into(),
1499 (w.revenue as f64).into(),
Margin alerts measure what is sold, and say dollars when a percentage would mislead1500 (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 running1501 (w.given.total() as f64).into(),
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1502 ])?,
1503 );
1504 }
1505 self.db.batch(statements).await?;
1506 }
1507 Ok(costs::days_between(since, until).len() as u32)
1508 }
1509
1510 async fn margin_days(&self, since: &str, until: &str) -> Result<Vec<ProductDay>> {
1511 Ok(self
1512 .db
1513 .prepare("SELECT * FROM margin_days WHERE day >= ?1 AND day <= ?2 ORDER BY day, bucket")
1514 .bind(&[since.into(), until.into()])?
1515 .all()
1516 .await?
1517 .results::<MarginRow>()?
1518 .into_iter()
1519 .map(ProductDay::from)
1520 .collect())
1521 }
1522
Merge branch 'worktree-agent-a633ac0f7f66d419d'1523 /// What AI Gateway's lines over the days, and the runs settled in them,
1524 /// say about whether its cost is what the providers bill.
1525 async fn gateway_caveats(&self, since: &str, until: &str) -> Result<costs::GatewayCaveats> {
1526 #[derive(Deserialize)]
1527 struct Line {
1528 meter: String,
1529 quantity: f64,
1530 cost_usd: f64,
1531 }
1532 let lines: Vec<(String, f64, f64)> = self
1533 .db
1534 .prepare("SELECT meter, quantity, cost_usd FROM cost_lines WHERE source = ?1 AND day >= ?2 AND day <= ?3")
1535 .bind(&[costs::SOURCE_GATEWAY.into(), since.into(), until.into()])?
1536 .all()
1537 .await?
1538 .results::<Line>()?
1539 .into_iter()
1540 .map(|l| (l.meter, l.quantity, l.cost_usd))
1541 .collect();
1542 let mut caveats = costs::gateway_caveats(&lines);
1543 #[derive(Deserialize)]
1544 struct Short {
1545 n: Option<f64>,
1546 }
1547 caveats.short_runs = self
1548 .db
1549 .prepare("SELECT COUNT(*) AS n FROM runs WHERE gateway_note IS NOT NULL AND settled_at >= ?1 AND settled_at <= ?2")
1550 .bind(&[since.into(), format!("{until}T23:59:59.999Z").into()])?
1551 .first::<Short>(None)
1552 .await?
1553 .and_then(|s| s.n)
1554 .unwrap_or(0.0) as u32;
1555 Ok(caveats)
1556 }
1557
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1558 /// Drift over the last week, written to `cost_drift` (replacing the
1559 /// last run's), with unmapped Cloudflare meters as leaks.
Merge costs and margin review: gateway query, own spend, discount meters, superseded rises1560 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 daily1561 let since = day_before(until, DRIFT_DAYS - 1);
1562 let settings = self.cost_settings().await?;
1563 let rules = self.rules().await?;
1564 let days = self.margin_days(&since, until).await?;
1565 let mut by: BTreeMap<String, Vec<ProductDay>> = BTreeMap::new();
1566 for d in days {
1567 by.entry(d.bucket.clone()).or_default().push(d);
1568 }
Merge branch 'worktree-agent-a633ac0f7f66d419d'1569 let caveats = self.gateway_caveats(&since, until).await?;
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb971570 let resets = self.resets_since(&since, until).await?;
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1571 let mut found = Vec::new();
Merge branch 'worktree-agent-a633ac0f7f66d419d'1572 if let Some(drift) = unpriced_drift(&caveats) {
1573 found.push(drift);
1574 }
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1575 for (bucket, days) in &by {
1576 let bucket_rules: Vec<&Rule> = rules.iter().filter(|r| &r.bucket == bucket).collect();
1577 let threshold = bucket_rules.iter().map(|r| r.drift_percent).fold(f64::INFINITY, f64::min);
1578 let threshold = if threshold.is_finite() { threshold } else { 10.0 };
1579 let counted = bucket_rules.iter().any(|r| r.own_meter.is_some());
1580 for drift in drifts(bucket, days, threshold, counted, settings.min_daily_cost_micros) {
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb971581 if wiped_not_leaked(&drift, &resets) {
1582 continue;
1583 }
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1584 let title = costs::bucket_title(bucket);
1585 let detail = match drift.kind {
Merge costs and margin review: gateway query, own spend, discount meters, superseded rises1586 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 daily1587 DriftKind::Count => format!(
One operation mapping, owned by repos; billing reads it instead of keeping its own1588 "{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 daily1589 crate::features::thousands(drift.ours.max(0.0).round() as u64),
1590 crate::features::thousands(drift.cloudflare.max(0.0).round() as u64),
1591 drift.delta_percent.unwrap_or(0.0)
1592 ),
1593 DriftKind::Cost => format!(
1594 "{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.",
1595 dollars(drift.cloudflare as i64),
1596 dollars(drift.ours as i64),
1597 drift.delta_percent.unwrap_or(0.0)
1598 ),
1599 DriftKind::Leak if bucket == UNMAPPED => {
1600 format!("Cloudflare charged {} for meters no mapping claims. Map them on Costs & margin.", dollars(drift.cloudflare as i64))
1601 }
Merge branch 'worktree-agent-a633ac0f7f66d419d'1602 DriftKind::Leak if NOT_CLOUDFLARE.contains(&bucket.as_str()) => format!(
1603 "{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).",
1604 dollars(drift.cloudflare as i64)
1605 ),
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1606 DriftKind::Leak => format!(
1607 "{title}: Cloudflare charged {} over the last {DRIFT_DAYS} days and customers were charged nothing for it.",
1608 dollars(drift.cloudflare as i64)
1609 ),
Merge branch 'worktree-agent-a633ac0f7f66d419d'1610 // Raised from the gateway's lines, not per bucket.
1611 DriftKind::Unpriced => unpriced_detail(&caveats),
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1612 };
1613 found.push((drift, detail));
1614 }
1615 }
1616 let now = rfc3339(now_ms());
1617 let mut statements = vec![self.db.prepare("DELETE FROM cost_drift")];
1618 for (drift, detail) in &found {
1619 statements.push(
1620 self.db
1621 .prepare("INSERT OR REPLACE INTO cost_drift (bucket, kind, ours, cloudflare, delta_percent, detail, found_at) VALUES (?, ?, ?, ?, ?, ?, ?)")
1622 .bind(&[
1623 drift.bucket.as_str().into(),
1624 drift.kind.as_str().into(),
1625 drift.ours.into(),
1626 drift.cloudflare.into(),
1627 drift.delta_percent.map_or(JsValue::NULL, JsValue::from),
1628 detail.as_str().into(),
1629 now.as_str().into(),
1630 ])?,
1631 );
1632 }
1633 self.db.batch(statements).await?;
1634 Ok(found)
1635 }
1636
1637 /// Unit costs from the bill for mappings that scale to g1t's own count
1638 /// (git operations), proposed to the price book.
1639 async fn measure_units(&self, until: &str) -> Result<u32> {
1640 #[derive(Deserialize)]
1641 struct Scaled {
1642 product: String,
1643 meter: String,
1644 price_meter: String,
1645 own_meter: String,
1646 unit: Option<String>,
1647 }
1648 let scaled = self
1649 .db
1650 .prepare(
1651 "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
1652 WHERE m.scale_to_own = 1 AND m.price_meter IS NOT NULL AND m.own_meter IS NOT NULL",
1653 )
1654 .all()
1655 .await?
1656 .results::<Scaled>()?;
1657 let since = day_before(until, MEASURE_DAYS - 1);
1658 let rules = self.rules().await?;
1659 let mut proposed = 0;
1660 for s in scaled {
1661 #[derive(Deserialize)]
1662 struct Day {
1663 product: String,
1664 meter: String,
1665 quantity: f64,
1666 cost_usd: f64,
1667 }
1668 let lines = self
1669 .db
1670 .prepare("SELECT product, meter, quantity, cost_usd FROM cost_lines WHERE source = ?1 AND product = ?2 AND day >= ?3 AND day <= ?4")
1671 .bind(&[SOURCE_BILLABLE.into(), s.product.as_str().into(), since.as_str().into(), until.into()])?
1672 .all()
1673 .await?
1674 .results::<Day>()?;
1675 // Only the lines this very mapping claims.
1676 let mine: Vec<(f64, f64)> = lines
1677 .iter()
1678 .filter(|l| costs::classify(&rules, &l.product, &l.meter).is_some_and(|r| r.product == s.product && r.meter == s.meter))
1679 .map(|l| (l.quantity, l.cost_usd))
1680 .collect();
1681 let Some(rate) = billed_rate(&mine) else { continue };
1682 let cf_units: f64 = mine.iter().map(|(q, _)| q).sum();
1683 #[derive(Deserialize)]
1684 struct Own {
1685 total: Option<f64>,
1686 }
1687 let own_units = self
1688 .db
1689 .prepare("SELECT SUM(quantity) AS total FROM own_counts WHERE meter = ?1 AND day >= ?2 AND day <= ?3")
1690 .bind(&[s.own_meter.as_str().into(), since.as_str().into(), until.into()])?
1691 .first::<Own>(None)
1692 .await?
1693 .and_then(|o| o.total)
1694 .unwrap_or(0.0);
1695 let Some(per_unit) = derived_unit_cost(rate, cf_units, own_units) else { continue };
1696 let size = unit_size(s.unit.as_deref().unwrap_or("1"));
1697 let measured = per_unit * size * 1_000_000.0;
1698 let reason = format!(
1699 "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 {})",
1700 rate * 1000.0,
1701 cf_units / own_units,
1702 crate::features::thousands(cf_units.round() as u64),
1703 crate::features::thousands(own_units.round() as u64)
1704 );
1705 if self.propose(&s.price_meter, measured, &reason, "reconciler").await?.is_some() {
1706 proposed += 1;
1707 }
1708 }
1709 Ok(proposed)
1710 }
1711
1712 /// Opens, updates and closes margin alerts, and emails staff about new
1713 /// ones (and open ones each week).
1714 async fn raise_alerts(&self, env: &Env, until: &str, drift: &[(Drift, String)]) -> Result<u32> {
1715 let settings = self.cost_settings().await?;
1716 let since = day_before(until, u64::from(settings.alert_days.max(1)) - 1);
1717 let days = self.margin_days(&since, until).await?;
1718 let mut conditions: Vec<(String, String, String, String)> = Vec::new();
1719 // Each product under the floor.
1720 let mut by: BTreeMap<String, Vec<(String, i64, i64)>> = BTreeMap::new();
1721 let mut all: BTreeMap<String, (i64, i64)> = BTreeMap::new();
1722 for d in &days {
1723 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 it1724 // What g1t gave away (comped workspaces, free periods, the
1725 // trial and the pools) is a budget it chose to spend, watched on
1726 // 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 daily1727 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 running1728 overall.1 += (d.cost() - d.given.total()).max(0);
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1729 if !OVERHEAD.contains(&d.bucket.as_str()) && d.bucket != UNMAPPED {
1730 by.entry(d.bucket.clone()).or_default().push((d.day.clone(), d.value_micros, d.cost()));
1731 }
1732 }
1733 let floor = settings.margin_floor_percent;
1734 let n = settings.alert_days as usize;
1735 for (bucket, series) in &by {
1736 if let Some((from, worst)) = breach(series, floor, n, settings.min_daily_cost_micros) {
1737 conditions.push((
1738 "margin".into(),
1739 bucket.clone(),
1740 format!("{}: margin under {floor:.0}% for {n} days running, as low as {worst:.1}%.", costs::bucket_title(bucket)),
1741 from,
1742 ));
Margin alerts measure what is sold, and say dollars when a percentage would mislead1743 }
1744 }
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1745 let series: Vec<(String, i64, i64)> = all.into_iter().map(|(day, (revenue, cost))| (day, revenue, cost)).collect();
1746 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 mislead1747 let tail = &series[series.len().saturating_sub(n)..];
1748 let (took, spent) = tail.iter().fold((0i64, 0i64), |(r, c), (_, revenue, cost)| (r + revenue, c + cost));
1749 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 daily1750 }
1751 for (d, detail) in drift {
1752 let kind = if d.kind == DriftKind::Leak { "leak" } else { "drift" };
1753 conditions.push((kind.into(), format!("{}:{}", d.bucket, d.kind.as_str()), detail.clone(), until.to_owned()));
1754 }
1755 // Workspaces costing more than they pay.
1756 for (workspace, cost, revenue) in self.workspace_anomalies(until, &settings).await? {
1757 conditions.push((
1758 "workspace".into(),
1759 workspace.clone(),
Margin alerts measure what is sold, and say dollars when a percentage would mislead1760 format!(
1761 "{workspace} cost g1t {} on Cloudflare over {ANOMALY_DAYS} days, and its usage was priced at {}: its prices are below cost.",
1762 dollars(cost),
1763 dollars(revenue)
1764 ),
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1765 day_before(until, ANOMALY_DAYS - 1),
1766 ));
1767 }
1768
1769 let open = self
1770 .db
1771 .prepare("SELECT * FROM margin_alerts WHERE resolved_at IS NULL")
1772 .all()
1773 .await?
1774 .results::<AlertRow>()?;
1775 let now = now_ms();
1776 let stamp = rfc3339(now);
1777 let mut to_email: Vec<String> = Vec::new();
1778 let mut kept: BTreeSet<String> = BTreeSet::new();
1779 for (kind, subject, detail, from) in &conditions {
1780 match open.iter().find(|a| &a.kind == kind && &a.subject == subject) {
1781 Some(alert) => {
1782 kept.insert(alert.id.clone());
1783 self.db
1784 .prepare("UPDATE margin_alerts SET detail = ? WHERE id = ?")
1785 .bind(&[detail.as_str().into(), alert.id.as_str().into()])?
1786 .run()
1787 .await?;
1788 let stale = alert
1789 .emailed_at
1790 .as_deref()
1791 .and_then(g1t_contracts::time::parse_rfc3339)
1792 .is_none_or(|at| now.saturating_sub(at) >= REMIND_MS);
1793 if stale && kind != "workspace" {
1794 to_email.push(format!("Still open: {detail}"));
1795 kept.insert(format!("email:{}", alert.id));
1796 }
1797 }
1798 None => {
1799 let id = new_id("mal", now);
1800 self.db
1801 .prepare("INSERT INTO margin_alerts (id, kind, subject, detail, since, opened_at) VALUES (?, ?, ?, ?, ?, ?)")
1802 .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()])?
1803 .run()
1804 .await?;
1805 kept.insert(id.clone());
Margin alerts measure what is sold, and say dollars when a percentage would mislead1806 // A workspace's is for Reach out, not the inbox.
1807 if kind != "workspace" {
1808 to_email.push(detail.clone());
1809 }
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1810 kept.insert(format!("email:{id}"));
1811 }
1812 }
1813 }
1814 for alert in &open {
1815 if !kept.contains(&alert.id) {
1816 self.db
1817 .prepare("UPDATE margin_alerts SET resolved_at = ? WHERE id = ?")
1818 .bind(&[stamp.as_str().into(), alert.id.as_str().into()])?
1819 .run()
1820 .await?;
1821 }
1822 }
1823 let to = env.var("COSTS_ALERT_EMAIL").map(|v| v.to_string()).unwrap_or_default();
1824 if !to_email.is_empty() && !to.trim().is_empty() {
1825 let subject = format!("[g1t costs] {} margin alert{}", to_email.len(), if to_email.len() == 1 { "" } else { "s" });
1826 match email_staff(env, to.trim(), &subject, &to_email).await {
1827 Ok(()) => {
1828 for marker in kept.iter().filter_map(|k| k.strip_prefix("email:")) {
1829 self.db
1830 .prepare("UPDATE margin_alerts SET emailed_at = ? WHERE id = ?")
1831 .bind(&[stamp.as_str().into(), marker.into()])?
1832 .run()
1833 .await?;
1834 }
1835 }
1836 Err(error) => worker::console_error!("could not email the margin alerts: {error}"),
1837 }
1838 }
1839 Ok(conditions.len() as u32)
1840 }
1841
1842 /// 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 mislead1843 /// Each day's cost shared out to comped workspaces.
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1844 async fn workspace_anomalies(&self, until: &str, settings: &CostSettings) -> Result<Vec<(String, i64, i64)>> {
1845 #[derive(Deserialize)]
1846 struct Row {
1847 workspace: String,
1848 cost: Option<i64>,
1849 revenue: Option<i64>,
1850 }
1851 let rows = self
1852 .db
1853 .prepare(format!(
Margin alerts measure what is sold, and say dollars when a percentage would mislead1854 // Against what its usage was priced at, not the cash it
1855 // paid: a trial or a gift paying for usage is not a price
1856 // below cost.
The workspace cost alert compares only days that carry their value, not the days before it was kept1857 // Days from before value_micros was kept have none: only days
1858 // since the first one that does are compared.
Margin alerts measure what is sold, and say dollars when a percentage would mislead1859 "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 kept1860 WHERE day >= ?1 AND day <= ?2 AND workspace NOT IN ({})
1861 AND day >= (SELECT MIN(day) FROM workspace_costs WHERE value_micros > 0)
1862 GROUP BY workspace",
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1863 crate::sales::INTERNAL_SQL
1864 ))
1865 .bind(&[day_before(until, ANOMALY_DAYS - 1).into(), until.into()])?
1866 .all()
1867 .await?
1868 .results::<Row>()?;
1869 let rows: Vec<(String, i64, i64)> = rows.into_iter().map(|r| (r.workspace, r.cost.unwrap_or(0), r.revenue.unwrap_or(0))).collect();
1870 Ok(anomalies(&rows, settings.anomaly_factor, settings.anomaly_floor_micros))
1871 }
1872
1873 /// For Reach out: workspaces with an open cost-over-revenue alert,
1874 /// each with its detail and cost.
1875 pub(crate) async fn costing_more_than_they_pay(&self) -> Result<Vec<(String, String, i64)>> {
1876 let alerts = self
1877 .db
1878 .prepare("SELECT * FROM margin_alerts WHERE resolved_at IS NULL AND kind = 'workspace' ORDER BY opened_at DESC LIMIT 50")
1879 .all()
1880 .await?
1881 .results::<AlertRow>()?;
1882 let mut out = Vec::new();
1883 for alert in alerts {
1884 #[derive(Deserialize)]
1885 struct Cost {
1886 cost: Option<i64>,
1887 }
1888 let cost = self
1889 .db
1890 .prepare("SELECT SUM(cost_micros) AS cost FROM workspace_costs WHERE workspace = ? AND day >= ?")
1891 .bind(&[alert.subject.as_str().into(), alert.since.as_str().into()])?
1892 .first::<Cost>(None)
1893 .await?
1894 .and_then(|c| c.cost)
1895 .unwrap_or(0);
1896 out.push((alert.subject, alert.detail, cost));
1897 }
1898 Ok(out)
1899 }
1900
1901 /// `admin_cost_alerts`: what sudo's banner says.
1902 pub(crate) async fn admin_cost_alerts(&self, _: AdminCostAlertsArgs) -> Result<Vec<MarginAlert>> {
1903 Ok(self
1904 .db
1905 .prepare("SELECT * FROM margin_alerts WHERE resolved_at IS NULL ORDER BY opened_at DESC LIMIT 50")
1906 .all()
1907 .await?
1908 .results::<AlertRow>()?
1909 .into_iter()
1910 .map(MarginAlert::from)
1911 .collect())
1912 }
1913
1914 /// `admin_run_costs`: the daily run, now.
1915 pub(crate) async fn admin_run_costs(&self, env: &Env, a: AdminRunCostsArgs) -> Result<Outcome<CostsRun>> {
1916 let keeper = crate::keeper::Keeper::from_env(env);
1917 let run = self.costs_daily(env, &keeper).await?;
1918 if !a.by.is_empty() {
1919 self.audit(
1920 "costs",
1921 "costs_run",
1922 &format!("{} lines, {} days, {} proposals, {} alerts", run.lines, run.days, run.proposals, run.alerts),
1923 &a.by,
1924 )
1925 .await?;
1926 }
1927 Ok(Outcome::Ok(run))
1928 }
1929
1930 /// `admin_set_cost_mapping`.
1931 pub(crate) async fn admin_set_cost_mapping(&self, a: AdminSetCostMappingArgs) -> Result<Outcome<CostMapping>> {
1932 let product = costs::slug(&a.product);
1933 let meter = if a.meter.trim() == "*" { "*".to_owned() } else { costs::slug(&a.meter) };
1934 if product.is_empty() || meter.is_empty() {
1935 return Ok(Outcome::fail(FailureCode::Invalid, "Name Cloudflare's product and a meter (or * for all of it)."));
1936 }
1937 let now = rfc3339(now_ms());
1938 if a.remove {
1939 self.db
1940 .prepare("DELETE FROM cost_map WHERE product = ? AND meter = ?")
1941 .bind(&[product.as_str().into(), meter.as_str().into()])?
1942 .run()
1943 .await?;
1944 self.audit("costs", "cost_mapping_removed", &format!("{product}/{meter}"), &a.by).await?;
1945 return Ok(Outcome::Ok(CostMapping {
1946 product,
1947 meter,
1948 bucket: String::new(),
1949 price_meter: None,
1950 own_meter: None,
1951 scale_to_own: false,
1952 drift_percent: 0.0,
1953 note: String::new(),
1954 updated_at: now,
1955 updated_by: a.by,
1956 }));
1957 }
1958 let bucket = costs::slug(&a.bucket);
1959 if bucket.is_empty() {
1960 return Ok(Outcome::fail(FailureCode::Invalid, "Say which of g1t's products it is a cost of."));
1961 }
1962 let clean = |v: Option<String>| v.map(|v| v.trim().to_owned()).filter(|v| !v.is_empty());
1963 let (price_meter, own_meter) = (clean(a.price_meter), clean(a.own_meter));
1964 let drift = a.drift_percent.filter(|d| d.is_finite() && *d > 0.0).unwrap_or(10.0);
1965 self.db
1966 .prepare(
1967 "INSERT INTO cost_map (product, meter, bucket, price_meter, own_meter, scale_to_own, drift_percent, note, updated_at, updated_by)
1968 VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10)
1969 ON CONFLICT (product, meter) DO UPDATE SET bucket = ?3, price_meter = ?4, own_meter = ?5, scale_to_own = ?6,
1970 drift_percent = ?7, note = ?8, updated_at = ?9, updated_by = ?10",
1971 )
1972 .bind(&[
1973 product.as_str().into(),
1974 meter.as_str().into(),
1975 bucket.as_str().into(),
1976 crate::optional(price_meter.as_deref()),
1977 crate::optional(own_meter.as_deref()),
1978 i32::from(a.scale_to_own).into(),
1979 drift.into(),
1980 a.note.trim().into(),
1981 now.as_str().into(),
1982 a.by.as_str().into(),
1983 ])?
1984 .run()
1985 .await?;
1986 self.audit("costs", "cost_mapping", &format!("{product}/{meter} → {bucket}"), &a.by).await?;
1987 Ok(Outcome::Ok(CostMapping {
1988 product,
1989 meter,
1990 bucket,
1991 price_meter,
1992 own_meter,
1993 scale_to_own: a.scale_to_own,
1994 drift_percent: drift,
1995 note: a.note.trim().to_owned(),
1996 updated_at: now,
1997 updated_by: a.by,
1998 }))
1999 }
2000
2001 /// `admin_costs`: the Costs & margin page.
2002 pub(crate) async fn admin_costs(&self, a: AdminCostsArgs, configured: bool) -> Result<CostsReport> {
2003 let until = rfc3339(now_ms())[..10].to_owned();
2004 let span = u64::from(a.days.unwrap_or(30).clamp(7, 90));
2005 let since = day_before(&until, span - 1);
2006 let days = self.margin_days(&since, &until).await?;
2007 let rules = self.rules().await?;
2008
2009 let mut products: BTreeMap<String, ProductMargin> = BTreeMap::new();
2010 let mut overall = OverallMargin::default();
2011 for d in &days {
2012 let p = products.entry(d.bucket.clone()).or_insert_with(|| ProductMargin {
2013 bucket: d.bucket.clone(),
2014 title: costs::bucket_title(&d.bucket),
2015 cost_source: if NOT_CLOUDFLARE.contains(&d.bucket.as_str()) { "ledger" } else { "cloudflare" }.into(),
2016 overhead: OVERHEAD.contains(&d.bucket.as_str()),
2017 ..ProductMargin::default()
2018 });
2019 p.cf_cost_micros += d.cf_cost_micros;
2020 p.own_cost_micros += d.own_cost_micros;
2021 p.value_micros += d.value_micros;
2022 p.cost_micros += d.cost();
2023 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 running2024 overall.given_micros += d.given.total();
2025 if NOT_CLOUDFLARE.contains(&d.bucket.as_str()) {
2026 overall.models_cost_micros += d.cost();
2027 } else {
2028 overall.cloudflare_cost_micros += d.cost();
2029 }
2030 overall.given_comped_micros += d.given.comped;
2031 overall.given_free_micros += d.given.free;
2032 overall.given_trial_micros += d.given.trial;
2033 overall.given_pool_micros += d.given.pool;
Merge branch 'worktree-agent-a633ac0f7f66d419d'2034 overall.given_discount_micros += d.given.discount;
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging2035 overall.given_credit_promotional_micros += d.given.credit_promotional;
2036 overall.given_credit_goodwill_micros += d.given.credit_goodwill;
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb972037 overall.given_reset_micros += d.given.reset;
Merge Cloudflare's usage over its billing cycle: every page read, included amounts once a cycle, a projection, test-mode charges never money in (billing 0052)2038 overall.given_unpaid_micros += d.given.unpaid;
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running2039 let sold = (d.cost() - d.given.total()).max(0);
2040 if OVERHEAD.contains(&d.bucket.as_str()) {
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily2041 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 running2042 overall.running_cost_micros += sold;
2043 } else if d.bucket == UNMAPPED {
2044 overall.usage_micros += d.cash_micros;
2045 overall.unmapped_cost_micros += sold;
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily2046 } else {
2047 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 running2048 overall.usage_cost_micros += sold;
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily2049 }
2050 }
2051 for p in products.values_mut() {
2052 p.margin_micros = p.value_micros - p.cost_micros;
2053 p.margin_percent = margin_percent(p.value_micros, p.cost_micros);
2054 }
2055 let revenue = overall.usage_micros + overall.plans_micros;
2056 overall.margin_micros = revenue - overall.cost_micros;
2057 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 it2058 let sold = (overall.cost_micros - overall.given_micros).max(0);
2059 overall.sold_margin_micros = revenue - sold;
2060 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)2061 // The plan's included usage was paid for by the plan's price: it is
2062 // money in for the usage it covered, and out of what the plans
2063 // leave for running g1t.
2064 #[derive(Deserialize)]
2065 struct Included {
2066 micros: Option<i64>,
2067 }
Merge Cloudflare's usage over its billing cycle: every page read, included amounts once a cycle, a projection, test-mode charges never money in (billing 0052)2068 // Only what a plan paid for with real money (`without_real_money`).
2069 let live_since = self.payments_live_since().await?;
2070 overall.included_micros = match &live_since {
2071 None => 0,
2072 Some(live) => self
2073 .db
2074 .prepare(format!(
2075 "SELECT SUM(COALESCE(credit_micros, 0)) AS micros FROM ledger
2076 WHERE kind = 'usage' AND created_at >= ?1 AND created_at <= ?2 AND workspace NOT IN ({})",
2077 crate::sales::INTERNAL_SQL
2078 ))
2079 .bind(&[std::cmp::max(since.clone(), live.clone()).into(), format!("{until}T23:59:59.999Z").into()])?
2080 .first::<Included>(None)
2081 .await?
2082 .and_then(|r| r.micros)
2083 .unwrap_or(0),
2084 };
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)2085 let usage_in = overall.usage_micros + overall.included_micros;
2086 overall.usage_margin_micros = usage_in - overall.usage_cost_micros;
2087 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 charging2088 // Credits from g1t over the range: given, spent, and refunds' money
2089 // given back.
2090 overall.credits_given_micros = self
2091 .db
2092 .prepare("SELECT SUM(amount_micros) AS micros FROM credit_grants WHERE created_at >= ?1 AND created_at <= ?2")
2093 .bind(&[since.as_str().into(), format!("{until}T23:59:59.999Z").into()])?
2094 .first::<Included>(None)
2095 .await?
2096 .and_then(|r| r.micros)
2097 .unwrap_or(0);
2098 let (draws, refunds) = self.credit_effects(&since, &until).await?;
2099 overall.credits_used_micros = draws.iter().map(|(_, d)| d.micros).sum();
2100 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 person2101 // Tax and card fees came in with payments but are neither cash nor
2102 // revenue: balances and plan payments are credited without them
2103 // (tax.rs), so cash above never holds them. Shown apart.
2104 (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 daily2105 let mut products: Vec<ProductMargin> = products.into_values().collect();
2106 products.sort_by_key(|p| std::cmp::Reverse(p.cost_micros.max(p.value_micros)));
2107
2108 #[derive(Deserialize)]
2109 struct DriftRow {
2110 bucket: String,
2111 kind: String,
2112 ours: f64,
2113 cloudflare: f64,
2114 delta_percent: Option<f64>,
2115 detail: String,
2116 found_at: String,
2117 }
2118 let drift = self
2119 .db
2120 .prepare("SELECT * FROM cost_drift ORDER BY kind, bucket")
2121 .all()
2122 .await?
2123 .results::<DriftRow>()?
2124 .into_iter()
2125 .map(|r| CostDrift {
2126 title: costs::bucket_title(&r.bucket),
2127 bucket: r.bucket,
2128 kind: r.kind,
2129 ours: r.ours,
2130 cloudflare: r.cloudflare,
2131 delta_percent: r.delta_percent,
2132 detail: r.detail,
2133 found_at: r.found_at,
2134 })
2135 .collect();
2136
2137 #[derive(Deserialize)]
2138 struct Top {
2139 workspace: String,
2140 cost: Option<i64>,
2141 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 it2142 given: Option<i64>,
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily2143 internal: i64,
2144 }
2145 let top_workspaces = self
2146 .db
2147 .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 it2148 "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 daily2149 CASE WHEN workspace IN ({}) THEN 1 ELSE 0 END AS internal
2150 FROM workspace_costs WHERE day >= ?1 AND day <= ?2 GROUP BY workspace ORDER BY cost DESC LIMIT 15",
2151 crate::sales::INTERNAL_SQL
2152 ))
2153 .bind(&[since.as_str().into(), until.as_str().into()])?
2154 .all()
2155 .await?
2156 .results::<Top>()?
2157 .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 it2158 .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 daily2159 .collect();
2160
2161 #[derive(Deserialize)]
2162 struct Summary {
2163 source: String,
2164 product: String,
2165 meter: String,
2166 raw_name: String,
2167 unit: String,
2168 quantity: f64,
2169 cost_usd: f64,
2170 }
2171 let lines = self
2172 .db
2173 .prepare(
2174 "SELECT source, product, meter, MAX(raw_name) AS raw_name, MAX(unit) AS unit, SUM(quantity) AS quantity, SUM(cost_usd) AS cost_usd
2175 FROM cost_lines WHERE day >= ?1 AND day <= ?2 GROUP BY source, product, meter ORDER BY cost_usd DESC, product, meter LIMIT 200",
2176 )
2177 .bind(&[since.as_str().into(), until.as_str().into()])?
2178 .all()
2179 .await?
2180 .results::<Summary>()?
2181 .into_iter()
2182 .map(|l| CostLineSummary {
2183 bucket: costs::classify(&rules, &l.product, &l.meter).map(|r| r.bucket.clone()),
2184 product: l.product,
2185 meter: l.meter,
2186 raw_name: l.raw_name,
2187 unit: l.unit,
2188 source: l.source,
2189 quantity: l.quantity,
2190 cost_micros: micros(l.cost_usd),
2191 })
2192 .collect();
2193
2194 #[derive(Deserialize)]
2195 struct MapRow {
2196 product: String,
2197 meter: String,
2198 bucket: String,
2199 price_meter: Option<String>,
2200 own_meter: Option<String>,
2201 scale_to_own: i64,
2202 drift_percent: f64,
2203 note: String,
2204 updated_at: String,
2205 updated_by: String,
2206 }
2207 let mappings = self
2208 .db
2209 .prepare("SELECT * FROM cost_map ORDER BY product, meter")
2210 .all()
2211 .await?
2212 .results::<MapRow>()?
2213 .into_iter()
2214 .map(|m| CostMapping {
2215 product: m.product,
2216 meter: m.meter,
2217 bucket: m.bucket,
2218 price_meter: m.price_meter,
2219 own_meter: m.own_meter,
2220 scale_to_own: m.scale_to_own == 1,
2221 drift_percent: m.drift_percent,
2222 note: m.note,
2223 updated_at: m.updated_at,
2224 updated_by: m.updated_by,
2225 })
2226 .collect();
2227
2228 #[derive(Deserialize)]
2229 struct Fetched {
2230 at: Option<String>,
2231 }
2232 let fetched_at = self.db.prepare("SELECT MAX(fetched_at) AS at FROM cost_lines").first::<Fetched>(None).await?.and_then(|f| f.at);
2233
Merge Cloudflare's usage over its billing cycle: every page read, included amounts once a cycle, a projection, test-mode charges never money in (billing 0052)2234 // Cloudflare's subscriptions over the range, day by day as each
2235 // day's share of its billing cycle; and the cycle itself.
2236 let anchor = self.cycle_anchor().await?;
2237 let fixed = self.fixed_monthly(self.caps.fixed_monthly).await?;
2238 overall.subscriptions_micros = crate::cycle::accrued(fixed.monthly_micros, &since, &until, anchor);
2239 let cycle = self.cloudflare_cycle(&until, anchor, fixed.monthly_micros).await?;
2240 let bill_read = self.bill_read().await?;
2241 // What AI Gateway priced g1t's own provider traffic at, beside the
2242 // ledger's model cost (Cloudflare-billed requests are Cloudflare's).
2243 overall.gateway_cost_micros = self
2244 .db
2245 .prepare("SELECT SUM(cost_usd) AS cost FROM cost_lines WHERE source = ?1 AND day >= ?2 AND day <= ?3 AND substr(meter, 1, 11) <> ?4")
2246 .bind(&[costs::SOURCE_GATEWAY.into(), since.as_str().into(), until.as_str().into(), costs::GATEWAY_WHOLESALE.into()])?
2247 .first::<CostSum>(None)
2248 .await?
2249 .and_then(|c| c.cost)
2250 .map_or(0, micros);
2251 // The workspaces' shares of the cost, and what no one's usage carried.
2252 #[derive(Deserialize)]
2253 struct Shared {
2254 micros: Option<i64>,
2255 }
2256 let shared = self
2257 .db
2258 .prepare("SELECT SUM(cost_micros) AS micros FROM workspace_costs WHERE day >= ?1 AND day <= ?2")
2259 .bind(&[since.as_str().into(), until.as_str().into()])?
2260 .first::<Shared>(None)
2261 .await?
2262 .and_then(|s| s.micros)
2263 .unwrap_or(0);
2264 let unattributed_micros = unattributed(overall.cost_micros, shared);
2265
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily2266 Ok(CostsReport {
2267 configured,
2268 fetched_at,
2269 days: days
2270 .iter()
2271 .map(|d| CostDay {
2272 day: d.day.clone(),
2273 bucket: d.bucket.clone(),
2274 cf_cost_micros: d.cf_cost_micros,
2275 own_cost_micros: d.own_cost_micros,
2276 value_micros: d.value_micros,
2277 cash_micros: d.cash_micros,
2278 })
2279 .collect(),
2280 since,
2281 until,
2282 products,
2283 overall,
2284 drift,
2285 alerts: self.admin_cost_alerts(AdminCostAlertsArgs {}).await?,
2286 proposals: self.proposals().await?,
2287 versions: self.versions().await?,
2288 top_workspaces,
2289 lines,
2290 mappings,
2291 settings: self.cost_settings().await?,
Spend caps: a monthly budget for comped workspaces and a daily breaker on what g1t pays2292 caps: self.spend_caps().await?,
Merge Cloudflare's usage over its billing cycle: every page read, included amounts once a cycle, a projection, test-mode charges never money in (billing 0052)2293 cycle,
2294 bill_read,
2295 unattributed_micros,
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily2296 })
2297 }
Merge Cloudflare's usage over its billing cycle: every page read, included amounts once a cycle, a projection, test-mode charges never money in (billing 0052)2298
2299 /// Cloudflare's billing cycle that `today` is in: its usage cost so far,
2300 /// by meter, with the included amounts, and where it is heading. None
2301 /// while nothing of it has been read.
2302 async fn cloudflare_cycle(&self, today: &str, anchor: u32, monthly_micros: i64) -> Result<Option<CloudflareCycle>> {
2303 #[derive(Deserialize)]
2304 struct Row {
2305 product: String,
2306 meter: String,
2307 raw_name: String,
2308 unit: String,
2309 quantity: f64,
2310 billable_quantity: Option<f64>,
2311 cost_usd: f64,
2312 basis: Option<String>,
2313 }
2314 let cycle = crate::cycle::cycle_of(today, anchor);
2315 let rows = self
2316 .db
2317 .prepare(
2318 "SELECT product, meter, MAX(raw_name) AS raw_name, MAX(unit) AS unit, SUM(quantity) AS quantity,
2319 SUM(billable_quantity) AS billable_quantity, SUM(cost_usd) AS cost_usd, MAX(basis) AS basis
2320 FROM cost_lines WHERE source = ?1 AND day >= ?2 AND day <= ?3 GROUP BY product, meter",
2321 )
2322 .bind(&[SOURCE_BILLABLE.into(), cycle.start.as_str().into(), today.into()])?
2323 .all()
2324 .await?
2325 .results::<Row>()?;
2326 if rows.is_empty() {
2327 return Ok(None);
2328 }
2329 let elapsed = cycle.days_elapsed(today);
2330 let meters = rows
2331 .into_iter()
2332 .map(|r| {
2333 let list = crate::cycle::list_price(&r.product, &r.meter);
2334 CycleMeter {
2335 included: list.map(|p| if p.daily { p.included * elapsed as f64 } else { p.included }),
2336 billable_quantity: r.billable_quantity.unwrap_or(0.0),
2337 cost_micros: micros(r.cost_usd),
2338 basis: r.basis.filter(|b| !b.is_empty()).unwrap_or_else(|| crate::cycle::BASIS_NONE.to_owned()),
2339 product: r.product,
2340 meter: r.meter,
2341 raw_name: r.raw_name,
2342 unit: r.unit,
2343 quantity: r.quantity,
2344 }
2345 })
2346 .collect();
2347 Ok(Some(cycle_report(&cycle, elapsed, meters, monthly_micros)))
2348 }
2349
2350 /// The last read of billable usage (`cost_reads`).
2351 async fn bill_read(&self) -> Result<Option<BillRead>> {
2352 #[derive(Deserialize)]
2353 struct Row {
2354 read_at: String,
2355 since: String,
2356 until: String,
2357 rows: u32,
2358 pages: u32,
2359 consumed_rows: u32,
2360 pricing_only_rows: u32,
2361 costed_rows: u32,
2362 }
2363 Ok(self
2364 .db
2365 .prepare("SELECT * FROM cost_reads WHERE source = ?1")
2366 .bind(&[SOURCE_BILLABLE.into()])?
2367 .first::<Row>(None)
2368 .await?
2369 .map(|r| BillRead {
2370 read_at: r.read_at,
2371 since: r.since,
2372 until: r.until,
2373 rows: r.rows,
2374 pages: r.pages,
2375 consumed_rows: r.consumed_rows,
2376 pricing_only_rows: r.pricing_only_rows,
2377 costed_rows: r.costed_rows,
2378 }))
2379 }
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily2380}
2381
Merge Cloudflare's usage over its billing cycle: every page read, included amounts once a cycle, a projection, test-mode charges never money in (billing 0052)2382#[derive(Deserialize)]
2383struct CostSum {
2384 cost: Option<f64>,
2385}
2386
2387/// Of `total` cost, what the workspaces' shares did not carry: running g1t
2388/// on days no workspace used anything. Never below zero.
2389pub(crate) fn unattributed(total: i64, shared: i64) -> i64 {
2390 (total - shared).max(0)
2391}
2392
2393/// The cycle as sudo shows it: the meters, most costly first, their total,
2394/// the average day and Cloudflare's projection.
2395pub(crate) fn cycle_report(cycle: &crate::cycle::Cycle, elapsed: u32, mut meters: Vec<CycleMeter>, monthly_micros: i64) -> CloudflareCycle {
2396 meters.sort_by(|a, b| b.cost_micros.cmp(&a.cost_micros).then(b.quantity.total_cmp(&a.quantity)).then(a.meter.cmp(&b.meter)));
2397 let usage: i64 = meters.iter().map(|m| m.cost_micros).sum();
2398 let days = cycle.days();
2399 CloudflareCycle {
2400 start: cycle.start.clone(),
2401 end: cycle.end.clone(),
2402 days,
2403 days_elapsed: elapsed,
2404 usage_micros: usage,
2405 projected_micros: crate::cycle::project(usage, elapsed, days),
2406 average_daily_micros: if elapsed > 0 { usage / elapsed as i64 } else { usage },
2407 subscriptions_micros: monthly_micros,
2408 meters,
2409 }
2410}
2411
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily2412#[cfg(test)]
2413mod tests {
2414 use super::*;
2415
Margin alerts measure what is sold, and say dollars when a percentage would mislead2416 #[test]
Models' margin read -14%: usage nothing paid for is valued at price, not $02417 fn usage_nothing_paid_for_is_valued_at_price_and_paid_usage_at_what_was_paid() {
2418 // A free period: charged nothing, drawn from nothing.
2419 assert_eq!(usage_value(false, 1_000_000, 0, 20), 1_200_000);
2420 // Charged, or drawn from a trial: what was paid.
2421 assert_eq!(usage_value(false, 1_000_000, 1_200_000, 20), 1_200_000);
2422 assert_eq!(usage_value(false, 1_000_000, 900_000, 20), 900_000);
2423 // g1t's own: at price.
2424 assert_eq!(usage_value(true, 1_000_000, 0, 20), 1_200_000);
2425 // No cost, nothing paid: nothing.
2426 assert_eq!(usage_value(false, 0, 0, 20), 0);
2427 }
2428
2429 #[test]
Margin alerts measure what is sold, and say dollars when a percentage would mislead2430 fn the_overall_alert_says_dollars_while_little_comes_in() {
2431 let small = overall_detail(90_000, 7_500_000, 3, 10.0, -8239.7);
2432 assert!(small.contains("took in $0.09 against $7.50"), "{small}");
2433 assert!(!small.contains('%'), "{small}");
2434 let real = overall_detail(30_000_000, 40_000_000, 3, 10.0, -33.3);
2435 assert!(real.contains("as low as -33.3%"), "{real}");
2436 }
2437
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily2438 fn rule(product: &str, meter: &str, bucket: &str, own: Option<&str>) -> Rule {
2439 Rule { product: product.into(), meter: meter.into(), bucket: bucket.into(), price_meter: None, own_meter: own.map(Into::into), drift_percent: 10.0 }
2440 }
2441
2442 fn rules() -> Vec<Rule> {
2443 vec![
2444 rule("containers", "*", "sandboxes", None),
2445 rule("workers", "*", "platform", None),
2446 rule("artifacts", "*", "git", Some("git_operations")),
2447 rule("artifacts", "events_", "git", Some("git_operations")),
2448 ]
2449 }
2450
2451 fn revenue_map() -> BTreeMap<String, String> {
2452 [("sandbox", "sandboxes"), ("git", "git"), ("plan", "platform")].iter().map(|(k, v)| (k.to_string(), v.to_string())).collect()
2453 }
2454
2455 fn line(day: &str, source: &str, product: &str, meter: &str, quantity: f64, cost: f64) -> LineRow {
2456 LineRow { day: day.into(), source: source.into(), product: product.into(), meter: meter.into(), quantity, cost_usd: cost }
2457 }
2458
2459 fn usage(day: &str, workspace: &str, key: &str, value: i64, cash: i64, cost: i64) -> UsageRow {
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb972460 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 daily2461 }
2462
2463 #[test]
2464 fn a_day_puts_the_bill_g1ts_counts_and_charges_side_by_side() {
2465 let lines = vec![
2466 line("2026-10-15", SOURCE_BILLABLE, "containers", "container_memory", 1000.0, 2.00),
2467 line("2026-10-15", SOURCE_BILLABLE, "artifacts", "artifacts_operations", 30_000.0, 3.00),
2468 // Artifacts' own events: not used while the bill has a count.
2469 line("2026-10-15", SOURCE_ARTIFACTS, "artifacts", "events_pull", 29_000.0, 0.0),
2470 line("2026-10-15", SOURCE_BILLABLE, "workers", "workers_cpu_ms", 1.0, 0.50),
2471 line("2026-10-15", SOURCE_BILLABLE, "browser_rendering", "browser_hours", 2.0, 0.25),
2472 ];
2473 let own = vec![
2474 OwnRow { day: "2026-10-15".into(), meter: "git_operations".into(), workspace: "acme".into(), quantity: 7_500.0 },
2475 OwnRow { day: "2026-10-15".into(), meter: "git_operations".into(), workspace: "beta".into(), quantity: 2_500.0 },
2476 ];
2477 let usage = vec![
2478 usage("2026-10-15", "acme", "sandbox", 2_400_000, 1_000_000, 2_000_000),
2479 usage("2026-10-15", "beta", "sandbox", 1_200_000, 1_200_000, 1_000_000),
2480 usage("2026-10-15", "acme", "git", 600_000, 600_000, 500_000),
2481 usage("2026-10-15", "acme", "implement", 120_000, 120_000, 100_000),
2482 usage("2026-10-15", "beta", "plan", 20_000_000, 20_000_000, 0),
2483 ];
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 running2484 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 daily2485 let get = |bucket: &str| days.iter().find(|d| d.bucket == bucket).unwrap();
2486 let sandboxes = get("sandboxes");
2487 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));
2488 let git = get("git");
2489 assert_eq!(git.cf_cost_micros, 3_000_000);
2490 assert_eq!((git.cf_quantity, git.own_quantity), (30_000.0, 10_000.0));
2491 assert_eq!(get("platform").value_micros, 20_000_000);
2492 // Not mapped: a leak until someone maps it.
2493 assert_eq!(get(UNMAPPED).cf_cost_micros, 250_000);
2494 // Models: no Cloudflare line, their cost is g1t's own.
2495 assert_eq!(get("models").cost(), 100_000);
2496 // Git's cost shared by g1t's own counts (Cloudflare gave none per
2497 // workspace here): three quarters to acme.
2498 let share = |ws: &str, bucket: &str| workspaces.iter().find(|w| w.workspace == ws && w.bucket == bucket).map(|w| (w.cost, w.revenue));
2499 assert_eq!(share("acme", "git"), Some((2_250_000, 600_000)));
2500 assert_eq!(share("beta", "git"), Some((750_000, 0)));
2501 // Every bucket's cost is shared out exactly.
2502 for d in &days {
2503 let shared: i64 = workspaces.iter().filter(|w| w.bucket == d.bucket).map(|w| w.cost).sum();
2504 assert_eq!(shared, d.cost(), "{}", d.bucket);
2505 }
2506 }
2507
2508 #[test]
Merge costs and margin review: gateway query, own spend, discount meters, superseded rises2509 fn a_planning_run_is_model_cost_not_running_g1t() {
2510 // flagon-io's planning run on 2026-10-07 cost $0.0748 of model
2511 // calls; read under the ledger's task, `plan`, it went to running
2512 // g1t, where Cloudflare's bill is the cost, and the model cost was
2513 // lost from the statement and the drift.
2514 let usage = vec![
2515 usage("2026-10-07", "flagon-io", PLANNING_KEY, 89_741, 0, 74_784),
2516 usage("2026-10-07", "acme", "plan", 666_666, 666_666, 0),
2517 ];
2518 let (days, _) = fold(&rules(), &revenue_map(), &[], &[], &usage, &BTreeSet::new());
2519 let get = |bucket: &str| days.iter().find(|d| d.bucket == bucket).unwrap();
2520 assert_eq!((get("models").cost(), get("models").value_micros), (74_784, 89_741));
2521 assert_eq!((get("platform").own_cost_micros, get("platform").cash_micros), (0, 666_666));
2522 assert!(!revenue_map().contains_key(PLANNING_KEY));
2523 }
2524
2525 #[test]
2526 fn a_comped_workspaces_month_end_meters_are_given_never_money_in() {
2527 let snap = |day: &str, cost: i64, charge: i64| (day.to_string(), "flagon-io".to_string(), "cache".to_string(), cost, charge);
2528 let rows: Vec<UsageRow> = pending_deltas(&[snap("2026-10-07", 1, 2), snap("2026-10-08", 473, 568)]).into_iter().map(comped_meter).collect();
2529 assert_eq!(rows.iter().map(|r| (r.cash, r.value, r.given.comped)).collect::<Vec<_>>(), vec![(0, 2, 2), (0, 566, 566)]);
2530 let internal: BTreeSet<String> = ["flagon-io".to_string()].into();
2531 let (days, workspaces) = fold(&rules(), &revenue_map(), &[], &[], &rows, &internal);
2532 assert!(days.iter().all(|d| d.cash_micros == 0));
2533 assert!(workspaces.iter().all(|w| w.revenue == 0));
2534 }
2535
2536 #[test]
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily2537 fn artifacts_events_count_when_the_bill_does_not() {
2538 let lines = vec![
2539 line("2026-10-05", SOURCE_ARTIFACTS, "artifacts", "events_pull", 120.0, 0.0),
2540 line("2026-10-05", SOURCE_ARTIFACTS, "artifacts", "events_push", 30.0, 0.0),
2541 line("2026-10-05", SOURCE_ARTIFACTS, "artifacts", "events_ratelimited", 9.0, 0.0),
2542 ];
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 running2543 let (days, _) = fold(&rules(), &revenue_map(), &lines, &[], &[], &BTreeSet::new());
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily2544 assert_eq!(days[0].cf_quantity, 150.0);
2545 assert_eq!(days[0].cf_cost_micros, 0);
2546 }
2547
2548 #[test]
2549 fn month_end_meters_are_told_by_the_day_from_snapshots() {
2550 let snap = |day: &str, cost: i64, charge: i64| (day.to_string(), "acme".to_string(), "git".to_string(), cost, charge);
2551 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)]);
2552 assert_eq!(
2553 rows.iter().map(|r| (r.day.as_str(), r.cost, r.value)).collect::<Vec<_>>(),
2554 vec![("2026-10-30", 100, 120), ("2026-10-31", 150, 180), ("2026-11-01", 40, 48)]
2555 );
2556 }
2557
2558 #[test]
2559 fn a_plan_payment_is_spread_over_the_month_it_pays_for() {
2560 let days = spread("2026-10-01T00:00:00.000Z", 20_000_000, 30);
2561 assert_eq!(days.len(), 30);
2562 assert_eq!(days[0], ("2026-10-01".to_string(), 666_667));
2563 assert_eq!(days[29], ("2026-10-30".to_string(), 666_666));
2564 assert_eq!(days.iter().map(|d| d.1).sum::<i64>(), 20_000_000);
2565 assert!(spread("2026-10-01", 0, 30).is_empty());
2566 assert_eq!(dollars(17_024_000), "$17.02");
2567 assert_eq!(dollars(-27_668_620), "-$27.67");
2568 assert_eq!(dollars(63_000), "$0.063");
2569 }
2570
2571 #[test]
2572 fn margins_and_deltas() {
2573 assert_eq!(margin_percent(1_200_000, 1_000_000).map(|m| (m * 100.0).round() / 100.0), Some(16.67));
2574 assert_eq!(margin_percent(0, 5), None);
2575 assert_eq!(delta_percent(110.0, 100.0), Some(10.0));
2576 assert_eq!(delta_percent(1.0, 0.0), None);
2577 }
2578
2579 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 running2580 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 daily2581 }
2582
2583 #[test]
2584 fn counts_more_than_the_threshold_apart_are_drift() {
2585 // Cloudflare counted 30,000 operations where g1t counted 10,000:
2586 // binding reads, perhaps. -66.7%.
2587 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);
2588 assert_eq!(drift.iter().map(|d| d.kind).collect::<Vec<_>>(), vec![DriftKind::Count, DriftKind::Cost]);
2589 assert!((drift[0].delta_percent.unwrap() + 66.666).abs() < 0.01);
2590 // 9% apart: within 10%.
2591 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());
2592 // Uncounted products have no count drift.
2593 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());
2594 }
2595
2596 #[test]
2597 fn cost_with_no_revenue_is_a_leak_but_not_for_running_g1t() {
2598 let leak = drifts("actions_cache", &[day("actions_cache", 400_000, 0, 0, 0.0, 0.0)], 10.0, false, 100_000);
2599 assert_eq!(leak.len(), 1);
2600 assert_eq!(leak[0].kind, DriftKind::Leak);
2601 assert!(drifts("platform", &[day("platform", 5_000_000, 0, 0, 0.0, 0.0)], 10.0, false, 100_000).is_empty());
2602 // Pennies say nothing.
2603 assert!(drifts("actions_cache", &[day("actions_cache", 50_000, 0, 0, 0.0, 0.0)], 10.0, false, 100_000).is_empty());
2604 assert!(drifts(UNMAPPED, &[day(UNMAPPED, 250_000, 0, 0, 0.0, 0.0)], 10.0, false, 100_000)[0].kind == DriftKind::Leak);
2605 }
2606
2607 #[test]
2608 fn a_margin_alert_needs_n_days_in_a_row_under_the_floor() {
2609 let s = |d: &str, revenue: i64, cost: i64| (d.to_string(), revenue, cost);
2610 // 5%, 0%, -20%: three days under 10%.
2611 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)];
2612 let (from, worst) = breach(&series, 10.0, 3, 100_000).unwrap();
2613 assert_eq!(from, "10-14");
2614 assert!((worst + 20.0).abs() < 1e-9);
2615 // A good day in the window clears it.
2616 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)];
2617 assert!(breach(&mended, 10.0, 3, 100_000).is_none());
2618 // Cost with no revenue at all is the worst margin there is.
2619 assert_eq!(breach(&[s("10-16", 0, 500_000)], 10.0, 1, 100_000).unwrap().1, -100.0);
2620 // Too little cost to judge.
2621 assert!(breach(&[s("10-16", 0, 5_000)], 10.0, 1, 100_000).is_none());
2622 assert!(breach(&series, 10.0, 9, 100_000).is_none());
2623 }
2624
2625 #[test]
2626 fn shared_costs_add_up_to_the_bill() {
2627 let w = |k: &str, v: f64| (k.to_string(), v);
2628 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)]);
2629 assert_eq!(attribute(10, &[w("a", 3.0), w("b", 1.0), w("a", 0.0)]), vec![("a".into(), 8), ("b".into(), 2)]);
2630 assert!(attribute(10, &[w("a", 0.0)]).is_empty());
2631 assert!(attribute(0, &[w("a", 1.0)]).is_empty());
2632 }
2633
2634 #[test]
Costs: counts are compared with Cloudflare's from the day g1t started counting, so days before a meter existed are not drift2635 fn counts_are_compared_from_the_day_g1t_started_counting() {
2636 let on = |day: &str, cf: f64, own: f64| ProductDay { day: day.into(), bucket: "git".into(), cf_quantity: cf, own_quantity: own, ..ProductDay::default() };
2637 // Five days of Cloudflare's count before g1t's meter, then two that match.
2638 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)];
2639 assert!(drifts("git", &days, 10.0, true, 0).iter().all(|d| d.kind != DriftKind::Count));
2640 // A real gap on the days both counted still shows.
2641 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)];
2642 let found = drifts("git", &days, 10.0, true, 0);
2643 let count = found.iter().find(|d| d.kind == DriftKind::Count).unwrap();
2644 assert_eq!((count.ours, count.cloudflare), (531.0, 1000.0));
2645 // A meter that never counted is compared over every day.
2646 let days = vec![on("2026-10-06", 400.0, 0.0)];
2647 assert!(drifts("git", &days, 10.0, true, 0).iter().any(|d| d.kind == DriftKind::Count));
2648 }
2649
2650 #[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 it2651 fn what_g1t_gives_away_is_kept_apart_from_what_it_sells() {
2652 let map = BTreeMap::new();
2653 // A comped workspace (all of it given), one in its trial (half paid
2654 // 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 running2655 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 it2656 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 running2657 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 it2658 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 running2659 // Nothing priced that day: free use.
2660 let free = usage("2026-10-15", "gamma", "agent", 0, 0, 1_000_000);
2661 let internal = BTreeSet::from(["flagon".to_string()]);
2662 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 it2663 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 running2664 assert_eq!(models.cost(), 4_000_000);
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging2665 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 running2666 let given = |w: &str| workspaces.iter().find(|x| x.workspace == w).unwrap().given.total();
2667 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 it2668 }
2669
2670 #[test]
Merge branch 'worktree-agent-a633ac0f7f66d419d'2671 fn a_discounted_sale_keeps_its_margin_and_counts_the_discount_as_given() {
2672 // $1 of model cost at 20%, sold to an account with 30% off: charged
2673 // $0.84, and $0.36 below cost plus the margin given (as usage_rows
2674 // reads the ledger: value at price, the discount part given).
2675 let mut sale = usage("2026-10-15", "acme", "agent", 1_200_000, 840_000, 1_000_000);
2676 sale.given = Given { discount: 360_000, ..Given::default() };
2677 let (days, _) = fold(&[], &BTreeMap::new(), &[], &[], &[sale], &BTreeSet::new());
2678 let models = days.iter().find(|d| d.bucket == "models").unwrap();
2679 assert_eq!(models.value_micros, 1_200_000);
2680 assert_eq!(models.given, Given { discount: 300_000, ..Given::default() });
2681 // What was sold (cost less given) still makes the margin.
2682 let sold = models.cost() - models.given.total();
2683 assert_eq!(margin_percent(models.cash_micros, sold).map(|m| m.round()), Some(17.0));
2684 }
2685
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging2686 fn draw(kind: CreditKind, reference: &str, task: Option<&str>, at: &str, micros: i64) -> (String, crate::grants::Draw) {
2687 let draw = crate::grants::Draw { grant: "crd_a".into(), kind, reference: reference.into(), task: task.map(Into::into), at: at.into(), micros };
2688 ("acme".to_owned(), draw)
2689 }
2690
2691 #[test]
2692 fn usage_paid_for_with_credit_is_given_not_money_in() {
2693 // $1.20 of usage on $1 of cost, all of it paid with promotional credit.
2694 let mut rows = vec![usage("2026-10-15", "acme", "implement", 1_200_000, 1_200_000, 1_000_000)];
2695 apply_credits(&mut rows, &[draw(CreditKind::Promotional, "run_1", Some("implement"), "2026-10-15T10:00:00Z", 1_200_000)], &[]);
2696 assert_eq!(rows[0].cash, 0);
2697 assert_eq!(rows[0].given, Given { credit_promotional: 1_200_000, ..Given::default() });
2698 let (days, workspaces) = fold(&[], &BTreeMap::new(), &[], &[], &rows, &BTreeSet::new());
2699 let models = days.iter().find(|d| d.bucket == "models").unwrap();
2700 // Valued at its price, none of it money in, all of its cost given:
2701 // the margin on what was sold is untouched by it.
2702 assert_eq!((models.value_micros, models.cash_micros), (1_200_000, 0));
2703 assert_eq!(models.given, Given { credit_promotional: 1_000_000, ..Given::default() });
2704 assert_eq!(models.cost() - models.given.total(), 0);
2705 assert_eq!(workspaces[0].given.total(), 1_000_000);
2706 // Half paid with goodwill credit: half the cost given, half sold.
2707 let mut rows = vec![usage("2026-10-15", "acme", "implement", 1_200_000, 1_200_000, 1_000_000)];
2708 apply_credits(&mut rows, &[draw(CreditKind::Goodwill, "run_1", Some("implement"), "2026-10-15T10:00:00Z", 600_000)], &[]);
2709 let (days, _) = fold(&[], &BTreeMap::new(), &[], &[], &rows, &BTreeSet::new());
2710 let models = days.iter().find(|d| d.bucket == "models").unwrap();
2711 assert_eq!(models.cash_micros, 600_000);
2712 assert_eq!(models.given, Given { credit_goodwill: 500_000, ..Given::default() });
2713 let sold = models.cost() - models.given.total();
2714 assert_eq!(margin_percent(models.cash_micros, sold).map(|m| m.round()), Some(17.0));
2715 }
2716
2717 #[test]
2718 fn what_a_refund_pays_for_is_paid_for_and_the_refund_comes_off_its_day() {
2719 // A refund's credit pays for usage: still money in, nothing given.
2720 let mut rows = vec![usage("2026-10-15", "acme", "implement", 1_200_000, 1_200_000, 1_000_000)];
2721 apply_credits(&mut rows, &[draw(CreditKind::Refund, "run_9", Some("implement"), "2026-10-15T10:00:00Z", 1_200_000)], &[]);
2722 assert_eq!((rows[0].cash, rows[0].given), (1_200_000, Given::default()));
2723 // The $3 refunded for Oct 2 comes off that day's money in, shared
2724 // over what was paid that day.
2725 let mut rows = vec![
2726 usage("2026-10-02", "acme", "implement", 4_000_000, 4_000_000, 3_000_000),
2727 usage("2026-10-02", "acme", "sandbox", 2_000_000, 2_000_000, 1_500_000),
2728 usage("2026-10-02", "beta", "implement", 9_000_000, 9_000_000, 7_000_000),
2729 ];
2730 let refund = crate::grants::Refunded { workspace: "acme".into(), day: "2026-10-02".into(), micros: 3_000_000 };
2731 apply_credits(&mut rows, &[], std::slice::from_ref(&refund));
2732 assert_eq!((rows[0].cash, rows[1].cash, rows[2].cash), (2_000_000, 1_000_000, 9_000_000));
2733 assert!(rows.iter().all(|r| r.given == Given::default()));
2734 // Nothing paid that day: a line of its own, money in less than nothing.
2735 let mut rows = vec![];
2736 apply_credits(&mut rows, &[], &[refund]);
2737 assert_eq!((rows[0].key.as_str(), rows[0].cash, rows[0].value), ("other", -3_000_000, 0));
2738 }
2739
2740 #[test]
2741 fn credit_spent_on_month_end_meters_is_a_line_of_its_own() {
2742 // Storage is reconciled from snapshots, not its ledger line: what
2743 // credit paid of it is its own row on the day it was charged.
2744 let mut rows = vec![usage("2026-10-01", "acme", "implement", 1_000, 1_000, 800)];
2745 apply_credits(&mut rows, &[draw(CreditKind::Goodwill, "storage/2026-09", Some("storage"), "2026-10-01T00:05:00Z", 2_000_000)], &[]);
2746 assert_eq!(rows.len(), 2);
2747 assert_eq!((rows[1].key.as_str(), rows[1].cash, rows[1].value), ("storage", -2_000_000, 0));
2748 assert_eq!(rows[1].given.credit_goodwill, 2_000_000);
2749 assert_eq!(rows[0].cash, 1_000);
2750 }
2751
Merge branch 'worktree-agent-a633ac0f7f66d419d'2752 #[test]
2753 fn the_gateways_total_against_the_ledgers_model_cost_is_drift() {
2754 // The gateway priced $5 of g1t's own traffic; the ledger has $3.
2755 let short = drifts("models", &[day("models", 5_000_000, 3_000_000, 3_600_000, 0.0, 0.0)], 10.0, false, 100_000);
2756 assert_eq!(short.iter().map(|d| d.kind).collect::<Vec<_>>(), vec![DriftKind::Cost]);
2757 assert!((short[0].delta_percent.unwrap() + 40.0).abs() < 1e-9);
2758 // Gateway traffic with nothing on the ledger at all: cost drift and a leak.
2759 let none = drifts("models", &[day("models", 2_000_000, 0, 0, 0.0, 0.0)], 10.0, false, 100_000);
2760 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 said2761 // Within the threshold: nothing.
Merge branch 'worktree-agent-a633ac0f7f66d419d'2762 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 said2763 // The gateway priced nothing against a ledger that has model cost:
2764 // not agreement (a token that cannot see AI Gateway reads as no
2765 // rows), so it is said. Under the minimum, or no model cost: nothing.
2766 let silent = drifts("models", &[day("models", 0, 1_000_000, 1_200_000, 0.0, 0.0)], 10.0, false, 100_000);
2767 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 rises2768 // Why it is empty, as far as the run could tell.
2769 let why = |caveats: &costs::GatewayCaveats, read: costs::GatewayRead| models_detail(&silent[0], caveats, &[], &read);
2770 let none = costs::GatewayCaveats::default();
2771 let said = why(&none, costs::GatewayRead::Empty { visible: Some(false) });
2772 assert!(said.contains("$1.00") && said.contains("priced nothing") && said.contains("cannot see the gateway") && said.contains("AI Gateway Read"), "{said}");
2773 let said = why(&none, costs::GatewayRead::Empty { visible: Some(true) });
2774 assert!(said.contains("logged no requests") && said.contains("went around it"), "{said}");
2775 let said = why(&none, costs::GatewayRead::Failed("Cloudflare answered 500".into()));
2776 assert!(said.contains("could not be read: Cloudflare answered 500"), "{said}");
2777 assert!(why(&none, costs::GatewayRead::NotRead).contains("not read on this run"));
2778 assert!(why(&none, costs::GatewayRead::Empty { visible: None }).contains("could not be told"));
2779 // Requests with no price: neither the token nor a bypass.
2780 let unpriced = costs::GatewayCaveats { requests: 42.0, unpriced: vec!["anthropic_claude_new_1".into()], ..Default::default() };
2781 let said = why(&unpriced, costs::GatewayRead::Rows);
2782 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 said2783 assert!(drifts("models", &[day("models", 0, 50_000, 60_000, 0.0, 0.0)], 10.0, false, 100_000).is_empty());
2784 assert!(drifts("models", &[day("models", 0, 0, 0, 0.0, 0.0)], 10.0, false, 100_000).is_empty());
Merge branch 'worktree-agent-a633ac0f7f66d419d'2785 // The detail says which way and why it may be off.
2786 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 rises2787 let detail = models_detail(&short[0], &caveats, &[], &costs::GatewayRead::default());
Merge branch 'worktree-agent-a633ac0f7f66d419d'2788 assert!(detail.contains("$5.00") && detail.contains("$3.00") && detail.contains("were not charged"), "{detail}");
2789 assert!(detail.contains("3,000,000 prompt-cache read") && detail.contains("no price for anthropic_claude_new_1"), "{detail}");
2790 }
2791
2792 #[test]
2793 fn model_usage_the_gateway_cannot_price_is_drift_even_when_the_totals_agree() {
2794 assert!(unpriced_drift(&costs::GatewayCaveats::default()).is_none());
2795 // Cache tokens alone are a note on the cost drift, not drift.
2796 assert!(unpriced_drift(&costs::GatewayCaveats { cache_write_tokens: 10.0, ..Default::default() }).is_none());
2797 let (drift, detail) = unpriced_drift(&costs::GatewayCaveats { unpriced: vec!["anthropic_claude_new_1".into()], short_runs: 2, ..Default::default() }).unwrap();
2798 assert_eq!((drift.bucket.as_str(), drift.kind.as_str()), ("models", "unpriced"));
2799 assert!(detail.contains("no price for anthropic_claude_new_1") && detail.contains("2 runs were settled"), "{detail}");
2800 }
2801
2802 #[test]
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily2803 fn a_workspace_that_costs_more_than_it_pays_is_flagged() {
2804 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)];
2805 let found = anomalies(&rows, 1.0, 1_000_000);
2806 assert_eq!(found, vec![("acme".to_string(), 5_000_000, 1_000_000)]);
2807 // At twice its revenue as the threshold, $5 against $3 is fine.
2808 assert!(anomalies(&[("acme".to_string(), 5_000_000, 3_000_000)], 2.0, 1_000_000).is_empty());
2809 }
2810
2811 #[test]
2812 fn a_git_operation_costs_what_cloudflare_counts_for_it() {
2813 // $0.15 per 1,000 of Cloudflare's operations, on the charged days.
2814 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();
2815 assert!((rate - 0.000_15).abs() < 1e-12);
2816 // Cloudflare counted 3 for every 1 g1t did: binding reads count.
2817 let per_op = derived_unit_cost(rate, 300_000.0, 100_000.0).unwrap();
2818 let per_thousand_micros = per_op * unit_size("1,000 operations") * 1e6;
2819 assert!((per_thousand_micros - 450_000.0).abs() < 1e-6, "{per_thousand_micros}");
2820 // Too few of g1t's units to say.
2821 assert!(derived_unit_cost(rate, 3_000.0, 500.0).is_none());
2822 assert!(billed_rate(&[(10_000.0, 0.0)]).is_none());
2823 assert_eq!(unit_size("million requests"), 1e6);
2824 assert_eq!(unit_size("second"), 1.0);
2825 }
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb972826
2827 /// The case that started it: syntaqx's ~$8.62 of model usage was wiped
2828 /// by a testing reset, AI Gateway still priced all $11.11, and the
2829 /// ledger had $2.49 left.
2830 fn gateway_and_ledger(kept: bool) -> (Vec<ProductDay>, Vec<WorkspaceDay>) {
2831 let rules = vec![rule("ai_gateway_requests", "*", "models", None), rule("containers", "*", "sandboxes", None)];
2832 let lines = vec![
2833 line("2026-10-05", costs::SOURCE_GATEWAY, "ai_gateway_requests", "anthropic_claude_opus_5_5", 1.0, 11.11),
2834 line("2026-10-05", SOURCE_BILLABLE, "containers", "container_memory", 10.0, 0.30),
2835 ];
2836 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)];
2837 if kept {
2838 // What the reset kept (reset_costs), read back for the day.
2839 usage.extend(reset_usage(&[
2840 ("2026-10-05".into(), "syntaqx".into(), "models".into(), 8_620_000, 10_344_000),
2841 ("2026-10-05".into(), "syntaqx".into(), "sandboxes".into(), 100_000, 120_000),
2842 // The reset's own row is not usage.
2843 ("2026-10-07".into(), "syntaqx".into(), String::new(), 0, 0),
2844 ]));
2845 }
2846 fold(&rules, &revenue_map(), &lines, &[], &usage, &BTreeSet::new())
2847 }
2848
2849 #[test]
2850 fn what_a_reset_kept_is_on_the_ledgers_side_of_the_models_drift() {
2851 let models = |days: &[ProductDay]| days.iter().find(|d| d.bucket == "models").cloned().unwrap();
2852 // Without it: AI Gateway's $11.11 against the ledger's $2.49.
2853 let (days, _) = gateway_and_ledger(false);
2854 let drift = drifts("models", &[models(&days)], 10.0, false, 100_000);
2855 assert_eq!(drift.iter().map(|d| d.kind).collect::<Vec<_>>(), vec![DriftKind::Cost]);
2856 assert_eq!((drift[0].ours, drift[0].cloudflare), (2_490_000.0, 11_110_000.0));
2857 // With it: the ledger's model cost and the reset's add up to the gateway's.
2858 let (days, _) = gateway_and_ledger(true);
2859 let m = models(&days);
2860 assert_eq!(m.own_cost_micros, 11_110_000);
2861 assert!(drifts("models", &[m], 10.0, false, 100_000).is_empty());
2862 // The reset's own row makes no bucket of its own.
2863 assert!(!days.iter().any(|d| d.bucket.is_empty()));
2864 }
2865
2866 #[test]
2867 fn what_a_reset_kept_is_given_away_as_testing_resets() {
2868 let (days, workspaces) = gateway_and_ledger(true);
2869 let models = days.iter().find(|d| d.bucket == "models").unwrap();
2870 // All of syntaqx's model cost is given, none of it money in.
2871 assert_eq!(models.given, Given { reset: 8_620_000, ..Given::default() });
2872 assert_eq!(models.cash_micros, 2_988_000);
2873 // Cloudflare's sandbox cost is shared by what each workspace's usage
2874 // cost: syntaqx's half is given too.
2875 let sandboxes = days.iter().find(|d| d.bucket == "sandboxes").unwrap();
2876 assert_eq!((sandboxes.cost(), sandboxes.given.reset), (300_000, 150_000));
2877 // Who g1t paid: syntaqx is still on it, all of its cost given.
2878 let syntaqx: Vec<&WorkspaceDay> = workspaces.iter().filter(|w| w.workspace == "syntaqx").collect();
2879 assert_eq!(syntaqx.iter().map(|w| w.cost).sum::<i64>(), 8_770_000);
2880 assert!(syntaqx.iter().all(|w| w.given.reset == w.cost && w.given.total() == w.cost && w.revenue == 0));
2881 // The statement reads it back from margin_days by why.
2882 let row = MarginRow {
2883 day: models.day.clone(),
2884 bucket: models.bucket.clone(),
2885 cf_cost_micros: models.cf_cost_micros,
2886 own_cost_micros: models.own_cost_micros,
2887 value_micros: models.value_micros,
2888 cash_micros: models.cash_micros,
2889 cf_quantity: 0.0,
2890 own_quantity: 0.0,
2891 given_comped_micros: Some(0),
2892 given_free_micros: Some(0),
2893 given_trial_micros: Some(0),
2894 given_pool_micros: Some(0),
2895 given_discount_micros: Some(0),
2896 given_credit_promotional_micros: Some(0),
2897 given_credit_goodwill_micros: Some(0),
2898 given_reset_micros: Some(models.given.reset),
Merge Cloudflare's usage over its billing cycle: every page read, included amounts once a cycle, a projection, test-mode charges never money in (billing 0052)2899 given_unpaid_micros: Some(0),
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb972900 };
2901 assert_eq!(ProductDay::from(row).given, models.given);
2902 }
2903
2904 #[test]
Merge Cloudflare's usage over its billing cycle: every page read, included amounts once a cycle, a projection, test-mode charges never money in (billing 0052)2905 fn test_mode_charges_are_never_money_in() {
2906 // A $12 sandbox charge and the $20 plan, both while payments were in
2907 // Stripe's test mode: valued as before, given as unpaid, no cash.
2908 let mut rows = vec![
2909 usage("2026-10-06", "acme", "sandbox", 12_000_000, 12_000_000, 10_000_000),
2910 UsageRow { day: "2026-10-06".into(), workspace: "acme".into(), key: "plan".into(), value: 666_667, cash: 666_667, ..UsageRow::default() },
2911 ];
2912 without_real_money(&mut rows, None);
2913 assert!(rows.iter().all(|r| r.cash == 0));
2914 // A comped workspace's charge is given once, as comped.
2915 let mut comped = vec![UsageRow { given: Given { comped: 500, ..Given::default() }, ..usage("2026-10-06", "flagon-io", "sandbox", 500, 500, 400) }];
2916 without_real_money(&mut comped, None);
2917 assert_eq!((comped[0].cash, comped[0].given.total()), (0, 500));
2918 assert_eq!(rows[0].given.unpaid, 12_000_000);
2919 assert_eq!(rows[0].value, 12_000_000);
2920 assert_eq!(rows[1].given.unpaid, 666_667);
2921 let (days, workspaces) = fold(&rules(), &revenue_map(), &[], &[], &rows, &BTreeSet::new());
2922 assert_eq!(days.iter().map(|d| d.cash_micros).sum::<i64>(), 0);
2923 let sandboxes = days.iter().find(|d| d.bucket == "sandboxes").unwrap();
2924 assert_eq!(sandboxes.given.unpaid, sandboxes.cost());
2925 assert!(workspaces.iter().all(|w| w.revenue == 0));
2926 // Once live: from that day on, it is money.
2927 let mut rows = vec![
2928 usage("2026-10-06", "acme", "sandbox", 1_000_000, 1_000_000, 800_000),
2929 usage("2026-10-07", "acme", "sandbox", 1_000_000, 1_000_000, 800_000),
2930 ];
2931 without_real_money(&mut rows, Some("2026-10-07"));
2932 assert_eq!((rows[0].cash, rows[0].given.unpaid), (0, 1_000_000));
2933 assert_eq!((rows[1].cash, rows[1].given.unpaid), (1_000_000, 0));
2934 // When payments went live is kept from the first time it is seen.
2935 assert_eq!(live_since(false, Some("2026-10-07"), "2026-10-09"), None);
2936 assert_eq!(live_since(true, None, "2026-10-09").as_deref(), Some("2026-10-09"));
2937 assert_eq!(live_since(true, Some("2026-10-07"), "2026-10-09").as_deref(), Some("2026-10-07"));
2938 }
2939
2940 #[test]
2941 fn workspaces_and_running_g1t_add_up_to_the_bill() {
2942 // Running g1t on a day with usage is shared; on a day with none it
2943 // is no one's, and the report says so.
2944 let lines = vec![
2945 line("2026-10-06", SOURCE_BILLABLE, "workers", "workers_cpu_ms", 1.0, 0.10),
2946 line("2026-10-07", SOURCE_BILLABLE, "workers", "workers_cpu_ms", 1.0, 0.20),
2947 line("2026-10-07", SOURCE_BILLABLE, "containers", "container_memory_per_gib_second", 1.0, 0.09),
2948 ];
2949 let usage = vec![usage("2026-10-07", "acme", "sandbox", 100, 100, 80), usage("2026-10-07", "beta", "sandbox", 300, 300, 240)];
2950 let (days, workspaces) = fold(&rules(), &revenue_map(), &lines, &[], &usage, &BTreeSet::new());
2951 let total: i64 = days.iter().map(ProductDay::cost).sum();
2952 let shared: i64 = workspaces.iter().map(|w| w.cost).sum();
2953 assert_eq!(total, 390_000);
2954 assert_eq!(unattributed(total, shared), 100_000);
2955 assert_eq!(shared + unattributed(total, shared), total);
2956 }
2957
2958 #[test]
2959 fn the_cycle_report_projects_as_cloudflare_does() {
2960 let cycle = crate::cycle::cycle_of("2026-10-09", 28);
2961 let meter = |meter: &str, cost: i64, quantity: f64| CycleMeter { meter: meter.into(), cost_micros: cost, quantity, ..CycleMeter::default() };
2962 let report = cycle_report(&cycle, 12, vec![meter("container_memory", 92_175, 126_870.0), meter("workers_cpu_ms", 200_000, 39_160_000.0), meter("d1_rows_read", 0, 61_820_000.0)], 30_000_000);
2963 assert_eq!((report.start.as_str(), report.end.as_str(), report.days, report.days_elapsed), ("2026-09-28", "2026-10-27", 30, 12));
2964 assert_eq!(report.usage_micros, 292_175);
2965 assert_eq!(report.projected_micros, 730_438);
2966 assert_eq!(report.average_daily_micros, 24_347);
2967 assert_eq!(report.meters[0].meter, "workers_cpu_ms");
2968 assert_eq!(report.meters[2].meter, "d1_rows_read");
2969 }
2970
2971 #[test]
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb972972 fn reconciling_again_gives_the_same_answer() {
2973 assert_eq!(gateway_and_ledger(true), gateway_and_ledger(true));
2974 // A reset's kept rows are read back exactly as kept: running it
2975 // again cannot count them twice.
2976 let kept = [("2026-10-05".to_string(), "syntaqx".to_string(), "models".to_string(), 8_620_000, 10_344_000)];
2977 assert_eq!(reset_usage(&kept), reset_usage(&kept));
2978 assert_eq!(reset_usage(&kept).len(), 1);
2979 }
2980
2981 #[test]
2982 fn a_reset_from_before_resets_kept_their_cost_is_said_not_called_a_leak() {
2983 let notes = reset_notes(
2984 &[("ws_syntaqx".into(), "2026-10-07T09:41:00.000Z".into()), ("ws_acme".into(), "2026-10-08T01:00:00.000Z".into())],
2985 &[("acme".into(), "2026-10-08T01:00:00.000Z".into(), 1_500_000)],
2986 );
2987 assert_eq!(
2988 notes,
2989 vec![
2990 ResetNote { workspace: "syntaqx".into(), day: "2026-10-07".into(), recorded: false, models_micros: 0 },
2991 ResetNote { workspace: "acme".into(), day: "2026-10-08".into(), recorded: true, models_micros: 1_500_000 },
2992 ]
2993 );
2994 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 rises2995 let detail = models_detail(&drift, &costs::GatewayCaveats::default(), &notes, &costs::GatewayRead::default());
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb972996 assert!(detail.contains("includes model usage wiped by a testing reset of syntaqx on 2026-10-07"), "{detail}");
2997 assert!(detail.contains("not a leak") && detail.contains("leaves the 7 days on 2026-10-14"), "{detail}");
2998 assert!(!detail.contains("a gap that stays is a leak"), "{detail}");
2999 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}");
3000 // The models leak is not raised while such a reset is in the window.
3001 let leak = Drift { bucket: "models".into(), kind: DriftKind::Leak, ours: 0.0, cloudflare: 11_110_000.0, delta_percent: None };
3002 assert!(wiped_not_leaked(&leak, &notes));
3003 assert!(!wiped_not_leaked(&leak, &notes[1..]));
3004 assert!(!wiped_not_leaked(&Drift { bucket: "actions_cache".into(), ..leak }, &notes));
3005 // No reset: the detail is as before.
Merge costs and margin review: gateway query, own spend, discount meters, superseded rises3006 assert!(models_detail(&drift, &costs::GatewayCaveats::default(), &[], &costs::GatewayRead::default()).contains("a gap that stays is a leak"));
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb973007 }
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily3008}

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