Neural Theorem Proving for Verification Conditions
This skill enables Claude to generate formal proofs for verification conditions (VCs) arising from program verification. Following the NTP4VC methodology (ICLR 2026), it applies neural theorem proving to the hardest bottleneck in software verification: proving the logical obligations that automated theorem provers (ATPs) like Sledgehammer or CoqHammer cannot discharge. Claude translates VCs from Why3/Frama-C pipelines into Isabelle, Lean 4, or Rocq tactic proofs, using structured proof decomposition rather than whole-proof generation.
When to Use
- When a user has a verification condition from Frama-C, Why3, or another deductive verification tool and needs a proof in Isabelle, Lean 4, or Rocq
- When a user needs to prove a loop invariant, precondition, postcondition, or memory safety obligation for annotated C code
- When Sledgehammer, CoqHammer, or
auto-level tactics fail on a VC and the user needs a manual tactic proof
- When a user wants to translate a Why3-generated VC into a different interactive theorem prover (ITP) language
- When a user is building a verification pipeline and needs help structuring VC proof obligations from ACSL-annotated C or WhyML specifications
- When a user asks to formally verify properties of data structures, sorting algorithms, or kernel-level code (e.g., linked lists, binary search, memory allocators)
Key Technique
Verification Conditions are logical formulas generated by tools like Why3 from annotated source code. When you write a C function with ACSL annotations (preconditions via requires, postconditions via ensures, loop invariants via loop invariant), Frama-C and Why3 decompose correctness into individual proof obligations. Each VC encodes one specific claim: "if the precondition holds and the loop invariant held before this iteration, then the invariant holds after the iteration" or "array access index is within bounds." ATPs handle many VCs automatically, but real-world projects (Linux kernel, Contiki-OS) produce VCs that ATPs cannot solve -- these are the targets.
The NTP4VC pipeline works as follows: Why3's VC generator produces an XML AST representation of each obligation. A translation layer (approximately 2,400 expert-written rewriting rules) maps these ASTs into Isabelle, Lean 4, or Rocq syntax. This handles prefix/infix conversions, if-then-else desugaring, match-case translation, and semantic rewrites (e.g., integer operations to natural number operations preferred by ITPs). The resulting ITP file contains the VC as a lemma or theorem statement with all necessary type definitions and previously-proved lemmas in scope.
Critical finding from NTP4VC: Tactic-based step-by-step proofs dramatically outperform whole-proof-term generation. The most effective approach is to decompose VCs into subgoals using structural tactics (intro, cases, simp, omega) before applying domain-specific automation. Error analysis shows three dominant failure modes: syntactic errors (24%+ of Isabelle attempts), semantic confusion with repetitive meaningless tactics (64%+ of some model outputs), and hallucinated tactics that do not exist in the target ITP. Avoiding these failure modes is the primary skill.
Step-by-Step Workflow
Identify the VC structure. Parse the verification condition to determine: (a) what quantified variables exist, (b) what hypotheses are assumed, (c) what the goal statement is. Separate preconditions from the proof obligation itself. Identify whether the VC concerns arithmetic bounds, pointer validity, functional correctness, or invariant preservation.
Choose the target ITP and set up the proof environment. Determine whether the user needs Isabelle (.thy), Lean 4 (.lean), or Rocq (.v). Set up the theory/import header with required libraries:
- Isabelle:
theory VCProof imports Main with relevant Int, List, or Array theories
- Lean 4:
import Mathlib.Tactic plus domain-specific imports
- Rocq:
Require Import Lia ZArith List as baseline
Translate the VC into a formal statement. Convert the logical formula into the target ITP's syntax. Apply rewriting rules: map Why3 integer division to ITP-native div, convert array access notation, handle bitvector operations for kernel code. Preserve all quantifier structure exactly.
Decompose the proof into subgoals. Use structural tactics first:
- Introduce universally quantified variables and hypotheses (
intros, intro, fix)
- Case-split on disjunctions or conditional expressions (
cases, split, destruct)
- Simplify with definitional unfolding (
simp, unfold, simpl)
Apply arithmetic automation to leaf goals. For integer/natural number arithmetic subgoals, use the strongest available automation:
- Isabelle:
sledgehammer first, then arith, linarith, auto
- Lean 4:
omega, linarith, norm_num, simp [*]
- Rocq:
lia, omega, ring, auto with zarith
Handle non-arithmetic goals with library lemmas. For goals involving lists, arrays, or data structures, search for and apply relevant library lemmas explicitly. Use apply, rewrite, or exact with the specific lemma name rather than hoping automation finds it.
Validate proof syntax before presenting. Check for the three dominant error categories:
- No mismatched parentheses or brackets (syntactic errors)
- No repeated identical tactics in sequence (semantic confusion pattern)
- No invented tactic names -- only use tactics that exist in the target ITP version
- No
sorry, admit, or Admitted unless explicitly marking incomplete subgoals
Provide the proof with verification instructions. Give the user the complete proof script and the exact command to check it (e.g., isabelle build, lake build, coqc). If the proof depends on specific library versions, state them (Lean 4.21+, Rocq 8.20+, Isabelle 2024).
If a tactic fails, diagnose and retry. When a user reports a tactic failure, analyze the goal state they provide. Determine whether the failure is: (a) missing hypothesis -- add an have intermediate lemma, (b) wrong argument type -- insert coercions, (c) insufficient automation -- decompose further before re-applying.
Concrete Examples
Example 1: Loop invariant preservation for binary search
User: "I have this Why3 VC from a binary search. Prove it in Lean 4."
-- VC: If 0 <= lo <= hi <= arr.size and arr is sorted,
-- then after mid = (lo + hi) / 2 and lo' = mid + 1,
-- we have 0 <= lo' <= hi <= arr.size
Approach:
- Identify this as an arithmetic bounds VC with integer division
- Set up Lean 4 with
import Mathlib.Tactic
- Formalize and prove with
omega after introducing hypotheses
Output:
import Mathlib.Tactic
theorem binary_search_invariant_preservation
(lo hi size : Nat)
(h_lo_nn : 0 ≤ lo)
(h_lo_hi : lo ≤ hi)
(h_hi_sz : hi ≤ size)
(h_lo_lt_hi : lo < hi) -- loop guard
(mid : Nat)
(h_mid : mid = (lo + hi) / 2)
(lo' : Nat)
(h_lo' : lo' = mid + 1) :
0 ≤ lo' ∧ lo' ≤ hi ∧ hi ≤ size := by
constructor
· omega
constructor
· omega
· omega
Verify with: lake env lean BinarySearchVC.lean
Example 2: Array access bounds from Frama-C/Why3
User: "Prove this Isabelle VC: accessing buf[i] is safe when 0 <= i and i < length buf."
Approach:
- Identify as a memory safety VC (array bounds check)
- Formalize in Isabelle with list-based array modeling
- Apply
auto with list library lemmas
Output:
theory BufAccess
imports Main
begin
lemma buf_access_safe:
assumes "0 ≤ i" and "i < length buf"
shows "i < length buf"
using assms by simp
lemma buf_access_nth_defined:
assumes "0 ≤ (i :: nat)" and "i < length buf"
shows "buf ! i = buf ! i"
by simp
end
Verify with: isabelle build -d . -b BufAccess
Example 3: Postcondition proof for a swap function in Rocq
User: "I need to prove that after swapping a[i] and a[j], the value at position i equals the old value at j. The VC comes from Frama-C."
Approach:
- Model array as a function with pointwise update
- Prove the store/select property: reading index
i after storing at i returns the stored value
- Use
Rocq functional array axioms
Output:
Require Import ZArith Lia.
(* Array modeled as Z -> Z with pointwise update *)
Definition store (a : Z -> Z) (idx val : Z) : Z -> Z :=
fun k => if Z.eq_dec k idx then val else a k.
Theorem swap_postcondition :
forall (a : Z -> Z) (i j : Z),
i <> j ->
let a1 := store a i (a j) in
let a2 := store a1 j (a i) in
a2 i = a j.
Proof.
intros a i j Hneq.
unfold store. simpl.
destruct (Z.eq_dec i j) as [Heq | _].
- contradiction.
- destruct (Z.eq_dec i i) as [_ | Habs].
+ reflexivity.
+ exfalso. apply Habs. reflexivity.
Qed.
Verify with: coqc SwapVC.v
Best Practices
Do:
- Always decompose VCs into subgoals with
intros/cases/split before applying automation. This mirrors how successful proofs are structured and avoids the "repetitive meaningless tactic" failure mode.
- Prefer
omega/lia/linarith for linear arithmetic goals -- these are decision procedures that either succeed or definitively fail, giving clear signal.
- Include all necessary hypotheses in the formal statement. VCs from Why3 often carry many preconditions; dropping any one can make the goal unprovable.
- State the exact ITP version. Tactic behavior differs across versions (Lean 4.21 vs 4.10, Rocq 8.20 vs 8.18).
Avoid:
- Never generate a single monolithic tactic like
auto or simp as the entire proof for non-trivial VCs. If it were that simple, the ATP would have already solved it.
- Never invent tactic names. Common hallucinations include
why3, verify, blast (in Lean), or program_simpl without the right imports. Only use tactics you are certain exist.
- Never use
sorry, admit, or Admitted without explicitly telling the user the proof is incomplete. Mark each unfinished subgoal clearly.
- Never assume integer and natural number operations are interchangeable. Why3 uses mathematical integers; ITPs often default to natural numbers. Insert explicit coercions (
Int.toNat, Z.of_nat) where types differ.
Error Handling
| Error |
Cause |
Fix |
| "unknown tactic" |
Hallucinated tactic name |
Check the tactic exists in the target ITP version; replace with a known equivalent |
| "type mismatch" in goal |
Integer vs natural number confusion |
Add explicit type annotations and coercions at the VC formalization step |
| Tactic succeeds but proof doesn't close |
Remaining subgoals after auto/simp |
Use · (Lean) or - (Isabelle/Rocq) to focus on remaining goals; decompose further |
| "timeout" during proof check |
Proof search space too large |
Replace auto with more directed tactics; provide explicit lemma names to apply |
| Mismatched parentheses / syntax error |
Translation artifacts from AST mapping |
Re-check bracket nesting; Isabelle uses ‹ › for fact references, not < > |
| "sorry is not allowed" |
Incomplete proof submitted to strict checker |
Identify which subgoal is unsolved; attempt targeted automation or ask user for hints about the domain |
Limitations
- Non-linear arithmetic: VCs involving multiplication of variables, modular arithmetic, or bitwise operations are beyond the reach of
omega/lia. These require manual lemma construction or specialized tactics (nlinarith in Lean, nia in Rocq), which have low success rates.
- Heap reasoning: VCs about pointer structures (linked lists, trees) require separation logic or custom heap models. Standard ITP tactics do not handle these natively; the user needs a framework like Iris (Rocq/Lean) or AutoCorres (Isabelle).
- Scale: NTP4VC shows even the best models achieve only ~6-18% pass rates on industrial VCs. For genuinely hard VCs (those ATPs already failed on), expect to provide proof sketches that need human refinement rather than complete proofs.
- Library dependence: Proofs often depend on specific Mathlib (Lean), AFP (Isabelle), or Coq-stdlib lemmas. If the user's project uses a different library version or custom theories, generated proofs may not compile without adaptation.
- Why3 translation fidelity: The 2,400-rule translation from Why3 ASTs to ITP syntax is imperfect. Some Why3 constructs (algebraic types with refinements, ghost code) may not map cleanly, requiring manual fixup of the VC statement itself.
Reference
Paper: Neural Theorem Proving for Verification Conditions: A Real-World Benchmark (ICLR 2026)
Key takeaway: The NTP4VC benchmark of 600 real-world VCs from Linux/Contiki-OS shows that LLMs achieve 1.5-12% pass@8 rates vs. 18% for Sledgehammer, with tactic-based decomposition and arithmetic decision procedures being the most reliable proof strategy. Focus on structured subgoal decomposition and avoid whole-proof generation.
1---2name: neural-theorem-proving-verification3description: Generate formal proofs for program verification conditions (VCs) in Isabelle, Lean 4, and Rocq. Translates C/WhyML code obligations into proof assistant syntax and synthesizes tactic-based proofs. Use when: 'prove this verification condition', 'generate Isabelle proof for this invariant', 'verify this C function formally', 'translate this VC to Lean', 'help me prove this loop invariant', 'synthesize a Rocq proof for this postcondition'.4---56# Neural Theorem Proving for Verification Conditions78This skill enables Claude to generate formal proofs for verification conditions (VCs) arising from program verification. Following the NTP4VC methodology (ICLR 2026), it applies neural theorem proving to the hardest bottleneck in software verification: proving the logical obligations that automated theorem provers (ATPs) like Sledgehammer or CoqHammer cannot discharge. Claude translates VCs from Why3/Frama-C pipelines into Isabelle, Lean 4, or Rocq tactic proofs, using structured proof decomposition rather than whole-proof generation.910## When to Use1112- When a user has a verification condition from Frama-C, Why3, or another deductive verification tool and needs a proof in Isabelle, Lean 4, or Rocq13- When a user needs to prove a loop invariant, precondition, postcondition, or memory safety obligation for annotated C code14- When Sledgehammer, CoqHammer, or `auto`-level tactics fail on a VC and the user needs a manual tactic proof15- When a user wants to translate a Why3-generated VC into a different interactive theorem prover (ITP) language16- When a user is building a verification pipeline and needs help structuring VC proof obligations from ACSL-annotated C or WhyML specifications17- When a user asks to formally verify properties of data structures, sorting algorithms, or kernel-level code (e.g., linked lists, binary search, memory allocators)1819## Key Technique2021**Verification Conditions** are logical formulas generated by tools like Why3 from annotated source code. When you write a C function with ACSL annotations (preconditions via `requires`, postconditions via `ensures`, loop invariants via `loop invariant`), Frama-C and Why3 decompose correctness into individual proof obligations. Each VC encodes one specific claim: "if the precondition holds and the loop invariant held before this iteration, then the invariant holds after the iteration" or "array access index is within bounds." ATPs handle many VCs automatically, but real-world projects (Linux kernel, Contiki-OS) produce VCs that ATPs cannot solve -- these are the targets.2223The NTP4VC pipeline works as follows: Why3's VC generator produces an XML AST representation of each obligation. A translation layer (approximately 2,400 expert-written rewriting rules) maps these ASTs into Isabelle, Lean 4, or Rocq syntax. This handles prefix/infix conversions, if-then-else desugaring, match-case translation, and semantic rewrites (e.g., integer operations to natural number operations preferred by ITPs). The resulting ITP file contains the VC as a `lemma` or `theorem` statement with all necessary type definitions and previously-proved lemmas in scope.2425**Critical finding from NTP4VC**: Tactic-based step-by-step proofs dramatically outperform whole-proof-term generation. The most effective approach is to decompose VCs into subgoals using structural tactics (`intro`, `cases`, `simp`, `omega`) before applying domain-specific automation. Error analysis shows three dominant failure modes: syntactic errors (24%+ of Isabelle attempts), semantic confusion with repetitive meaningless tactics (64%+ of some model outputs), and hallucinated tactics that do not exist in the target ITP. Avoiding these failure modes is the primary skill.2627## Step-by-Step Workflow28291. **Identify the VC structure.** Parse the verification condition to determine: (a) what quantified variables exist, (b) what hypotheses are assumed, (c) what the goal statement is. Separate preconditions from the proof obligation itself. Identify whether the VC concerns arithmetic bounds, pointer validity, functional correctness, or invariant preservation.30312. **Choose the target ITP and set up the proof environment.** Determine whether the user needs Isabelle (`.thy`), Lean 4 (`.lean`), or Rocq (`.v`). Set up the theory/import header with required libraries:32 - Isabelle: `theory VCProof imports Main` with relevant `Int`, `List`, or `Array` theories33 - Lean 4: `import Mathlib.Tactic` plus domain-specific imports34 - Rocq: `Require Import Lia ZArith List` as baseline35363. **Translate the VC into a formal statement.** Convert the logical formula into the target ITP's syntax. Apply rewriting rules: map Why3 integer division to ITP-native `div`, convert array access notation, handle bitvector operations for kernel code. Preserve all quantifier structure exactly.37384. **Decompose the proof into subgoals.** Use structural tactics first:39 - Introduce universally quantified variables and hypotheses (`intros`, `intro`, `fix`)40 - Case-split on disjunctions or conditional expressions (`cases`, `split`, `destruct`)41 - Simplify with definitional unfolding (`simp`, `unfold`, `simpl`)42435. **Apply arithmetic automation to leaf goals.** For integer/natural number arithmetic subgoals, use the strongest available automation:44 - Isabelle: `sledgehammer` first, then `arith`, `linarith`, `auto`45 - Lean 4: `omega`, `linarith`, `norm_num`, `simp [*]`46 - Rocq: `lia`, `omega`, `ring`, `auto with zarith`47486. **Handle non-arithmetic goals with library lemmas.** For goals involving lists, arrays, or data structures, search for and apply relevant library lemmas explicitly. Use `apply`, `rewrite`, or `exact` with the specific lemma name rather than hoping automation finds it.49507. **Validate proof syntax before presenting.** Check for the three dominant error categories:51 - No mismatched parentheses or brackets (syntactic errors)52 - No repeated identical tactics in sequence (semantic confusion pattern)53 - No invented tactic names -- only use tactics that exist in the target ITP version54 - No `sorry`, `admit`, or `Admitted` unless explicitly marking incomplete subgoals55568. **Provide the proof with verification instructions.** Give the user the complete proof script and the exact command to check it (e.g., `isabelle build`, `lake build`, `coqc`). If the proof depends on specific library versions, state them (Lean 4.21+, Rocq 8.20+, Isabelle 2024).57589. **If a tactic fails, diagnose and retry.** When a user reports a tactic failure, analyze the goal state they provide. Determine whether the failure is: (a) missing hypothesis -- add an `have` intermediate lemma, (b) wrong argument type -- insert coercions, (c) insufficient automation -- decompose further before re-applying.5960## Concrete Examples6162**Example 1: Loop invariant preservation for binary search**6364User: "I have this Why3 VC from a binary search. Prove it in Lean 4."65```66-- VC: If 0 <= lo <= hi <= arr.size and arr is sorted,67-- then after mid = (lo + hi) / 2 and lo' = mid + 1,68-- we have 0 <= lo' <= hi <= arr.size69```7071Approach:721. Identify this as an arithmetic bounds VC with integer division732. Set up Lean 4 with `import Mathlib.Tactic`743. Formalize and prove with `omega` after introducing hypotheses7576Output:77```lean78import Mathlib.Tactic7980theorem binary_search_invariant_preservation81 (lo hi size : Nat)82 (h_lo_nn : 0 ≤ lo)83 (h_lo_hi : lo ≤ hi)84 (h_hi_sz : hi ≤ size)85 (h_lo_lt_hi : lo < hi) -- loop guard86 (mid : Nat)87 (h_mid : mid = (lo + hi) / 2)88 (lo' : Nat)89 (h_lo' : lo' = mid + 1) :90 0 ≤ lo' ∧ lo' ≤ hi ∧ hi ≤ size := by91 constructor92 · omega93 constructor94 · omega95 · omega96```9798Verify with: `lake env lean BinarySearchVC.lean`99100**Example 2: Array access bounds from Frama-C/Why3**101102User: "Prove this Isabelle VC: accessing `buf[i]` is safe when `0 <= i` and `i < length buf`."103104Approach:1051. Identify as a memory safety VC (array bounds check)1062. Formalize in Isabelle with list-based array modeling1073. Apply `auto` with list library lemmas108109Output:110```isabelle111theory BufAccess112 imports Main113begin114115lemma buf_access_safe:116 assumes "0 ≤ i" and "i < length buf"117 shows "i < length buf"118 using assms by simp119120lemma buf_access_nth_defined:121 assumes "0 ≤ (i :: nat)" and "i < length buf"122 shows "buf ! i = buf ! i"123 by simp124125end126```127128Verify with: `isabelle build -d . -b BufAccess`129130**Example 3: Postcondition proof for a swap function in Rocq**131132User: "I need to prove that after swapping `a[i]` and `a[j]`, the value at position `i` equals the old value at `j`. The VC comes from Frama-C."133134Approach:1351. Model array as a function with pointwise update1362. Prove the store/select property: reading index `i` after storing at `i` returns the stored value1373. Use `Rocq` functional array axioms138139Output:140```coq141Require Import ZArith Lia.142143(* Array modeled as Z -> Z with pointwise update *)144Definition store (a : Z -> Z) (idx val : Z) : Z -> Z :=145 fun k => if Z.eq_dec k idx then val else a k.146147Theorem swap_postcondition :148 forall (a : Z -> Z) (i j : Z),149 i <> j ->150 let a1 := store a i (a j) in151 let a2 := store a1 j (a i) in152 a2 i = a j.153Proof.154 intros a i j Hneq.155 unfold store. simpl.156 destruct (Z.eq_dec i j) as [Heq | _].157 - contradiction.158 - destruct (Z.eq_dec i i) as [_ | Habs].159 + reflexivity.160 + exfalso. apply Habs. reflexivity.161Qed.162```163164Verify with: `coqc SwapVC.v`165166## Best Practices167168**Do:**169- Always decompose VCs into subgoals with `intros`/`cases`/`split` before applying automation. This mirrors how successful proofs are structured and avoids the "repetitive meaningless tactic" failure mode.170- Prefer `omega`/`lia`/`linarith` for linear arithmetic goals -- these are decision procedures that either succeed or definitively fail, giving clear signal.171- Include all necessary hypotheses in the formal statement. VCs from Why3 often carry many preconditions; dropping any one can make the goal unprovable.172- State the exact ITP version. Tactic behavior differs across versions (Lean 4.21 vs 4.10, Rocq 8.20 vs 8.18).173174**Avoid:**175- Never generate a single monolithic tactic like `auto` or `simp` as the entire proof for non-trivial VCs. If it were that simple, the ATP would have already solved it.176- Never invent tactic names. Common hallucinations include `why3`, `verify`, `blast` (in Lean), or `program_simpl` without the right imports. Only use tactics you are certain exist.177- Never use `sorry`, `admit`, or `Admitted` without explicitly telling the user the proof is incomplete. Mark each unfinished subgoal clearly.178- Never assume integer and natural number operations are interchangeable. Why3 uses mathematical integers; ITPs often default to natural numbers. Insert explicit coercions (`Int.toNat`, `Z.of_nat`) where types differ.179180## Error Handling181182| Error | Cause | Fix |183|-------|-------|-----|184| "unknown tactic" | Hallucinated tactic name | Check the tactic exists in the target ITP version; replace with a known equivalent |185| "type mismatch" in goal | Integer vs natural number confusion | Add explicit type annotations and coercions at the VC formalization step |186| Tactic succeeds but proof doesn't close | Remaining subgoals after `auto`/`simp` | Use `·` (Lean) or `-` (Isabelle/Rocq) to focus on remaining goals; decompose further |187| "timeout" during proof check | Proof search space too large | Replace `auto` with more directed tactics; provide explicit lemma names to `apply` |188| Mismatched parentheses / syntax error | Translation artifacts from AST mapping | Re-check bracket nesting; Isabelle uses `‹` `›` for fact references, not `<` `>` |189| "sorry is not allowed" | Incomplete proof submitted to strict checker | Identify which subgoal is unsolved; attempt targeted automation or ask user for hints about the domain |190191## Limitations192193- **Non-linear arithmetic**: VCs involving multiplication of variables, modular arithmetic, or bitwise operations are beyond the reach of `omega`/`lia`. These require manual lemma construction or specialized tactics (`nlinarith` in Lean, `nia` in Rocq), which have low success rates.194- **Heap reasoning**: VCs about pointer structures (linked lists, trees) require separation logic or custom heap models. Standard ITP tactics do not handle these natively; the user needs a framework like Iris (Rocq/Lean) or AutoCorres (Isabelle).195- **Scale**: NTP4VC shows even the best models achieve only ~6-18% pass rates on industrial VCs. For genuinely hard VCs (those ATPs already failed on), expect to provide proof sketches that need human refinement rather than complete proofs.196- **Library dependence**: Proofs often depend on specific Mathlib (Lean), AFP (Isabelle), or Coq-stdlib lemmas. If the user's project uses a different library version or custom theories, generated proofs may not compile without adaptation.197- **Why3 translation fidelity**: The 2,400-rule translation from Why3 ASTs to ITP syntax is imperfect. Some Why3 constructs (algebraic types with refinements, ghost code) may not map cleanly, requiring manual fixup of the VC statement itself.198199## Reference200201**Paper**: [Neural Theorem Proving for Verification Conditions: A Real-World Benchmark](https://arxiv.org/abs/2601.18944v2) (ICLR 2026)202**Key takeaway**: The NTP4VC benchmark of 600 real-world VCs from Linux/Contiki-OS shows that LLMs achieve 1.5-12% pass@8 rates vs. 18% for Sledgehammer, with tactic-based decomposition and arithmetic decision procedures being the most reliable proof strategy. Focus on structured subgoal decomposition and avoid whole-proof generation.