Updated in v5.3.0 (breaking). Every
SymbolicVerifier public method now returns a DiagnosticResult instead of an ad-hoc dict. See the migration section for a field-by-field mapping. The Code Engine is the first fully DiagnosticResult-conformant engine and serves as the reference implementation for future engine migrations.Capabilities
Quick start
SymbolicVerifier returns a DiagnosticResult with the same three layers described in Verification Diagnostics: a Layer 1 agent_message, a Layer 2 developer_fields dict, and a Layer 3 proof_ref (always None for this engine — see Why VERIFIED is never emitted).
The DiagnosticResult contract
All six public methods — verify_code, verify_function_contract, verify_safety_properties, verify_bounded, analyze_complexity, get_verification_budget — return a DiagnosticResult.
Status values
Read the specific reason from
result.developer_fields["constraint_id"].
Common developer_fields keys
Every result carries verification_mode so callers can tell a plain symbolic run from a bounded run:
Method-specific keys (function counts, loop lists, budget estimates) are documented in the sections below.
Why VERIFIED is never emitted
CrossHair’s search is timeout-bounded, not a completeness proof. “No counterexample found” is not the same as “no counterexample exists,” so a clean run maps to UNVERIFIABLE with constraint_id = "symbolic_verifier.no_counterexample_found" — never to VERIFIED. The DiagnosticResult contract structurally requires a proof_ref for VERIFIED, and this engine has no proof artifact to bind.
Downstream policy gates must reject UNVERIFIABLE results for control flow. Treat symbolic-execution output as a bug hunter, not an authority.
Symbolic verification
verify_code() walks the AST, extracts every function with type annotations, and runs CrossHair on each one.
developer_fields on verify_code() results:
Counterexample: division by zero
Untyped function: fails closed
CrossHair requires type hints. Untyped functions are reported as skipped and unverifiable — the result cannot beis_verified: True.
Contract verification
verify_function_contract() injects preconditions as assert statements and re-runs verify_code() against the decorated source.
DiagnosticResult follows the same shape as verify_code(). Read result.developer_fields["issues"] to find failing assertions.
Safety property analysis
verify_safety_properties() scans the AST for division-by-zero and index-out-of-bounds hazards. It always returns UNVERIFIABLE (the check is advisory and does not prove the code is safe) with structured details in developer_fields.
advisory_checks array carries a safety_properties entry with the same summary counts.
Bounded model checking
verify_bounded() transforms the source to inject loop counters and recursion-depth checks, then calls verify_code() on the rewritten source. Every result from this method sets verification_mode = "bounded_symbolic" and includes the applied bounds plus the underlying complexity analysis.
verify_bounded() returns BLOCKED with constraint_id = "symbolic_verifier.bounds_transform_error" instead of silently falling back to the untransformed source.
See Bounded model checking for configuration guidance and preset budgets.
Complexity analysis
analyze_complexity() counts loops, direct and mutual recursions, and maximum loop-nesting depth. The result is always UNVERIFIABLE — the analysis is advisory and produces no proof.
recommendation to pick sensible bounds for a follow-up verify_bounded() call.
Verification budget
get_verification_budget() estimates the number of symbolic paths CrossHair would explore, so callers can decide whether to attempt verification or fall back to a cheaper check.
analyze_complexity() are advisory — treat their output as heuristics, not proof. The result carries an advisory_checks entry with the same summary details.
Migrating from the legacy dict API
Before v5.3.0,SymbolicVerifier methods returned ad-hoc dicts (result.verified, result["issues"], result["complexity"], and so on). v5.3.0 removes those return types entirely — every method now returns a DiagnosticResult. Update your call sites using the map below.
Field cheat sheet
Before
After
Removed parameters
verify_code(code, check_assertions=...)— the parameter was never used. Remove it from your call sites; passing it now raisesTypeError.
Language support
Next steps
- Verification Diagnostics — the
DiagnosticResultcontract in depth - Symbolic execution limits — when CrossHair works, when it doesn’t, and how to bound it
- SQL engine — verify SQL queries
- Logic engine — verify logical constraints