Skip to content

Where people and agents ship software together. The open-source git platform for the whole job: issues, agents, checks and deploys to the edge.

1,026 linesCodeBlameRaw
1//! Usage other services meter through the month, charged once it is over:
2//! security scans, the actions cache, and two billing measures itself each
3//! day: private repository storage and git operations. `context` (search
4//! embeddings) is no longer reported, since the context service was
5//! retired; it stays a source so usage noted before is still charged and
6//! closed.
7//!
8//! Each reports what it cost g1t so far this month (`note_pending`), so the
9//! workspace's limit counts it as it happens. When the month is over,
10//! billing charges it once: at cost plus the margin, on the account's
11//! terms, after the plan's included usage and the trial credit (see
12//! `credits`), dated the month's last second so it falls in that month's
13//! statement and invoice.
14//!
15//! The forge is free for every workspace up to the same amounts, on the
16//! plan or not; past them, a workspace on the plan pays at cost plus the
17//! margin and is never refused or slowed, and a free workspace is never
18//! charged but is held to them.
19//!
20//! **Git operations.** Cloudflare Artifacts charges g1t $0.15 per 1,000
21//! operations (clones, fetches, pushes) from 2026-10-14. The repos service
22//! counts those through g1t's git endpoints. Every workspace has
23//! `GIT_OPERATIONS_INCLUDED` (50,000) a month free; past it, the plan pays
24//! at cost plus the margin, and a free workspace is slowed down instead
25//! (the repos service's `GIT_OPERATIONS_FREE_CAP`, the same number).
26//!
27//! **Actions cache.** The actions service reports what each workspace's
28//! `actions/cache` entries held each day (source `cache`), at what R2
29//! charges g1t to store them ($0.015 a GB-month). Only the plan is charged
30//! for it, from the first byte; a free workspace is never charged, and its
31//! repositories are held to the cache's quota like everyone's.
32//!
33//! **Storage.** The git store does not report a repository's size, so the
34//! repos service counts the packs pushed through g1t's git endpoints (see
35//! `g1t_contracts::repos::StorageArgs`): a lower bound. Each day billing
36//! records what each workspace's private repositories hold and what is
37//! free that day (`FREE_PRIVATE_STORAGE_BYTES`, 1 GB, for everyone). Like
38//! Cloudflare's own storage billing, a month's GB-months are the days'
39//! amounts past the free one, added up and divided by the month's days
40//! (28 to 31), and only the
41//! plan is charged for them. A free workspace is never charged for
42//! storage: pushes to its private repositories stop once they hold its
43//! free amount. Public repositories are never charged.
44
45use g1t_contracts::billing::PlanKind;
46use g1t_contracts::new_id;
47use g1t_contracts::repos::{GitOperationsArgs, StorageArgs, WorkspaceGitOperations, WorkspaceStorage};
48use g1t_contracts::time::rfc3339;
49use g1t_kit::now_ms;
50use serde::Deserialize;
51use worker::Result;
52
53use crate::credits::{self, Drawn, Eligible};
54use crate::{Billing, optional};
55
56/// Sources billing charges itself when the month is over: those billing
57/// measures (storage, git operations, package storage) and those other
58/// services note through the month (`note_pending`: security scans, search
59/// embeddings, the actions cache, observability data).
60pub(crate) const CHARGED_HERE: [&str; 7] = ["security", "context", "storage", "git", "cache", "package_storage", "telemetry"];
61
62/// A `pending_usage` column less what the workspace had of it when its
63/// test-mode data was detached (`pending_baselines`, migration 0060),
64/// never below nothing. Reads `pending_usage` unaliased.
65macro_rules! net_of_baseline {
66 ($column:literal) => {
67 concat!(
68 "MAX(0, COALESCE(pending_usage.", $column, ", 0) - COALESCE((SELECT b.", $column,
69 " FROM pending_baselines b WHERE b.workspace = pending_usage.workspace AND b.source = pending_usage.source",
70 " AND b.month = pending_usage.month), 0))"
71 )
72 };
73}
74
75/// What a pending row cost g1t since the detach (all of it without one):
76/// what everything that values or charges pending usage reads.
77pub(crate) const NET_COST: &str = net_of_baseline!("cost_micros");
78/// What a pending row will be charged since the detach, before terms.
79pub(crate) const NET_CHARGE: &str = net_of_baseline!("charge_micros");
80
81/// A month's figure for a source, as `note_pending` writes it. Never into
82/// a month closed for the workspace, and the update leaves a row already
83/// charged as it was: a late report of a month that is over never
84/// rewrites what was charged.
85pub(crate) const SET_PENDING: &str = "INSERT INTO pending_usage (workspace, source, month, charge_micros, cost_micros, updated_at, detail)
86 SELECT ?1, ?2, ?3, ?4, ?5, ?6, ?7
87 WHERE NOT EXISTS (SELECT 1 FROM month_closes WHERE month_closes.workspace = ?1 AND month_closes.month = ?3)
88 ON CONFLICT (workspace, source, month) DO UPDATE SET
89 charge_micros = ?4, cost_micros = ?5, updated_at = ?6, detail = COALESCE(?7, detail)
90 WHERE pending_usage.charged_at IS NULL";
91
92/// A late report's new high-water mark for a month already charged (or
93/// closed with no row): written only if the row still holds the figure the
94/// correction was worked out from (`?8`), so of two late reports at once
95/// only one moves it. `?1` workspace, `?2` source, `?3` month, `?4` charge,
96/// `?5` cost, `?6` now, `?7` detail, `?8` the cost it was worked out from.
97pub(crate) const LATE_RAISE: &str = "INSERT INTO pending_usage (workspace, source, month, charge_micros, cost_micros, updated_at, detail, charged_at)
98 VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, 'late: ' || ?6)
99 ON CONFLICT (workspace, source, month) DO UPDATE SET
100 charge_micros = ?4, cost_micros = ?5, updated_at = ?6, detail = COALESCE(?7, detail)
101 WHERE pending_usage.charged_at IS NOT NULL AND pending_usage.cost_micros = ?8";
102
103/// The correction's ledger line, entered only if this batch's `LATE_RAISE`
104/// moved the row to its figure (`?11`); its reference names that figure, so
105/// the same report again is the same line. `?1` id, `?2` workspace, `?3`
106/// amount, `?4` description, `?5` source, `?6` cost, `?7` reference, `?8`
107/// created_at, `?9` price version, `?10` month, `?11` the new cost.
108pub(crate) const LATE_LEDGER: &str = "INSERT INTO ledger
109 (id, workspace, kind, amount_micros, description, repo, task, cost_micros, reference,
110 created_at, billed_to, credit_micros, trial_micros, oss_micros, given_micros, price_version)
111 SELECT ?1, ?2, 'usage', ?3, ?4, NULL, ?5, ?6, ?7, ?8, 'g1t', 0, 0, 0, 0, ?9
112 WHERE (SELECT cost_micros FROM pending_usage WHERE workspace = ?2 AND source = ?5 AND month = ?10) = ?11
113 ON CONFLICT (reference) DO NOTHING RETURNING id";
114
115/// What a late report changes, from the figure charged (`charged`, net of
116/// any baseline) and the new one: the cost to charge on top, or nothing.
117/// A lower figure is never refunded on its own; it is logged for staff,
118/// and the high-water mark stays, so a later rise is never charged twice.
119pub(crate) fn late_delta(charged_net: i64, new_net: i64) -> i64 {
120 (new_net - charged_net).max(0)
121}
122
123/// Whether a workspace had the plan in a month by what it paid or drew:
124/// a plan payment in the month (not a complimentary enrollment's fee, paid
125/// while enrolled) that a test-mode detach has not reversed
126/// (its `_test_detach` row nets it to nothing), or the plan's included
127/// usage drawn in it. `?1` workspace, `?2` the month's first day, `?3` the
128/// next month's, `?4` the month.
129pub(crate) const PLAN_EVIDENCE: &str = "SELECT (EXISTS (SELECT 1 FROM plan_payments p WHERE p.workspace = ?1 AND p.paid_at >= ?2 AND p.paid_at < ?3
130 AND NOT EXISTS (SELECT 1 FROM complimentary_periods c WHERE c.workspace = ?1 AND c.starts_at <= p.paid_at
131 AND (c.ends_at IS NULL OR c.ends_at > p.paid_at) AND p.amount_micros = c.fee_cents * 10000)
132 AND p.amount_micros > 0 AND substr(p.invoice_id, -12) <> '_test_detach'
133 AND p.amount_micros + COALESCE((SELECT SUM(q.amount_micros) FROM plan_payments q
134 WHERE q.invoice_id = p.invoice_id || '_test_detach'), 0) > 0)
135 OR EXISTS (SELECT 1 FROM allowance_use WHERE kind = 'plan_credit' AND scope = ?1 AND month = ?4 AND used > 0)) AS had";
136
137/// Sources only the plan is charged for: a free workspace's are not kept.
138pub(crate) const PLAN_ONLY: [&str; 1] = ["cache"];
139
140/// Sources g1t pays for itself on a free workspace: security scans and
141/// (from before the context service was retired) search embeddings. No security feature is held back for the plan, and
142/// a free workspace never runs up a bill, so on a workspace without the
143/// plan they are recorded at cost, covered by g1t, and charged nothing
144/// (not even from the trial). On the plan they are charged like any usage.
145pub(crate) const COVERED_FOR_FREE: [&str; 2] = ["security", "context"];
146
147/// What pays for a month-end source's charge: the plan's included usage
148/// and the trial, and for a covered source on a free workspace, g1t.
149pub(crate) fn month_end_eligible(source: &str, plan: bool) -> Eligible {
150 let covered = !plan && COVERED_FOR_FREE.contains(&source);
151 Eligible { trial: !covered, repo: None, cover_rest: covered }
152}
153
154/// What Artifacts charges g1t, when the price book cannot be read: $0.50
155/// a GB-month of storage, and $0.15 per 1,000 git operations.
156pub(crate) const STORAGE_MICROS_PER_GB_MONTH: i64 = 500_000;
157pub(crate) const GIT_MICROS_PER_THOUSAND: i64 = 150_000;
158
159/// When Cloudflare starts charging for Artifacts operations: none before
160/// count.
161pub(crate) const GIT_BILLING_STARTS: &str = "2026-10-14T00";
162
163/// What git operations past the free amount cost g1t, at
164/// `micros_per_thousand`: nothing up to it.
165pub(crate) fn git_cost(operations: u64, included: u64, micros_per_thousand: f64) -> i64 {
166 let past = operations.saturating_sub(included);
167 (past as f64 * micros_per_thousand / 1000.0).ceil() as i64
168}
169
170/// How long after a month is over its last git operations are given to
171/// land before its final count: the repos service adds each to its hourly
172/// counts after the answer has gone back.
173pub(crate) const FINAL_MEASURE_AFTER_MS: u64 = 60 * 60 * 1000;
174
175/// How long a month's charge and close wait for its final count of git
176/// operations before going ahead without it (logged): an outage of the
177/// repos service never holds every workspace's invoice for good.
178pub(crate) const FINAL_MEASURE_WAIT_MS: u64 = 3 * 24 * 60 * 60 * 1000;
179
180/// How long ago `month` ended, at `now_ms`; none while it is not over.
181fn since_month_end(now_ms: u64, month: &str) -> Option<u64> {
182 let end = g1t_contracts::time::parse_rfc3339(&credits::next_month_start(month))?;
183 now_ms.checked_sub(end)
184}
185
186/// Whether the final count of `month`'s git operations may be taken at
187/// `now_ms`: the month is over, and its last hour has had time to land.
188pub(crate) fn final_measure_due(now_ms: u64, month: &str) -> bool {
189 since_month_end(now_ms, month).is_some_and(|ms| ms >= FINAL_MEASURE_AFTER_MS)
190}
191
192/// Whether `month`'s metered usage may be charged and the month closed:
193/// its final count of git operations was taken (`measured`), or it has
194/// waited for it long enough.
195pub(crate) fn month_final(measured: bool, now_ms: u64, month: &str) -> bool {
196 since_month_end(now_ms, month).is_some_and(|ms| measured || ms >= FINAL_MEASURE_WAIT_MS)
197}
198
199/// The first hour of `month` to count git operations from.
200pub(crate) fn git_since(month: &str) -> String {
201 let start = format!("{month}-01T00");
202 if start.as_str() < GIT_BILLING_STARTS { GIT_BILLING_STARTS.to_owned() } else { start }
203}
204
205/// A gigabyte, as Cloudflare bills storage.
206pub(crate) const GB: f64 = 1_000_000_000.0;
207
208/// GB-months from a month's daily measures, each `(private, free)` bytes:
209/// what was past the free amount each day, over the month's `month_days`.
210pub(crate) fn storage_gb_months(days: &[(i64, i64)], month_days: u32) -> f64 {
211 days.iter().map(|(private, free)| (private - free).max(0) as f64).sum::<f64>() / GB / f64::from(month_days.max(1))
212}
213
214/// GB-months of package storage past the free amounts from a month's
215/// daily measures, each `(public, private, public_free, private_free)`:
216/// public and private past their own amounts, over the month's
217/// `month_days`.
218pub(crate) fn package_gb_months(days: &[(i64, i64, i64, i64)], month_days: u32) -> f64 {
219 days.iter()
220 .map(|(public, private, public_free, private_free)| ((public - public_free).max(0) + (private - private_free).max(0)) as f64)
221 .sum::<f64>()
222 / GB
223 / f64::from(month_days.max(1))
224}
225
226/// What R2 charges g1t a GB-month, when the price book cannot be read.
227pub(crate) const PACKAGE_MICROS_PER_GB_MONTH: i64 = 15_000;
228
229/// What `gb_months` cost g1t at `micros_per_gb_month`, rounded up.
230pub(crate) fn storage_cost(gb_months: f64, micros_per_gb_month: f64) -> i64 {
231 (gb_months * micros_per_gb_month).ceil() as i64
232}
233
234/// What a source is called on the statement.
235pub(crate) fn title(source: &str) -> &'static str {
236 match source {
237 "security" => "Security scans",
238 "context" => "Search embeddings",
239 "storage" => "Private repository storage past the free amount",
240 "package_storage" => "Package storage past the free amounts",
241 "git" => "Git operations past the free amount",
242 "cache" => "Actions cache storage",
243 "domains" => "Custom domains",
244 "telemetry" => "Observability data past the free amount",
245 _ => "Metered usage",
246 }
247}
248
249/// Whether a batch's first statement, an insert `RETURNING id`, wrote its
250/// row: none comes back when `ON CONFLICT … DO NOTHING` skipped it.
251pub(crate) fn posted(results: &[worker::D1Result]) -> Result<bool> {
252 posted_at(results, 0)
253}
254
255/// `posted`, for the batch's statement at `index`.
256pub(crate) fn posted_at(results: &[worker::D1Result], index: usize) -> Result<bool> {
257 #[derive(Deserialize)]
258 struct Row {
259 #[allow(dead_code)]
260 id: String,
261 }
262 match results.get(index) {
263 Some(result) => Ok(!result.results::<Row>()?.is_empty()),
264 None => Ok(false),
265 }
266}
267
268/// A usage entry to put on the ledger.
269pub(crate) struct UsageLine<'a> {
270 pub workspace: &'a str,
271 /// What the workspace is charged, after terms and what paid for it.
272 pub charged: i64,
273 pub description: &'a str,
274 pub repo: Option<&'a str>,
275 pub task: &'a str,
276 pub cost: i64,
277 pub reference: &'a str,
278 pub created_at: &'a str,
279 pub drawn: Drawn,
280}
281
282impl Billing {
283 /// Puts a usage entry on the ledger and takes it off the balance, as
284 /// one write. False when an entry with its reference is already there
285 /// (another delivery of the same usage wrote it first): nothing is
286 /// written, and no allowance is consumed.
287 pub(crate) async fn post_usage(&self, line: UsageLine<'_>) -> Result<bool> {
288 let month = credits::month_of(line.created_at);
289 let plan = self.has_plan(line.workspace).await?;
290 self.post_usage_with(line, vec![], Some((&month, &credits::Eligible::default(), plan))).await
291 }
292
293 /// `post_usage`, with `also` written in the same batch, whether or not
294 /// the entry was new.
295 pub(crate) async fn post_usage_with(&self, line: UsageLine<'_>, also: Vec<worker::D1PreparedStatement>, allocation: Option<(&str, &credits::Eligible, bool)>) -> Result<bool> {
296 // The price versions it was charged at (pricing.rs).
297 let mut versions = Vec::new();
298 for meter in crate::pricing::meters_of(line.task) {
299 versions.extend(self.version_now(meter).await?);
300 }
301 let price_version = versions.join(",");
302 let id = new_id("led", now_ms());
303 // The ledger's unique reference is the guard: the entry goes in
304 // only if no other has it, and the balance moves only if it did.
305 let mut statements = vec![
306 self.db
307 .prepare(
308 "INSERT INTO ledger
309 (id, workspace, kind, amount_micros, description, repo, task, cost_micros, reference,
310 created_at, billed_to, credit_micros, trial_micros, oss_micros, given_micros, price_version)
311 VALUES (?, ?, 'usage', ?, ?, ?, ?, ?, ?, ?, 'g1t', ?, ?, ?, ?, ?)
312 ON CONFLICT (reference) DO NOTHING RETURNING id",
313 )
314 .bind(&[
315 id.as_str().into(),
316 line.workspace.into(),
317 (-(line.charged as f64)).into(),
318 line.description.into(),
319 optional(line.repo),
320 line.task.into(),
321 (line.cost as f64).into(),
322 line.reference.into(),
323 line.created_at.into(),
324 (line.drawn.credit as f64).into(),
325 (line.drawn.trial as f64).into(),
326 (line.drawn.oss as f64).into(),
327 // What g1t covered itself (a free workspace's scans and
328 // embeddings): given, never margin lost.
329 (line.drawn.given as f64).into(),
330 optional(Some(price_version.as_str()).filter(|v| !v.is_empty())),
331 ])?,
332 ];
333 if let Some((month, eligible, plan)) = allocation {
334 statements.extend(self.allocation_statements(&id, line.workspace, month, eligible, plan).await?);
335 }
336 statements.push(self.balance_if_posted(line.workspace, &id)?);
337 statements.extend(also);
338 let results = self.db.batch(statements).await?;
339 posted(&results)
340 }
341
342 /// Takes `charged` off the workspace's balance, only if the ledger entry
343 /// `id` was written in the same batch.
344 pub(crate) fn balance_if_posted(&self, workspace: &str, id: &str) -> Result<worker::D1PreparedStatement> {
345 self.db
346 .prepare(
347 "INSERT INTO accounts (workspace, balance_micros, created_at)
348 SELECT ?1, amount_micros, ?2 FROM ledger WHERE id = ?3
349 ON CONFLICT (workspace) DO UPDATE SET balance_micros = balance_micros + excluded.balance_micros",
350 )
351 .bind(&[workspace.into(), rfc3339(now_ms()).into(), id.into()])
352 }
353
354 /// Writes down what a source cost g1t so far in `month`, what it will
355 /// be charged, and how much of it there was (`detail`, for the Billing
356 /// page), replacing the last figure.
357 pub(crate) async fn set_pending(
358 &self,
359 workspace: &str,
360 source: &str,
361 month: &str,
362 cost_micros: i64,
363 detail: Option<&str>,
364 ) -> Result<()> {
365 // A covered source on a free workspace is never charged, so it does
366 // not count toward the workspace's limit either.
367 let covered = COVERED_FOR_FREE.contains(&source) && !self.has_plan(&workspace.to_lowercase()).await?;
368 let charge = if covered { 0 } else { credits::with_margin(cost_micros, self.margin_percent) };
369 self.db
370 .prepare(SET_PENDING)
371 .bind(&[
372 workspace.to_lowercase().into(),
373 source.into(),
374 month.into(),
375 (charge as f64).into(),
376 (cost_micros.max(0) as f64).into(),
377 rfc3339(now_ms()).into(),
378 optional(detail),
379 ])?
380 .run()
381 .await?;
382 Ok(())
383 }
384
385 /// Charges every month that is over for the usage billing charges
386 /// itself, once each.
387 pub(crate) async fn charge_pending(&self) -> Result<()> {
388 if !self.charging {
389 return Ok(());
390 }
391 let now = rfc3339(now_ms());
392 let current = credits::month_of(&now);
393 #[derive(Deserialize)]
394 struct Row {
395 workspace: String,
396 source: String,
397 month: String,
398 cost_micros: Option<i64>,
399 }
400 let marks = CHARGED_HERE.iter().map(|s| format!("'{s}'")).collect::<Vec<_>>().join(", ");
401 // Last month waits for its final count of git operations: the
402 // months before it are due.
403 let due_before = if self.last_month_final().await? { current.clone() } else { crate::limits::previous_month(&current) };
404 let due = self
405 .db
406 // Less what was metered before a test-mode detach: not charged.
407 .prepare(format!(
408 "SELECT workspace, source, month, {NET_COST} AS cost_micros FROM pending_usage
409 WHERE month < ? AND charged_at IS NULL AND source IN ({marks}) ORDER BY month LIMIT 50"
410 ))
411 .bind(&[due_before.as_str().into()])?
412 .all()
413 .await?
414 .results::<Row>()?;
415 // Each row on its own: one whose atomic posting fails
416 // is logged and tried again next tick, and never holds back the
417 // rest, whose months could otherwise not close.
418 for row in due {
419 let charged = async {
420 // Marked charged in the same batch as its ledger entry, never
421 // before: a failure in between leaves it to the next tick, and
422 // the ledger's unique reference keeps two crons from charging
423 // it twice.
424 let mark = || {
425 self.db
426 .prepare(
427 "UPDATE pending_usage SET charged_at = ?1
428 WHERE workspace = ?2 AND source = ?3 AND month = ?4 AND charged_at IS NULL",
429 )
430 .bind(&[now.as_str().into(), row.workspace.as_str().into(), row.source.as_str().into(), row.month.as_str().into()])
431 };
432 let cost = row.cost_micros.unwrap_or(0);
433 if cost <= 0 {
434 mark()?.run().await?;
435 return Ok(());
436 }
437 let base = credits::with_margin(cost, self.margin_percent);
438 let (charge, terms_note, discount) = self.charged(&row.workspace, base).await?;
439 // The plan as it was in that month, not as it is now: a plan
440 // started since does not pay for it, and one ended since still
441 // does.
442 let plan = self.had_plan_in(&row.workspace, &row.month).await?;
443 let eligible = month_end_eligible(&row.source, plan);
444 // Resolve the description before the atomic allocation and posting.
445 let detail = if row.source == "storage" {
446 let gb_months = self.gb_months(&row.workspace, &row.month).await?;
447 format!(": {gb_months:.2} GB-months")
448 } else {
449 String::new()
450 };
451 let marking = mark()?;
452 let drawn = Drawn::default();
453 let description = format!("{} in {}{detail}{terms_note}{}", title(&row.source), row.month, drawn.note());
454 let reference = format!("{}/{}/{}", row.source, row.workspace, row.month);
455 let created_at = credits::month_end(&row.month);
456 let line = UsageLine {
457 workspace: &row.workspace,
458 charged: charge - drawn.total(),
459 description: &description,
460 repo: None,
461 task: &row.source,
462 cost,
463 reference: &reference,
464 created_at: &created_at,
465 drawn,
466 };
467 if !self.post_usage_with(line, vec![marking], Some((&row.month, &eligible, plan))).await? {
468 return Ok(());
469 }
470 self.record_discount(&reference, discount).await?;
471 Ok::<(), worker::Error>(())
472 };
473 if let Err(error) = charged.await {
474 worker::console_error!("{}: charging {} for {} failed; tried again next tick: {error}", row.workspace, row.source, row.month);
475 }
476 }
477 Ok(())
478 }
479
480 /// A source's figure for a month already charged or closed, reported
481 /// late: what it adds is charged as a correction on that month (drawing
482 /// on that month's allowances, at most once for any figure), and the
483 /// row keeps the highest figure seen. Only sources billing charges
484 /// itself; true when the report was taken.
485 pub(crate) async fn late_correction(&self, workspace: &str, source: &str, month: &str, cost_micros: i64, detail: Option<&str>) -> Result<bool> {
486 if !CHARGED_HERE.contains(&source) {
487 return Ok(false);
488 }
489 #[derive(Deserialize)]
490 struct Charged {
491 cost_micros: Option<i64>,
492 net: Option<i64>,
493 baseline: Option<i64>,
494 charged_at: Option<String>,
495 }
496 let row = self
497 .db
498 .prepare(format!(
499 "SELECT cost_micros, charged_at, {NET_COST} AS net,
500 (SELECT b.cost_micros FROM pending_baselines b WHERE b.workspace = pending_usage.workspace
501 AND b.source = pending_usage.source AND b.month = pending_usage.month) AS baseline
502 FROM pending_usage WHERE workspace = ?1 AND source = ?2 AND month = ?3"
503 ))
504 .bind(&[workspace.into(), source.into(), month.into()])?
505 .first::<Charged>(None)
506 .await?;
507 let new_cost = cost_micros.max(0);
508 // A closed month whose row is not charged yet: the month-end charge
509 // still takes it, so the higher figure goes on the row for it.
510 if let Some(row) = row.as_ref().filter(|r| r.charged_at.is_none()) {
511 if new_cost > row.cost_micros.unwrap_or(0) {
512 self.db
513 .prepare(
514 "UPDATE pending_usage SET cost_micros = MAX(cost_micros, ?4), charge_micros = MAX(charge_micros, ?5), updated_at = ?6
515 WHERE workspace = ?1 AND source = ?2 AND month = ?3 AND charged_at IS NULL",
516 )
517 .bind(&[
518 workspace.into(),
519 source.into(),
520 month.into(),
521 (new_cost as f64).into(),
522 (credits::with_margin(new_cost, self.margin_percent) as f64).into(),
523 rfc3339(now_ms()).into(),
524 ])?
525 .run()
526 .await?;
527 }
528 return Ok(true);
529 }
530 let (raw_old, net_old, baseline) = row.map_or((0, 0, 0), |r| (r.cost_micros.unwrap_or(0), r.net.unwrap_or(0), r.baseline.unwrap_or(0)));
531 let delta = late_delta(net_old, (new_cost - baseline).max(0));
532 if new_cost < raw_old {
533 worker::console_warn!(
534 "{workspace}: late {source} figure for {month} is lower than what was charged ({} < {}); nothing refunded on its own",
535 new_cost,
536 raw_old
537 );
538 // Once per source and month: a reporter repeats its figure daily.
539 let account = self.account_of(workspace).await?;
540 let subject = format!("{source} for {month} reported lower after it was charged");
541 let noted = self
542 .db
543 .prepare("SELECT 1 AS found FROM admin_actions WHERE account = ?1 AND action = 'late_usage' AND substr(detail, 1, length(?2)) = ?2 LIMIT 1")
544 .bind(&[account.id.as_str().into(), subject.as_str().into()])?
545 .first::<serde_json::Value>(None)
546 .await?;
547 if noted.is_none() {
548 self.audit(
549 &account.id,
550 "late_usage",
551 &format!("{subject}: {} cost now, {} charged. Review for a refund.", crate::features::dollars(new_cost), crate::features::dollars(raw_old)),
552 "billing",
553 )
554 .await?;
555 }
556 return Ok(true);
557 }
558 if delta == 0 || new_cost == raw_old {
559 return Ok(true);
560 }
561 let now = rfc3339(now_ms());
562 let base = credits::with_margin(delta, self.margin_percent);
563 let (charge, terms_note, discount) = self.charged(workspace, base).await?;
564 let plan = self.had_plan_in(workspace, month).await?;
565 let eligible = month_end_eligible(source, plan);
566 let mut versions = Vec::new();
567 for meter in crate::pricing::meters_of(source) {
568 versions.extend(self.version_now(meter).await?);
569 }
570 let price_version = versions.join(",");
571 let id = new_id("led", now_ms());
572 let reference = format!("{source}/{workspace}/{month}/late/{new_cost}");
573 let description = format!("{} in {month}, reported after the month was charged{terms_note}", title(source));
574 let mut statements = vec![
575 self.db.prepare(LATE_RAISE).bind(&[
576 workspace.into(),
577 source.into(),
578 month.into(),
579 (credits::with_margin(new_cost, self.margin_percent) as f64).into(),
580 (new_cost as f64).into(),
581 now.as_str().into(),
582 optional(detail),
583 (raw_old as f64).into(),
584 ])?,
585 self.db.prepare(LATE_LEDGER).bind(&[
586 id.as_str().into(),
587 workspace.into(),
588 (-(charge as f64)).into(),
589 description.as_str().into(),
590 source.into(),
591 (delta as f64).into(),
592 reference.as_str().into(),
593 credits::month_end(month).into(),
594 optional(Some(price_version.as_str()).filter(|v| !v.is_empty())),
595 month.into(),
596 (new_cost as f64).into(),
597 ])?,
598 ];
599 statements.extend(self.allocation_statements(&id, workspace, month, &eligible, plan).await?);
600 statements.push(self.balance_if_posted(workspace, &id)?);
601 let results = self.db.batch(statements).await?;
602 if posted_at(&results, 1)? {
603 self.record_discount(&reference, discount).await?;
604 }
605 Ok(true)
606 }
607
608 /// Whether the workspace had the plan in `month`: g1t's own,
609 /// enterprises and a plan staff gave by their terms; otherwise a plan
610 /// invoice paid in the month and not reversed by a test-mode detach,
611 /// the plan's included usage drawn in it, or a plan on now that
612 /// started before the month ended (`PLAN_EVIDENCE`).
613 pub(crate) async fn had_plan_in(&self, workspace: &str, month: &str) -> Result<bool> {
614 let account = self.account_of(workspace).await?;
615 let now = self.plan_kind_for(workspace, &account).await?;
616 if matches!(now, PlanKind::Internal | PlanKind::Enterprise) || account.allowances.plan || !self.charging {
617 return Ok(now != PlanKind::Free);
618 }
619 let next = credits::next_month_start(month);
620 if now == PlanKind::Paid
621 && let Some((started_at, _)) = self.plan_cycle(workspace).await?
622 && started_at.as_str() < next.as_str()
623 {
624 return Ok(true);
625 }
626 #[derive(Deserialize)]
627 struct Evidence {
628 had: Option<i64>,
629 }
630 Ok(self
631 .db
632 .prepare(PLAN_EVIDENCE)
633 .bind(&[workspace.into(), format!("{month}-01").into(), next.as_str().into(), month.into()])?
634 .first::<Evidence>(None)
635 .await?
636 .and_then(|e| e.had)
637 .unwrap_or(0)
638 == 1)
639 }
640
641 /// A workspace's private storage past the free amount in `month`.
642 async fn gb_months(&self, workspace: &str, month: &str) -> Result<f64> {
643 #[derive(Deserialize)]
644 struct Day {
645 private_bytes: i64,
646 free_bytes: i64,
647 }
648 let days = self
649 .db
650 .prepare("SELECT private_bytes, free_bytes FROM storage_days WHERE workspace = ? AND substr(day, 1, 7) = ?")
651 .bind(&[workspace.into(), month.into()])?
652 .all()
653 .await?
654 .results::<Day>()?;
655 Ok(storage_gb_months(&days.iter().map(|d| (d.private_bytes, d.free_bytes)).collect::<Vec<_>>(), credits::days_in(month)))
656 }
657
658 /// Once a day: what each workspace's private repositories hold, and
659 /// what this month's storage past the free amount comes to so far.
660 pub(crate) async fn measure_storage(&self) -> Result<()> {
661 let Some(repos) = &self.repos else { return Ok(()) };
662 let list: Vec<WorkspaceStorage> = g1t_kit::call(repos, "storage", &StorageArgs {}).await?;
663 let now = rfc3339(now_ms());
664 let (day, month) = (&now[..10], credits::month_of(&now));
665 let price = self.price("private_storage").await?.map_or(STORAGE_MICROS_PER_GB_MONTH as f64, |(cost, _)| cost);
666 for workspace in list {
667 let slug = workspace.namespace.to_lowercase();
668 let plan = self.has_plan(&slug).await?;
669 // The same free amount for everyone: the plan pays past it.
670 let free = self.plans.free_storage_bytes;
671 self.db
672 .prepare(
673 "INSERT INTO storage_days (workspace, day, private_bytes, free_bytes) VALUES (?1, ?2, ?3, ?4)
674 ON CONFLICT (workspace, day) DO UPDATE SET private_bytes = ?3, free_bytes = ?4",
675 )
676 .bind(&[slug.as_str().into(), day.into(), (workspace.private_bytes as f64).into(), (free as f64).into()])?
677 .run()
678 .await?;
679 // Only the plan pays for storage past its amount. A free
680 // workspace is never charged: the repos service stops its pushes
681 // to private repositories once it is full (see git_ops.rs there).
682 let gb_months = if plan { self.gb_months(&slug, &month).await? } else { 0.0 };
683 if gb_months > 0.0 {
684 let detail = format!("{gb_months:.2} GB-months past the free {}", crate::features::bytes(free));
685 self.set_pending(&slug, "storage", &month, storage_cost(gb_months, price), Some(&detail)).await?;
686 }
687 }
688 Ok(())
689 }
690
691 /// Once a day: what each workspace's packages hold, public and private,
692 /// from the packages service, and for the plan what this month's
693 /// storage past the free amounts comes to so far. A free workspace is
694 /// never charged: the packages service refuses its pushes past them.
695 pub(crate) async fn measure_packages(&self) -> Result<()> {
696 #[derive(Deserialize)]
697 struct Held {
698 workspace: String,
699 public_bytes: i64,
700 private_bytes: i64,
701 }
702 #[derive(Deserialize)]
703 struct Day {
704 public_bytes: i64,
705 private_bytes: i64,
706 public_free_bytes: i64,
707 private_free_bytes: i64,
708 }
709 let Some(packages) = &self.packages else { return Ok(()) };
710 let list: Vec<Held> = g1t_kit::call(packages, "storage_all", &serde_json::json!({})).await?;
711 let now = rfc3339(now_ms());
712 let (day, month) = (&now[..10], credits::month_of(&now));
713 let price = self.price("package_storage").await?.map_or(PACKAGE_MICROS_PER_GB_MONTH as f64, |(cost, _)| cost);
714 let (public_free, private_free) = (self.plans.public_package_free_bytes, self.plans.private_package_free_bytes);
715 for held in list {
716 let slug = held.workspace.to_lowercase();
717 self.db
718 .prepare(
719 "INSERT INTO package_storage_days (workspace, day, public_bytes, private_bytes, public_free_bytes, private_free_bytes)
720 VALUES (?1, ?2, ?3, ?4, ?5, ?6)
721 ON CONFLICT (workspace, day) DO UPDATE SET public_bytes = ?3, private_bytes = ?4, public_free_bytes = ?5, private_free_bytes = ?6",
722 )
723 .bind(&[
724 slug.as_str().into(),
725 day.into(),
726 (held.public_bytes as f64).into(),
727 (held.private_bytes as f64).into(),
728 (public_free as f64).into(),
729 (private_free as f64).into(),
730 ])?
731 .run()
732 .await?;
733 if !self.has_plan(&slug).await? {
734 continue;
735 }
736 let days = self
737 .db
738 .prepare(
739 "SELECT public_bytes, private_bytes, public_free_bytes, private_free_bytes FROM package_storage_days
740 WHERE workspace = ? AND substr(day, 1, 7) = ?",
741 )
742 .bind(&[slug.as_str().into(), month.as_str().into()])?
743 .all()
744 .await?
745 .results::<Day>()?;
746 let gb_months = package_gb_months(
747 &days.iter().map(|d| (d.public_bytes, d.private_bytes, d.public_free_bytes, d.private_free_bytes)).collect::<Vec<_>>(),
748 credits::days_in(&month),
749 );
750 if gb_months > 0.0 {
751 let detail = format!(
752 "{gb_months:.2} GB-months past the free {} public and {} private",
753 crate::features::bytes(public_free),
754 crate::features::bytes(private_free)
755 );
756 self.set_pending(&slug, "package_storage", &month, storage_cost(gb_months, price), Some(&detail)).await?;
757 }
758 }
759 Ok(())
760 }
761
762 /// Git operations this month for each workspace, from the repos
763 /// service: what is past the free amount goes to the month's pending
764 /// usage for workspaces on the plan, which are never slowed or refused
765 /// for them. Free workspaces are never charged for them.
766 pub(crate) async fn measure_git(&self) -> Result<()> {
767 let Some(repos) = &self.repos else { return Ok(()) };
768 let month = credits::month_of(&rfc3339(now_ms()));
769 let list: Vec<WorkspaceGitOperations> =
770 g1t_kit::call(repos, "git_operations", &GitOperationsArgs { since: Some(git_since(&month)), month: month.clone(), namespace: None }).await?;
771 let price = self.price("git_operations").await?.map_or(GIT_MICROS_PER_THOUSAND as f64, |(cost, _)| cost);
772 for workspace in list {
773 let slug = workspace.namespace.to_lowercase();
774 if self.plan_kind(&slug).await? == PlanKind::Free {
775 continue;
776 }
777 let cost = git_cost(workspace.operations, self.plans.git_included, price);
778 if cost > 0 {
779 let detail = format!(
780 "{} operations, {} of them free",
781 crate::features::thousands(workspace.operations),
782 crate::features::thousands(self.plans.git_included)
783 );
784 self.set_pending(&slug, "git", &month, cost, Some(&detail)).await?;
785 }
786 }
787 Ok(())
788 }
789
790 /// Once last month is over and its last hour has landed: its final
791 /// count of git operations, for workspaces that had the plan in it,
792 /// then the month marked measured (`measured_months`), which lets its
793 /// usage be charged and the month close. The daily measure ran hours
794 /// before the month ended; charging on it would leave out the
795 /// operations after it. Called every tick until it is done.
796 pub(crate) async fn measure_last_month_git(&self) -> Result<()> {
797 let Some(repos) = &self.repos else { return Ok(()) };
798 let now = now_ms();
799 let previous = crate::limits::previous_month(&credits::month_of(&rfc3339(now)));
800 if !final_measure_due(now, &previous) || self.measured("git", &previous).await? {
801 return Ok(());
802 }
803 let list: Vec<WorkspaceGitOperations> = g1t_kit::call(
804 repos,
805 "git_operations",
806 &GitOperationsArgs { since: Some(git_since(&previous)), month: previous.clone(), namespace: None },
807 )
808 .await?;
809 let price = self.price("git_operations").await?.map_or(GIT_MICROS_PER_THOUSAND as f64, |(cost, _)| cost);
810 for workspace in list {
811 let slug = workspace.namespace.to_lowercase();
812 // The plan as it was that month, as `charge_pending` charges it.
813 if !self.had_plan_in(&slug, &previous).await? {
814 continue;
815 }
816 let cost = git_cost(workspace.operations, self.plans.git_included, price);
817 if cost > 0 {
818 let detail = format!(
819 "{} operations, {} of them free",
820 crate::features::thousands(workspace.operations),
821 crate::features::thousands(self.plans.git_included)
822 );
823 self.set_pending(&slug, "git", &previous, cost, Some(&detail)).await?;
824 }
825 }
826 self.db
827 .prepare("INSERT OR IGNORE INTO measured_months (source, month, measured_at) VALUES ('git', ?1, ?2)")
828 .bind(&[previous.as_str().into(), rfc3339(now).into()])?
829 .run()
830 .await?;
831 Ok(())
832 }
833
834 /// Whether `source`'s final count of `month` was taken.
835 async fn measured(&self, source: &str, month: &str) -> Result<bool> {
836 #[derive(Deserialize)]
837 struct Row {
838 #[allow(dead_code)]
839 month: String,
840 }
841 Ok(self
842 .db
843 .prepare("SELECT month FROM measured_months WHERE source = ? AND month = ?")
844 .bind(&[source.into(), month.into()])?
845 .first::<Row>(None)
846 .await?
847 .is_some())
848 }
849
850 /// Whether last month's metered usage may be charged and the month
851 /// closed (`month_final`). Without a repos service there are no git
852 /// operations to wait for.
853 pub(crate) async fn last_month_final(&self) -> Result<bool> {
854 if self.repos.is_none() {
855 return Ok(true);
856 }
857 let now = now_ms();
858 let previous = crate::limits::previous_month(&credits::month_of(&rfc3339(now)));
859 let measured = self.measured("git", &previous).await?;
860 let ready = month_final(measured, now, &previous);
861 if ready && !measured {
862 worker::console_error!("closing {previous} without its final count of git operations: it was never taken");
863 }
864 Ok(ready)
865 }
866
867 /// The workspace's git operations this month, as last measured.
868 pub(crate) async fn git_operations_this_month(&self, workspace: &str) -> Result<u64> {
869 let Some(repos) = &self.repos else { return Ok(0) };
870 let month = credits::month_of(&rfc3339(now_ms()));
871 let list: Result<Vec<WorkspaceGitOperations>> =
872 g1t_kit::call(repos, "git_operations", &GitOperationsArgs { since: Some(format!("{month}-01T00")), month, namespace: Some(workspace.to_lowercase()) }).await;
873 Ok(list
874 .ok()
875 .and_then(|list| list.into_iter().find(|w| w.namespace.eq_ignore_ascii_case(workspace)))
876 .map_or(0, |w| w.operations))
877 }
878}
879
880#[cfg(test)]
881mod tests {
882 use super::*;
883
884 #[test]
885 fn scans_and_embeddings_on_a_free_workspace_are_covered_by_g1t() {
886 // No plan: g1t pays, and the trial is left alone.
887 let free = month_end_eligible("security", false);
888 assert!(free.cover_rest && !free.trial);
889 assert!(month_end_eligible("context", false).cover_rest);
890 // On the plan: charged like any usage, from its included usage first.
891 let plan = month_end_eligible("security", true);
892 assert!(!plan.cover_rest && plan.trial);
893 // Storage, git and the cache are never kept for a free workspace;
894 // were one at the close, it would not be covered either.
895 assert!(!month_end_eligible("storage", false).cover_rest);
896 assert!(!month_end_eligible("git", false).cover_rest);
897 // A covered source is still a source billing charges at the close.
898 assert!(COVERED_FOR_FREE.iter().all(|s| CHARGED_HERE.contains(s)));
899 }
900
901 #[test]
902 fn storage_past_the_free_amount_is_counted_by_the_day() {
903 // 3 GB private with 1 GB free, every day of a 30-day month: 2 GB-months.
904 let month = vec![(3_000_000_000, 1_000_000_000); 30];
905 assert!((storage_gb_months(&month, 30) - 2.0).abs() < 1e-9);
906 // Under the free amount: nothing.
907 assert_eq!(storage_gb_months(&[(500_000_000, 1_000_000_000); 30], 30), 0.0);
908 // The plan has the same 1 GB free: 11 GB on it all month is 10
909 // GB-months, $5.00 to g1t, $6.00 charged, from its included usage.
910 let days = vec![(11_000_000_000, 1_000_000_000); 30];
911 assert!((storage_gb_months(&days, 30) - 10.0).abs() < 1e-9);
912 assert_eq!(credits::with_margin(storage_cost(storage_gb_months(&days, 30), STORAGE_MICROS_PER_GB_MONTH as f64), 20), 6_000_000);
913 // Ten days of 4 GB past it: a third of 4 GB-months.
914 let days = vec![(5_000_000_000, 1_000_000_000); 10];
915 assert!((storage_gb_months(&days, 30) - 4.0 / 3.0).abs() < 1e-9);
916 }
917
918 #[test]
919 fn a_month_of_storage_is_its_own_days_not_thirty() {
920 // 2 GB past the free amount every day of a 31-day month is 2
921 // GB-months, not 2.07; every day of February, 2 too, not 1.87.
922 let october = vec![(3_000_000_000, 1_000_000_000); 31];
923 assert!((storage_gb_months(&october, credits::days_in("2026-10")) - 2.0).abs() < 1e-9);
924 let february = vec![(3_000_000_000, 1_000_000_000); 28];
925 assert!((storage_gb_months(&february, credits::days_in("2026-02")) - 2.0).abs() < 1e-9);
926 assert_eq!(credits::days_in("2028-02"), 29);
927 let gb = GB as i64;
928 let packages = vec![(11 * gb, 0, 10 * gb, 0); 31];
929 assert!((package_gb_months(&packages, 31) - 1.0).abs() < 1e-9);
930 }
931
932 #[test]
933 fn package_storage_counts_public_and_private_past_their_own_free_amounts() {
934 let gb = GB as i64;
935 // 12 GB public with 10 free, 0.5 GB private with 0.5 free: 2 GB past, for 15 days.
936 let days = vec![(12 * gb, gb / 2, 10 * gb, gb / 2); 15];
937 assert!((package_gb_months(&days, 30) - 1.0).abs() < 1e-9);
938 // Under both amounts: nothing.
939 assert_eq!(package_gb_months(&[(gb, gb / 4, 10 * gb, gb / 2)], 30), 0.0);
940 // At R2's rate, a GB-month is $0.015.
941 assert_eq!(storage_cost(1.0, PACKAGE_MICROS_PER_GB_MONTH as f64), 15_000);
942 }
943
944 #[test]
945 fn storage_is_priced_at_cloudflares_rate_plus_the_margin() {
946 // $0.50 a GB-month to g1t: 2 GB-months cost $1.00, charged $1.20.
947 let cost = storage_cost(2.0, 500_000.0);
948 assert_eq!(cost, 1_000_000);
949 assert_eq!(credits::with_margin(cost, 20), 1_200_000);
950 // A fraction of a millionth rounds up.
951 assert_eq!(storage_cost(0.000_000_001, 500_000.0), 1);
952 }
953
954 #[test]
955 fn embeddings_and_scans_are_charged_at_cost_plus_the_margin() {
956 // 10 million tokens at $0.067 a million: $0.67, charged $0.804.
957 assert_eq!(credits::with_margin(670_000, 20), 804_000);
958 assert_eq!(title("context"), "Search embeddings");
959 assert_eq!(title("security"), "Security scans");
960 assert!(CHARGED_HERE.contains(&"storage") && !CHARGED_HERE.contains(&"deployments"));
961 assert!(CHARGED_HERE.contains(&"git"));
962 // Package storage and observability data are charged when the month
963 // closes, like storage, and each is named on the statement.
964 for source in ["package_storage", "telemetry"] {
965 assert!(CHARGED_HERE.contains(&source), "{source}");
966 assert_ne!(title(source), "Metered usage", "{source}");
967 assert!(!crate::pricing::meters_of(source).is_empty(), "{source}");
968 assert!(crate::pricing::meters_of(source).iter().all(|m| crate::pricing::MONTHLY.contains(m)), "{source}");
969 assert!(!COVERED_FOR_FREE.contains(&source) && !PLAN_ONLY.contains(&source), "{source}");
970 }
971 assert_eq!(title("telemetry"), "Observability data past the free amount");
972 }
973
974 #[test]
975 fn git_operations_are_charged_past_the_free_amount_at_cloudflares_price() {
976 // $0.15 per 1,000 to g1t; 50,000 a month free for everyone.
977 let per_thousand = GIT_MICROS_PER_THOUSAND as f64;
978 let free = crate::credits::Config::default().git_included;
979 assert_eq!(free, 50_000);
980 assert_eq!(git_cost(49_000, free, per_thousand), 0);
981 assert_eq!(git_cost(50_000, free, per_thousand), 0);
982 // A workspace on the plan pushes on past it, and pays: 70,000
983 // operations are 20,000 past it, $3.00 to g1t, $3.60 charged.
984 assert_eq!(git_cost(70_000, free, per_thousand), 3_000_000);
985 assert_eq!(credits::with_margin(git_cost(70_000, free, per_thousand), 20), 3_600_000);
986 // A million in a month: no cap, $142.50 to g1t.
987 assert_eq!(git_cost(1_000_000, free, per_thousand), 142_500_000);
988 // One past it: a fraction of a cent, rounded up to a millionth.
989 assert_eq!(git_cost(50_001, free, per_thousand), 150);
990 assert_eq!(title("git"), "Git operations past the free amount");
991 }
992
993 #[test]
994 fn a_month_is_charged_and_closed_only_after_its_final_count_of_git_operations() {
995 let at = |text: &str| g1t_contracts::time::parse_rfc3339(text).unwrap();
996 // The quarter-hour just after October ends: the daily measure ran at
997 // 04:17 on the 31st, so its count leaves out nearly 20 hours of
998 // operations. Neither due to measure yet, nor final.
999 let midnight = at("2026-11-01T00:00:00Z");
1000 assert!(!final_measure_due(midnight, "2026-10"));
1001 assert!(!month_final(false, midnight, "2026-10"));
1002 // An hour on, the last hour has landed: the final count is taken,
1003 // and once it is, the month is charged and closed.
1004 let hour = at("2026-11-01T01:00:00Z");
1005 assert!(final_measure_due(hour, "2026-10"));
1006 assert!(!month_final(false, hour, "2026-10"));
1007 assert!(month_final(true, hour, "2026-10"));
1008 // A month not over is never final, measured or not.
1009 assert!(!final_measure_due(at("2026-10-31T23:59:59Z"), "2026-10"));
1010 assert!(!month_final(true, at("2026-10-31T23:59:59Z"), "2026-10"));
1011 // Three days without it (the repos service down), the close goes
1012 // ahead regardless.
1013 assert!(!month_final(false, at("2026-11-03T23:59:59Z"), "2026-10"));
1014 assert!(month_final(false, at("2026-11-04T00:00:00Z"), "2026-10"));
1015 // December's ends with the year.
1016 assert!(final_measure_due(at("2027-01-01T01:00:00Z"), "2026-12"));
1017 assert!(!final_measure_due(at("2027-01-01T00:59:59Z"), "2026-12"));
1018 }
1019
1020 #[test]
1021 fn git_operations_count_from_when_cloudflare_starts_charging() {
1022 assert_eq!(git_since("2026-10"), "2026-10-14T00");
1023 assert_eq!(git_since("2026-11"), "2026-11-01T00");
1024 assert_eq!(git_since("2026-09"), "2026-10-14T00");
1025 }
1026}