Stage 3 — Build
What runs on a pull request
Two commands, and they answer different questions. Keeping them separate keeps their failures legible — “we could not measure this” and “this costs too much” send you to different places.
| COMMAND | QUESTION | FAILS THE BUILD? |
|---|---|---|
| finowl breakdown | What will this product cost per month, resource by resource? | Only if it cannot produce a number — a missing bundle, an unresolved cluster reference, an unworded compute. |
| finowl guard | Does this change violate a rule that is in force here? | Yes. Exit 1 on a blocking violation, 2 when a check could not run. |
Both report to the platform under the product identity in finowl.yml, and they share one run identity — the CI pipeline id plus the commit SHA — so they merge into a single entry on the deployment timeline rather than appearing as two unrelated runs.
How the guard reaches a verdict
- Resolve the target.
--targetis checked against the bundle'stargets:block first, before the platform is called at all. Naming an environment that does not exist is answerable from the definition alone and should not cost a round trip to find out. - Authenticate.The credential resolves once — in CI, from the pipeline's own OIDC identity.
- Fetch the rules.The platform merges account and organization policies, filters them to this environment, and fills a budget rule with this product's own budget. The CLI holds no policy file.
- Check the workload gate. Every compute the definition declares for this target must have a word in
finowl.yml. Gaps are reported as one aggregated list — the complete to-do list, before any pricing call. - Diff.
databricks bundle planproduces the deployed and planned states. Both sides are priced with the same workload words, so the delta reflects the change and not a change of assumption. - Evaluate. Cost thresholds against the diff; governance rules against the target-resolved bundle. Every rule produces a verdict with the resources that caused it.
Cost is evaluated at P90
The guard evaluates the P90 total rather than the midpoint. A gate that fires only when the expected case breaches the budget is a gate that lets through every change whose expected case is exactly at the limit — which is most of them, since teams size to their budget.
What Build deliberately does not check
- Drift from the baseline estimate. Nothing fails a merge for costing more than the estimate said. The estimate is a projection made before the code existed; holding code to it would make the estimator a liability rather than a planning tool. The budget is what gets enforced.
- Products with no budget. A budget rule finds no ceiling and drops out. The total is still reported and the run still recorded.
- Rules in no policy. A rule that no policy carries never reaches the CLI. The rules page marks these Never runs.
- Unchanged bundles. When the plan shows no cluster changes, the cost check reports no movement rather than re-litigating a total nobody touched.
Running it locally
Engineers can run every command on their machine before opening a pull request. The CLI detects that it is not in CI and files the run as a local preview: attributed to the developer's git identity, marked -dirty when the tree has uncommitted changes, and shown in a separate table.
# before you push, see what it will say
finowl breakdown --target dev
finowl guard --target devA local run never overwrites deployment costs and never feeds the dashboards. That separation is what makes local runs safe to encourage — the fastest way to stop engineers checking cost early is to make their experiments move the number the business reads.
The inner loop
When the guard blocks, there are three legitimate answers and the team picks one:
| ANSWER | WHAT IT MEANS |
|---|---|
| Optimize | Same outcome, cheaper shape. Smaller cluster, spot instances, a schedule that does not run hourly for a daily report. |
| Descope | Less outcome for the agreed money. The product owner decides what comes out. |
| Re-commit | The work genuinely costs more than was agreed. Go back to the sponsor, re-estimate, and commit a new figure with the reason. |
Details of each command in the CLI reference, and of every rule type in Policies and rules.