TL;DR: CrossHair symbolic execution has real-world limitations. QWED addresses these with bounded model checking, depth limits, and graceful fallbacks. Every result is a DiagnosticResult — see the Code engine reference for the full field map.
The path explosion problem
Symbolic execution explores all possible execution paths. This works great for small code, but real-world code has:When CrossHair works
When CrossHair fails
QWED’s bounded model checking solution
We implemented depth limits to prevent path explosion:Fail-closed verification results
Updated in v5.3.0.
verify_code() and every other SymbolicVerifier public method now return a DiagnosticResult. The legacy dict shape (result["status"], result["is_verified"], result["functions_checked"]) is gone. See the Code engine migration guide for the field-by-field mapping.verify_code() is fail-closed: absence of proof is never reported as success. In fact, this engine never emits VERIFIED — CrossHair’s search is timeout-bounded, not a completeness proof, so a clean run maps to UNVERIFIABLE with constraint_id = "symbolic_verifier.no_counterexample_found". Treat symbolic-engine output as a bug hunter, not an authority.
Result fields
DiagnosticStatus
DiagnosticStatus.UNVERIFIABLE for any incomplete or counterexample-producing run, or DiagnosticStatus.BLOCKED when verification could not be attempted at all (parse error, no functions, CrossHair missing). Read developer_fields["constraint_id"] for the specific reason.boolean
Convenience alias for
status is DiagnosticStatus.VERIFIED. Always False for this engine.string
Layer 1 agent-safe summary of the outcome. Safe to surface to models — no rule IDs, detection logic, or internals.
string | None
Layer 3 proof reference. Always
None for the Code engine.string
Structured reason code. One of:
symbolic_verifier.no_counterexample_found— clean CrossHair run, stillUNVERIFIABLEbecause completeness was not proven.symbolic_verifier.counterexample_found— CrossHair disproved a check.symbolic_verifier.timeout— verification did not converge withintimeout_seconds.symbolic_verifier.incomplete_coverage— at least one function could not be checked.symbolic_verifier.no_typed_functions— no functions carried the type hints CrossHair requires.symbolic_verifier.no_verifiable_functions— the source contained no functions.symbolic_verifier.syntax_error— the code could not be parsed.symbolic_verifier.crosshair_not_available— the CrossHair engine is not installed.symbolic_verifier.verification_error— verification did not complete cleanly.
string
"symbolic" for standard runs, "bounded_symbolic" for verify_bounded() runs.integer
Total functions found in the submitted code.
integer
Functions that were actually run through symbolic execution. A skipped function does not count as checked.
integer
Functions that were proven by CrossHair without counterexamples.
integer
Functions skipped because they cannot be analyzed (for example, no type annotations).
integer
Functions that could not be proven, including skipped functions and functions whose verification raised an error.
integer
Number of concrete counterexamples produced by CrossHair.
integer
Number of per-function timeout issues reported.
array
Per-function issue records with
type (unverifiable, counterexample, timeout, or error), function name, and description.Why untyped code fails closed
CrossHair requires type annotations to perform symbolic execution. Functions without type hints are reported asskipped and unverifiable, and the overall result fails closed:
is_verified = False and produce constraint_id = "symbolic_verifier.incomplete_coverage". Code that contains no functions at all returns BLOCKED with constraint_id = "symbolic_verifier.no_verifiable_functions".
Pre-verification exits (crosshair_not_available, syntax_error) return the same fail-closed shape — is_verified is False and function counters are absent from developer_fields — so callers can rely on constraint_id and agent_message regardless of how verification ended.
Configuration guide
Conservative (fast, less coverage)
Balanced (default)
Thorough (slow, more coverage)
Fallback strategy
When symbolic execution fails, QWED falls back to:Honest benchmarks
We tested CrossHair on different code types:Best practices
Do use symbolic execution for
- ✅ LLM-generated utility functions
- ✅ Mathematical calculations
- ✅ Validation logic
- ✅ Simple transformations
- ✅ Code with clear contracts
Don’t use symbolic execution for
- ❌ Entire applications
- ❌ Code with external dependencies
- ❌ Deep recursion algorithms
- ❌ Real-time systems
- ❌ Code with I/O operations
API reference
Bounded model checking
verify_bounded() applies loop and recursion bounds to the code before verification, then delegates to verify_code(). Every result sets developer_fields["verification_mode"] = "bounded_symbolic" and includes the applied bounds and the underlying complexity analysis. If the bounds transform itself fails (for example, the AST cannot be unparsed after transformation), the method returns BLOCKED with constraint_id = "symbolic_verifier.bounds_transform_error" instead of silently falling back to the original code:
Fail-closed result semantics
SymbolicVerifier.verify_code() treats absence of proof as failure. The engine never emits VERIFIED — a clean CrossHair run is UNVERIFIABLE with constraint_id = "symbolic_verifier.no_counterexample_found". Code that contains no functions, contains untyped functions, or mixes typed and untyped functions cannot ever appear as verified.
Result reason table
Typed function — no counterexample found
Untyped function — fails closed
CrossHair requires type hints. Untyped functions are reported as skipped and unverifiable rather than silently passing:Mixed typed and untyped — fails closed
Even if some functions verify, a single skipped function prevents an overall pass:Code with no functions — fails closed
Source that contains only top-level statements has nothing to prove and is reported asBLOCKED:
Summary
“My guess would be that technique falls apart at depths required in real world coding environments.” — Reddit criticism. We agree. That’s why we have bounds and fallbacks.