Skip to content
Certus

Policy

Your repository decides what “good enough to merge” means, in a file you review like code. Certus never picks the bar for you.

The file

Policy lives at .certus/policy.yaml in your repository, committed and reviewed like anything else. There is no dashboard setting that silently overrides it.

A missing file, an empty file and a file that parses to nothing all mean the same thing: the conservative defaults below. Unknown keys are rejected rather than ignored, at every level, so a typo is an error you see instead of a rule that quietly does nothing.

There are five blocks.

merge — what gates a pull request

minimum_grade: B
The floor on the verdict's grade. A run graded below it does not pass the gate.
max_open_debt_on_completion: 0
Checked only on a pull request that completes a feature. How many obligations may still be open when the feature is declared done.
require_negative_control: true
The behavior must be shown absent on BASE. This is a floor: setting it to false does not lower it, because policy may tighten the engine's rules and never relax them.
allow_model_only_claims: false
Whether a claim resting on a model judgement alone may pass. Off by default.
max_debt_age_days: null
No age limit on a carried obligation.
max_open_debt_per_feature: null
No standing ceiling on open obligations per feature.
require_reason_length: null
No minimum length on a waiver's stated reason.

externals, budget, frontiers and fleet

externals.default: recorded
How a third-party dependency is answered when nothing else says. The ladder runs stub, local, recorded, provider sandbox, live — and how a dependency is answered caps the grade. Per-dependency assignment lives in .certus/env.yaml; this sets the default.
externals.allow_live: []
Which dependencies may be called for real. Writes are never made live.
budget.per_pr_usd: 10
Compute and model spend for one pull request.
budget.per_claim_minutes: 10
Wall clock per claim, across both twins. Integers, because a policy file is not a place for floating point.
frontiers.prefer: [user_ui, http]
Which boundaries to reach for first.
frontiers.never: [stub_only]
Proofs reaching only these do not count.
fleet.enabled: true
Whether the synthetic-user fleet runs. Off keeps http proofs and skips the browser fleet. The planner cannot raise this.
fleet.max_actors: 3
How many distinct synthetic users may be live on a run, one session each per side. Ceiling 8. Policy and a run may only lower it.

Frontier policy narrows and never widens. If nothing survives the narrowing, no frontier is admissible and the claim cannot be planned — which is the commonest self-inflicted cause of a run that proves less than you expected.

How a policy failure reads

Policy is evaluated only when a run reached VERIFIED or VERIFIED_WITH_DEBT. The other three statuses never pass the gate and never produce policy reasons — there is nothing to gate on.

When the gate does not pass, the verdict carries a Gate: block listing every reason, each naming what it measured. A grade failure names both the grade and the oracle that capped it, so you know what to strengthen rather than only that you fell short.

The gate block is suppressed on BLOCKED, because nothing was proved and a list of policy complaints would imply otherwise.

When it says something unexpected

Your file was rejected
An unknown key or a bad value. The error names the path in the document. Unknown keys are refused deliberately, so a misspelled rule never silently does nothing.
require_negative_control: false did not take effect
Correct. It is an engine floor: policy can tighten Certus's rules, never relax them.
The gate fails on grade
The reason names the capping oracle. Usually the fix is a dependency moved off a stub or a recording, not a change to the minimum.
No frontier was admissible
Check the never list. Policy only narrows, so an entry that is too broad removes every candidate.
A run costs more than you budgeted
It does not: a plan whose estimated cost exceeds the claim budget is rejected before it runs, rather than killed halfway through.
You want to loosen the bar
Do it in the file, in a diff people can see. That is the point of policy as code.