with one click
fstarverifier
Verify F* and Pulse code with fstar.exe and interpret errors
Install with Codex or Claude Copy this prompt, paste it into Codex, Claude, or another assistant, and let it review the skill page and install it for you.
Menu
Verify F* and Pulse code with fstar.exe and interpret errors
Install with Codex or Claude Copy this prompt, paste it into Codex, Claude, or another assistant, and let it review the skill page and install it for you.
Based on SOC occupation classification
Use the F* MCP server for interactive, incremental typechecking of F* and Pulse code
Extract verified F*/Pulse code to C via KaRaMeL (.krml intermediate representation)
Structure a new F*/Pulse verification project with Makefile and directory layout
Systematic workflows for debugging F*/Pulse verification failures
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 | fstarverifier |
| description | Verify F* and Pulse code with fstar.exe and interpret errors |
This skill is used when:
#lang-pulse) files# Verify a single file (stage3 fstar.exe includes Pulse support)
fstar.exe Module.fst
# With project include paths and caching
fstar.exe --cache_checked_modules --cache_dir _cache \
--already_cached Prims,FStar,Pulse.Nolib,Pulse.Lib,Pulse.Class,PulseCore \
--include path/to/spec --include path/to/impl \
Module.fst
# Verify interface first, then implementation (always in this order)
fstar.exe Module.fsti
fstar.exe Module.fst
| Flag | Purpose |
|---|---|
--query_stats | Show per-query timing and success/failure |
--split_queries always | Send each assertion as a separate Z3 query |
--log_queries | Write .smt2 files for Z3 query inspection |
--z3refresh | Restart Z3 between queries (detect flaky proofs) |
--print_full_names | Show fully qualified names (catch symbol confusion) |
--print_implicits | Show implicit arguments (debug unification) |
--detail_errors | More precise error locations, but can take much longer |
# Combined debugging
fstar.exe --query_stats --split_queries always --z3refresh Module.fst
#push-options "--z3rlimit 10" // SMT timeout (target ≤ 10)
#push-options "--fuel 1 --ifuel 1" // Recursion unfolding depth
#push-options "--z3rlimit 10 --fuel 0 --ifuel 0" // Tight: no unfolding
Cause: SMT cannot establish the postcondition from available facts.
Solutions:
assert statements to locate the gapSeq.equal / Set.equal for collection equality (not ==)FS.all_finite_set_facts_lemma() before FiniteSet reasoning--print_full_names)unfold needs a matching foldCause: Symbol not in scope.
Solutions:
open declarations and module X = ... aliases--print_full_names on a working referenceCause: Proof too complex for SMT within the time limit.
Solutions:
--fuel 0 --ifuel 0{:pattern ...} on quantifiers for controlled instantiation[@@"opaque_to_smt"] and reveal_opaque manuallyDo not just increase rlimit — find the root cause instead.
Cause: Type mismatch, often involving refinements.
Solutions:
(x <: refined_type)Cause: Cannot prove a refinement type's predicate.
Solutions:
assert establishing the predicate just before the expressionCause: Match expression doesn't cover all cases.
Solutions:
| _ -> ...--warn_error -321 only if completeness is verifiedCause: Calling a stateful (stt) function inside a ghost context.
How this happens:
with x y. _ are ghostif condition depends on ghost values, both branches become ghostSolutions:
// WRONG: ghost_seq is ghost from 'with'
let val = Seq.index ghost_seq idx;
let data = !some_ref; // Error: ghost context
// RIGHT: Read from the actual array
let val = arr.(idx); // Concrete
Cause: Trying to bind a concrete type from a ghost expression.
Solutions:
let x : erased (list entry) = ...assert (pure (Cons? ghost_list))Cause: Predicate arguments don't match the definition.
Solutions:
Cause: A pure (...) assertion in the slprop cannot be established.
Solutions:
assert (pure (...)) stepsEvery predicate manipulation must be balanced:
unfold (is_valid table spec);
// ... work with exposed resources ...
fold (is_valid table spec);
For range predicates, use get/put helpers:
get_at ptrs contents lo hi idx; // Extract element from range
// ... work with element ...
put_at ptrs contents lo hi idx; // Restore range
drop_ non-empty resources — this is a memory leakdrop_ (LL.is_list null_ptr []); // OK: empty list is null
// drop_ (LL.is_list ptr (hd::tl)); // WRONG: memory leak!
B.free: let b = B.alloc v; ... B.free barr |-> contents // Full permission: read and write
A.pts_to arr #p contents // Fractional: read-only (p is a fraction)
// MUST call before FiniteSet assertions
FS.all_finite_set_facts_lemma();
assert (FS.cardinality (FS.remove x s) == FS.cardinality s - 1);
assert (Seq.equal s1 s2); // NOT: s1 == s2
assert (Set.equal set1 set2);
assert (pure (SZ.v idx < len));
assert (pure (len <= SZ.v capacity));
assert (pure (SZ.fits (SZ.v capacity)));
assert (pure (SZ.fits (SZ.v idx + 1)));
let next = idx `SZ.add` 1sz;
my_arithmetic_lemma arg1 arg2; // Ghost: costs nothing at runtime
assert (pure (conclusion_of_lemma));
while (!i <^ len)
invariant exists* vi v_acc.
R.pts_to i vi **
R.pts_to acc v_acc **
A.pts_to arr #p s **
pure (SZ.v vi <= Seq.length s /\ v_acc == partial_result s (SZ.v vi))
{
// loop body
}
Do NOT use invariant b. exists* ... style.
.fsti interface first with full pre/post conditions.fsti.fst with admit() placeholders to validate structure--query_stats to find slow/cancelled queries--split_queries always to isolate which assertion failsassert statements to binary-search the failure point--z3refresh to detect order-dependent proofsadmit() or assume_ callsdrop_ of non-empty resources (Pulse)--query_stats shows no cancelled queriesFSTAR_HOME/ulib/ — F* standard library sourcesFSTAR_HOME/pulse/test/ — Pulse test cases and examplesFSTAR_HOME/pulse/lib/pulse/lib/ — Pulse library sourcesproofdebugging skill for systematic debugging workflows