com um clique
proofdebugging
Systematic workflows for debugging F*/Pulse verification failures
Instalar com Codex ou Claude Copie este prompt, cole no Codex, Claude ou outro assistente e deixe que ele revise a página da skill e instale para você.
Menu
Systematic workflows for debugging F*/Pulse verification failures
Instalar com Codex ou Claude Copie este prompt, cole no Codex, Claude ou outro assistente e deixe que ele revise a página da skill e instale para você.
Baseado na classificação ocupacional SOC
Use the F* MCP server for interactive, incremental typechecking of F* and Pulse code
Verify F* and Pulse code with fstar.exe and interpret errors
Extract verified F*/Pulse code to C via KaRaMeL (.krml intermediate representation)
Structure a new F*/Pulse verification project with Makefile and directory layout
Debug F* queries sent to Z3, diagnosing proof instability and performance issues
Build F*, Pulse, and KaRaMeL from source (fstar2 branch) for use in a verification project
| name | proofdebugging |
| description | Systematic workflows for debugging F*/Pulse verification failures |
This skill is used when:
fstar.exe --query_stats --split_queries always Module.fst 2>&1 | grep -E 'cancelled|failed|succeeded'
--query_stats shows time and result per query--split_queries always separates each assertion into its own querycancelled (timeout) or failed queriesUse admit() as a binary search tool:
let my_proof () : Lemma (ensures conclusion) =
step1;
assert (fact1); // Does this pass?
admit(); // Cut here — if it passes, failure is below
step2;
assert (fact2); // Move admit() down to find exact failure
step3
Move the admit() down until the proof fails again. The assertion just before
the admit() position is where Z3 gets stuck.
Extract the failing assertion into a standalone lemma:
// Separate lemma — easier to debug in isolation
let helper_lemma (x: t)
: Lemma (requires precondition x) (ensures failing_fact x)
= // prove it here, with full focus
let my_proof () : Lemma (ensures conclusion) =
step1;
helper_lemma arg; // Call the helper
step2;
step3
Small lemmas are easier for Z3, easier to understand, and more reusable.
Once it passes, reduce rlimits:
#push-options "--z3rlimit 10 --fuel 0 --ifuel 0"
let my_proof () : Lemma (...) = ...
#pop-options
If it fails at low rlimit, add more intermediate assertions rather than increasing the limit.
Symptom: Assertion about sequences, sets, or arithmetic fails.
Fix: Call the appropriate lemma:
// FiniteSet facts
FS.all_finite_set_facts_lemma();
// Sequence properties
Seq.lemma_eq_intro s1 s2;
// Arithmetic
FStar.Math.Lemmas.pow2_plus a b;
Symptom: s1 == s2 fails but the sequences are clearly equal.
Fix: Use extensional equality:
assert (Seq.equal s1 s2); // Compares element-by-element
// NOT: assert (s1 == s2); // Requires decidable equality proof
Same for sets: use Set.equal, not ==.
Symptom: A forall property is known but Z3 can't use it.
Fixes:
forall (x:t). {:pattern (f x)} P x
my_forall_lemma specific_value;
assert (P specific_value);
[@@"opaque_to_smt"]
let complex_def = ...
let use_it (x:t) : Lemma (P x) =
reveal_opaque (`%complex_def) complex_def
Symptom: Z3 knows x < 100 but can't prove x < 200.
Fix: Add explicit assertion chains:
assert (x < 100); // Known
assert (100 <= 200); // Trivial
assert (x < 200); // Now provable
Symptom: Proofs that worked in small files break in large ones.
Fixes:
.fst file#push-options / #pop-options to scope rlimit changes[@@"opaque_to_smt"] when not needed by nearby proofsassert_spinoff (P) creates a separate Z3 query for P, preventing it from
bloating the main proof context. Use when:
let complex_proof () : Lemma (...) =
step1;
assert_spinoff (intermediate_fact1); // Proven in isolation
assert_spinoff (intermediate_fact2); // Proven in isolation
// Main proof continues with both facts available but Z3 didn't have
// to carry the burden of proving them while also proving the rest
final_step
When you have large, repeated predicate expressions copied across multiple functions or assertions:
let my_pred x y = ...// BAD: large inline predicate repeated in 5 places
ensures (forall i. 0 <= i /\ i < length arr ==> index arr i >= 0 /\ index arr i < bound /\ ...)
// GOOD: named predicate with a lemma
let all_in_bounds (arr: seq int) (bound: int) = forall i. 0 <= i /\ i < length arr ==> ...
val all_in_bounds_preserved : arr:_ -> bound:_ -> i:_ -> Lemma (...)
Symptom: Proof fails inexplicably; the code "looks right."
Fix: Use --print_full_names --print_implicits:
fstar.exe --print_full_names --print_implicits Module.fst
Check that each symbol resolves to the intended module. A function copied from another module may reference the wrong qualified name.
Symptom: "Could not prove post-condition" with separation logic.
Debugging approach:
assert for each slprop component to find which one is missingrewrite targets are correctSymptom: "Application of stateful computation cannot have ghost effect."
Debugging approach:
with ... . _ scopeif condition depends on ghost valuesSymptom: A lemma works in pure F* but fails when called from Pulse.
Debugging approach:
assert (pure (precondition)) before the call--print_full_names to verify the lemma resolves to the right definitionU64.v x = U64.v y vs x == y)❌ #push-options "--z3rlimit 300" — masks the problem, creates flaky proof
✅ Factor into smaller lemmas with --z3rlimit 10
❌ "Pulse can't handle this pattern" (without evidence)
✅ Produce a minimal reproducer; check for mundane bugs first
❌ "It passes sometimes, so it's fine"
✅ Run with --z3refresh and reduce rlimit until it's robust
❌ admit() left in "for now"
✅ Extract the exact property into a named lemma and prove it
admit() placeholders to validate the approach--z3refresh, clean up