Skip to content

Commit 133e31b

Browse files
hyperpolymathclaude
andcommitted
feat(suggest): implement echidna suggest CLI verb — mechanical tactic-variant finder
Closes §4.1+§4.3 from ECHIDNA-NOTES-2026-04-27.md. New `echidna suggest <file>:<lemma> --prover <P>` verb extracts a named lemma, identifies tactic sites, looks up alternatives in data/synonyms/<prover>.toml, re-runs the prover on each single-site substitution, and prints a ranked Markdown table. Module layout: src/rust/suggest/ mod.rs — SuggestConfig, run() entry point, parse_target, detect_prover extractor.rs — Probe + TacticSite; per-prover line-based extractors for Isabelle/HOL, Coq, Lean4, Idris2, Agda synonyms.rs — SynonymTable + SynonymEntry (TOML deserialisation, by_name index) variant.rs — Variant generation (single-site substitutions, Levenshtein ranking) tester.rs — VariantOutcome; concurrent JoinSet-based variant testing report.rs — Markdown table printer (closes > fails > timeout > syntax; edit asc) 26 new unit tests across 5 files; lib test count 972 → 998. All 998 pass. Does NOT call ProverBackend::suggest_tactics (that is the §4.4 GNN layer). Acceptance criteria covered: parse_target, detect_prover, synonym alternatives, word-boundary substitution, Levenshtein, dry-run, empty-result paths. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 36d4c09 commit 133e31b

8 files changed

Lines changed: 1482 additions & 0 deletions

File tree

src/rust/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ pub mod parsers;
3535
pub mod proof_encoding; // CBOR encoding + proof identity hashing
3636
pub mod proof_search; // Chapel parallel proof search (optional feature)
3737
pub mod provers;
38+
pub mod suggest; // Mechanical tactic-variant finder (echidna suggest verb)
3839
pub mod vcl_ut;
3940
pub mod verification;
4041
#[cfg(feature = "verisim")]

src/rust/main.rs

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,37 @@ enum Commands {
174174

175175
/// Interactive diagnostics REPL for health monitoring
176176
Diagnostics,
177+
178+
/// Suggest tactic variants that close a failing lemma
179+
Suggest {
180+
/// Target in `<file>:<lemma>` form, e.g. `Foo.thy:bar`
181+
#[arg(value_name = "TARGET")]
182+
target: String,
183+
184+
/// Prover to use (auto-detected from file extension if absent)
185+
#[arg(short, long)]
186+
prover: Option<ProverKind>,
187+
188+
/// Time budget per variant attempt in seconds
189+
#[arg(long, default_value_t = 60)]
190+
budget: u64,
191+
192+
/// Maximum number of variants to report
193+
#[arg(long, default_value_t = 10)]
194+
top: usize,
195+
196+
/// Synonym table directory (defaults to `data/synonyms/`)
197+
#[arg(long)]
198+
synonyms_dir: Option<PathBuf>,
199+
200+
/// Don't run the prover; just print candidate variants
201+
#[arg(long)]
202+
dry_run: bool,
203+
204+
/// Maximum concurrent variant tests
205+
#[arg(long, default_value_t = 4)]
206+
max_parallel: usize,
207+
},
177208
}
178209

179210
#[tokio::main]
@@ -254,6 +285,27 @@ async fn main() -> Result<()> {
254285
Commands::Diagnostics => {
255286
diagnostics_command().await?;
256287
},
288+
Commands::Suggest {
289+
target,
290+
prover,
291+
budget,
292+
top,
293+
synonyms_dir,
294+
dry_run,
295+
max_parallel,
296+
} => {
297+
let synonyms_dir = synonyms_dir.unwrap_or_else(|| PathBuf::from("data/synonyms"));
298+
let config = echidna::suggest::SuggestConfig {
299+
target,
300+
prover,
301+
budget: std::time::Duration::from_secs(budget),
302+
top,
303+
synonyms_dir,
304+
dry_run,
305+
max_parallel,
306+
};
307+
echidna::suggest::run(config).await?;
308+
},
257309
}
258310

259311
Ok(())

0 commit comments

Comments
 (0)