1818//
1919// Wave-1 (this file): Tier-1 backends — version-check smoke tests. Proves
2020// the subprocess wiring is real, not mocked, and that the binary responds.
21+ // L3 hygiene: emits VeriSimDB records for each test outcome via
22+ // `record_proof_attempt`, feeding the learning loop.
2123//
2224// Wave-2+: adds per-backend canonical micro-goals (fed through
2325// `ProverBackend::verify_proof`) once the fixtures land under
2729#![ cfg( feature = "live-provers" ) ]
2830
2931use std:: path:: PathBuf ;
32+ use std:: time:: Instant ;
3033
3134use echidna:: provers:: { ProverBackend , ProverConfig , ProverFactory , ProverKind } ;
35+ use echidna:: verisim_bridge:: { ProofAttempt , VeriSimDBClient } ;
3236
3337/// Check if a binary exists on PATH. Returns the resolved absolute path
3438/// for diagnostics, or None when the binary is absent.
@@ -56,11 +60,64 @@ fn try_live_backend(kind: ProverKind, exe: &str) -> Option<Box<dyn ProverBackend
5660 ProverFactory :: create ( kind, config) . ok ( )
5761}
5862
63+ /// Emit a VeriSimDB proof-attempt record for the test outcome.
64+ /// Non-fatal: if VeriSimDB is unreachable, log a warning and continue —
65+ /// never fail the test. This feeds the learning loop in hypatia's rule H3.
66+ async fn emit_live_result ( kind : ProverKind , exe : & str , version : Option < & str > , elapsed_ms : u64 ) {
67+ let outcome = if version. is_some ( ) { "success" } else { "failure" } ;
68+ let error_msg = if version. is_none ( ) {
69+ Some ( format ! ( "{} version() returned no version string" , kind_label( kind) ) )
70+ } else {
71+ None
72+ } ;
73+
74+ let attempt = ProofAttempt {
75+ attempt_id : format ! (
76+ "live-version-{}-{}" ,
77+ exe. replace( [ '/' , '\\' ] , "_" ) ,
78+ std:: time:: SystemTime :: now( )
79+ . duration_since( std:: time:: UNIX_EPOCH )
80+ . unwrap_or_default( )
81+ . as_millis( )
82+ ) ,
83+ obligation_id : format ! ( "live-version-check-{}" , exe) ,
84+ repo : "hyperpolymath/echidna" . to_string ( ) ,
85+ file : "tests/live_prover_suite.rs" . to_string ( ) ,
86+ claim : format ! ( "Prover {} is reachable and can report version" , kind_label( kind) ) ,
87+ obligation_class : "system_integration" . to_string ( ) ,
88+ prover_used : format ! ( "{:?}" , kind) . to_lowercase ( ) ,
89+ outcome : outcome. to_string ( ) ,
90+ duration_ms : elapsed_ms,
91+ confidence : if version. is_some ( ) { 1.0 } else { 0.0 } ,
92+ parent_attempt_id : None ,
93+ strategy_tag : "live_version_check" . to_string ( ) ,
94+ started_at : chrono:: Utc :: now ( )
95+ . checked_sub_signed ( chrono:: Duration :: milliseconds ( elapsed_ms as i64 ) )
96+ . unwrap_or_else ( chrono:: Utc :: now)
97+ . to_rfc3339 ( ) ,
98+ completed_at : chrono:: Utc :: now ( ) . to_rfc3339 ( ) ,
99+ prover_output : version. unwrap_or ( "" ) . to_string ( ) ,
100+ error_message : error_msg,
101+ } ;
102+
103+ // Resolve VeriSimDB URL from VERISIMDB_URL env var or default to localhost:7700
104+ let verisimdb_url = std:: env:: var ( "VERISIMDB_URL" )
105+ . unwrap_or_else ( |_| "http://localhost:7700" . to_string ( ) ) ;
106+ let client = VeriSimDBClient :: new ( & verisimdb_url) ;
107+ if let Err ( e) = client. record_proof_attempt ( & attempt) . await {
108+ eprintln ! (
109+ "VeriSimDB emit skipped for {}: {} (VeriSimDB not running?)" ,
110+ kind_label( kind) ,
111+ e
112+ ) ;
113+ }
114+ }
115+
59116/// Version-check helper: instantiates the backend, calls `version()`, and
60117/// asserts the call succeeded and returned *something*. A backend that
61118/// compiles but cannot speak to its binary returns `Err`, which we surface
62119/// as a test failure — that is exactly the mock-vs-reality gap this suite
63- /// exists to catch.
120+ /// exists to catch. Also emits a VeriSimDB record on completion.
64121async fn assert_version_reachable ( kind : ProverKind , exe : & str ) {
65122 let Some ( backend) = try_live_backend ( kind, exe) else {
66123 eprintln ! (
@@ -70,16 +127,23 @@ async fn assert_version_reachable(kind: ProverKind, exe: &str) {
70127 ) ;
71128 return ;
72129 } ;
130+
131+ let start_time = Instant :: now ( ) ;
73132 match backend. version ( ) . await {
74133 Ok ( v) => {
134+ let elapsed_ms = start_time. elapsed ( ) . as_millis ( ) as u64 ;
75135 assert ! (
76136 !v. trim( ) . is_empty( ) ,
77137 "{} version() returned empty string — subprocess is wired but produced no output" ,
78138 kind_label( kind) ,
79139 ) ;
80140 eprintln ! ( "OK: {} reported version = {:?}" , kind_label( kind) , v) ;
141+ emit_live_result ( kind, exe, Some ( & v) , elapsed_ms) . await ;
81142 } ,
82143 Err ( e) => {
144+ let elapsed_ms = start_time. elapsed ( ) . as_millis ( ) as u64 ;
145+ eprintln ! ( "FAIL: {} version() failed: {}" , kind_label( kind) , e) ;
146+ emit_live_result ( kind, exe, None , elapsed_ms) . await ;
83147 panic ! (
84148 "{} live version() failed: {}. Binary found on PATH but the \
85149 backend's subprocess wiring did not produce a usable version \
0 commit comments