Reviews
Cost deltas
Read this first: the estimate is order-of-magnitude and is never a billing source of truth. It exists to tell you a change adds roughly $40 a month rather than roughly $4,000, and to compare a change against a threshold you set. If you need a number you can put in a budget spreadsheet, get it from your provider.
With that stated, here is what it does.
Where the number comes from
Costs are computed over parser facts — the literal values in your code. Five resource types are priced, each from its shape attribute: aws_db_instance and aws_rds_cluster_instance (instance_class), aws_elasticache_replication_group and aws_elasticache_cluster (node_type), and aws_instance (instance_type). Module calls are priced the same way when they pass a literal shape input — the terraform-aws-modules pattern.
Every other resource type contributes nothing and is not counted as “unpriced” either: an S3 bucket or a security group is simply outside the model. This is a compute-shape estimator, not a bill.
Two price sources, and each line says which one answered
The built-in table is the default: a small static map of on-demand monthly prices (roughly hourly × 730). It is rough in three specific ways worth knowing before you trust a number:
- The prices are us-east-1. They are not adjusted for your region.
- It is blind to the database engine. Oracle and SQL Server cost multiples of PostgreSQL on identical hardware, and the table cannot express that.
- Multi-AZ is approximated by doubling the single-AZ price.
Live AWS prices replace that lookup with AWS’s own published price list, read per region, engine and Multi-AZ, so those three roughnesses become real prices. A server operator turns them on with GARBOARD_PRICING=aws. Details worth knowing:
- Coverage is RDS and ElastiCache. EC2 keeps the built-in table — AWS’s EC2 price list is 413 MB per region, which is not something to hold in a review path. An estimate can therefore be a mixture, and each priced line records which source answered it.
- An engine the price list does not recognise — including a variable-driven one — is priced at the cheapest engine offered for that shape: a deliberate lower bound, never a guess upward.
- The region is read only from a literal
regionin the provider block. A variable-driven region is no region, not us-east-1, and the live source declines to answer — the table answers instead. - Live pricing never delays or blocks a review. A price list that is not cached yet answers “unknown” immediately, the table fills in, and the list warms in the background. Both sides of a delta always go through the same source, so a delta is never manufactured out of the difference between two price lists.
- No AWS credentials are involved either way; the price list is public JSON. It is off by default, deliberately: a self-hosted install in a locked-down network should not start making outbound calls because it upgraded.
The delta
The number on a pull request is head minus base, computed once per review over the changed files, with the base versions fetched at the PR’s base commit. Two consequences that are easy to miss:
- No base means no number. A review that cannot fetch the base renders nothing — never
$0. Anywhere you see no cost, read it as “unmeasured”, not “free”. The fleet page follows the same rule. - An upsize to an unpriceable shape is never a saving. A resource that was priced at base but cannot be priced at head keeps its old price in the head total and is named as unpriced. Without that rule, upsizing a database to a shape the table does not know would render as money saved.
A rename reads as one resource removed and one added at equal price — net zero — which matches how the destructive-change rules see the same event: a destroy and a create.
The same rendered sentence — estimated monthly cost +$N (from $B to $H) — appears verbatim in the PR comment, in the cost gate chip, and on the fleet Cost page, because there is exactly one computation per review and one renderer. The surfaces cannot disagree.
What is unpriced, and why that is not zero
A resource lands in the unpriced list — named, never guessed at — in exactly three cases: its shape attribute is variable-driven, its shape is unknown to both price sources, or a module’s shape input is present but not a literal. Any unpriced resource marks the whole estimate partial, and the sentence says so: “at least — some resources could not be priced, so this is a lower bound.”
The distinction matters more than the arithmetic. “This change costs nothing” and “we could not price this change” are completely different sentences, and collapsing them into $0 is the kind of quiet wrongness that makes people stop believing a number.
When it comments
A priced monthly increase at or above cost.comment_above_usd comments on its own, even when the pull request is otherwise clean. The default is 0, meaning every priced increase says so. Savings and unchanged costs never trigger a standalone comment.
It renders as a context blockquote: no severity, never one of the three finding slots. It is telling you something, not judging you. See when Deltz says nothing.
Budgets, which do block
Separately from the comment floor, an organisation can set budget.threshold_prod_usd and budget.threshold_nonprod_usd. Both default to 0, which means the blocking gate is off out of the box — a threshold is a policy decision, and it is yours to make, not ours.
When a priced increase exceeds the threshold, the review carries a single critical budget.threshold finding with the file:line evidence of what got dearer, and the commit check fails. A change that touches both production and non-production paths is held to the production threshold; “production” is recognised from the resource’s file path.
The asymmetry is deliberate and worth stating plainly:
- A partial estimate can trip the gate — if the part that was priced is over, the change is over.
- A partial estimate can never clear the gate, and a fully-unpriced change never blocks. “The gate passed” means “not over the threshold or not measurable”, never “we checked and it is cheap”.
Going over budget is a decision, not a dead end. An admin or owner approves the spend and the approval is bound to the amount shown — if a later push makes the change dearer, it re-blocks. An approved gate renders as approved, a deliberately distinct state from passed: the fleet remembers that someone said yes to this money. The break-glass override also exists for the case where it must merge now, and records who decided that.
The fleet view
The Cost screen totals the month across every reviewed change: net monthly change, increases, savings, and the per-review rows behind them. Reviews that carried no estimate are excluded from every figure and the page says exactly how many — “N reviews carried no estimate and are not counted.” They are not counted as $0, for the same reason nothing else here is.
What the offline gate does not do
garboard gate prints no cost figure at all — not from HCL, and not from a plan file either; the artifact lane emits change-shape findings, not prices. Cost exists where a base exists: on server-side pull-request reviews. Forge drafts are priced for context using the built-in table.
Where the thresholds live
cost.comment_above_usd, budget.threshold_prod_usd and budget.threshold_nonprod_usd are organisation policy settings: database-stored, admin-only, and every change is audited with the old and new value.