@@ -285,13 +285,34 @@ impl ProverBackend for KeYmaeraXBackend {
285285
286286 async fn suggest_tactics (
287287 & self ,
288- _state : & ProofState ,
289- _limit : usize ,
288+ state : & ProofState ,
289+ limit : usize ,
290290 ) -> Result < Vec < Tactic > > {
291- // The Bellerophon tactic language is too large to enumerate
292- // statically here; GNN-ranked Bellerophon suggestion is
293- // queued under §4.4 of ECHIDNA-NOTES.
294- Ok ( vec ! [ ] )
291+ if state. goals . is_empty ( ) {
292+ return Ok ( vec ! [ ] ) ;
293+ }
294+ // KeYmaera X Bellerophon tactics. This is a curated starter set
295+ // covering the tactics most visible in the KeYmaera X tutorial and
296+ // distributed benchmark archive. The full Bellerophon language is
297+ // open-ended; GNN-ranked corpus suggestions (§4.4) will extend this.
298+ let tactics = vec ! [
299+ Tactic :: Custom { prover: "keymaerax" . to_string( ) , command: "auto" . to_string( ) , args: vec![ ] } ,
300+ Tactic :: Custom { prover: "keymaerax" . to_string( ) , command: "QE" . to_string( ) , args: vec![ ] } ,
301+ Tactic :: Custom { prover: "keymaerax" . to_string( ) , command: "loop" . to_string( ) , args: vec![ ] } ,
302+ Tactic :: Custom { prover: "keymaerax" . to_string( ) , command: "ODE" . to_string( ) , args: vec![ ] } ,
303+ Tactic :: Custom { prover: "keymaerax" . to_string( ) , command: "solve" . to_string( ) , args: vec![ ] } ,
304+ Tactic :: Custom { prover: "keymaerax" . to_string( ) , command: "dI" . to_string( ) , args: vec![ ] } ,
305+ Tactic :: Custom { prover: "keymaerax" . to_string( ) , command: "dC" . to_string( ) , args: vec![ ] } ,
306+ Tactic :: Custom { prover: "keymaerax" . to_string( ) , command: "dW" . to_string( ) , args: vec![ ] } ,
307+ Tactic :: Custom { prover: "keymaerax" . to_string( ) , command: "diffInd" . to_string( ) , args: vec![ ] } ,
308+ Tactic :: Custom { prover: "keymaerax" . to_string( ) , command: "cut" . to_string( ) , args: vec![ ] } ,
309+ Tactic :: Custom { prover: "keymaerax" . to_string( ) , command: "prop" . to_string( ) , args: vec![ ] } ,
310+ Tactic :: Custom { prover: "keymaerax" . to_string( ) , command: "implyR" . to_string( ) , args: vec![ ] } ,
311+ Tactic :: Custom { prover: "keymaerax" . to_string( ) , command: "andL" . to_string( ) , args: vec![ ] } ,
312+ Tactic :: Custom { prover: "keymaerax" . to_string( ) , command: "hideL" . to_string( ) , args: vec![ ] } ,
313+ Tactic :: Custom { prover: "keymaerax" . to_string( ) , command: "closeTrue" . to_string( ) , args: vec![ ] } ,
314+ ] ;
315+ Ok ( crate :: provers:: gnn_augment_tactics ( & self . config , state, "keymaerax" , tactics, limit) . await )
295316 }
296317
297318 async fn search_theorems ( & self , _pattern : & str ) -> Result < Vec < String > > {
@@ -405,4 +426,26 @@ mod tests {
405426 _ => panic ! ( "expected Term::Const" ) ,
406427 }
407428 }
429+
430+ #[ tokio:: test]
431+ async fn suggest_tactics_empty_goals_returns_empty ( ) {
432+ let backend = KeYmaeraXBackend :: new ( ProverConfig :: default ( ) ) ;
433+ let state = ProofState :: default ( ) ;
434+ let tactics = backend. suggest_tactics ( & state, 10 ) . await . unwrap ( ) ;
435+ assert ! ( tactics. is_empty( ) ) ;
436+ }
437+
438+ #[ tokio:: test]
439+ async fn suggest_tactics_returns_kyx_tactics ( ) {
440+ let backend = KeYmaeraXBackend :: new ( ProverConfig :: default ( ) ) ;
441+ let kyx = "ArchiveEntry \" x\" \n Problem\n x > 0 -> [x := x + 1;] x > 0\n End.\n End.\n " ;
442+ let state = backend. parse_string ( kyx) . await . expect ( "parse_string" ) ;
443+ let tactics = backend. suggest_tactics ( & state, 10 ) . await . unwrap ( ) ;
444+ assert ! ( !tactics. is_empty( ) ) ;
445+ let names: Vec < _ > = tactics. iter ( ) . filter_map ( |t| {
446+ if let Tactic :: Custom { command, .. } = t { Some ( command. as_str ( ) ) } else { None }
447+ } ) . collect ( ) ;
448+ assert ! ( names. contains( & "auto" ) , "expected 'auto' tactic" ) ;
449+ assert ! ( names. contains( & "QE" ) , "expected 'QE' tactic" ) ;
450+ }
408451}
0 commit comments