Skip to content

The CEGIS loop

CEGIS — counterexample-guided inductive synthesis — is the workflow Vow is designed around:

Write a contract → verify → if it fails, read the counterexamplefix the code (or the contract) → verify again, until Verified.

Because the compiler emits a concrete counterexample (not just "failed"), an agent can act on it mechanically. Let's watch it happen.

Write a function with a bug

Here is max, with a tight contract — and a bug in the body:

module Max

fn max(a: i64, b: i64) -> i64 vow {
    ensures: result >= a,
    ensures: result >= b,
    ensures: result == a || result == b
} {
    a   // BUG: ignores b
}

fn main() -> i32 [io] {
    print_i64(max(3, 7));
    0
}

Verify

$ ulimit -v 2000000; build/vowc verify max.vow
{"status":"VerifyFailed","function":"max",
 "counterexamples":[
   {"function":"max","vow_id":1,"blame":"Callee",
    "values":{"a":0,"b":1,"result":0}}
 ]}

The verifier found inputs that break the promise: with a = 0, b = 1, the body returns 0, which violates ensures: result >= b. Blame is Callee — the function failed to deliver its postcondition. The values object is the witness.

This is the signal an agent fixes from

The counterexample names the failing function, the broken clause (via vow_id, resolved through the diagnostics), and concrete values. See the CLI reference for the exact JSON schema.

Fix and re-verify

Read the witness, fix the body:

fn max(a: i64, b: i64) -> i64 vow {
    ensures: result >= a,
    ensures: result >= b,
    ensures: result == a || result == b
} {
    if a >= b { a } else { b }
}
$ ulimit -v 2000000; build/vowc verify max.vow
{"status":"Verified","executable":null,"diagnostics":[],"counterexamples":[]}

Verified. The loop is closed.

Why tight contracts matter

If the postcondition had only been result >= a && result >= b, the buggy return a would still have failed (good) — but a different bug, return a + b + 100, would have passed, because it satisfies >= on both. The clause result == a || result == b is what forces the answer to actually be one of the inputs. Weak contracts admit wrong code; see Contract methodology for how to make them tight.

Next: proving properties of code that loops.