use crate::traits::RuntimeDriverError;
use meerkat_core::SessionId;
use meerkat_core::panic_payload::{PanicPayloadLogGate, panic_payload_detail};
pub(crate) fn run_boundary_guarded<T>(
gate: &PanicPayloadLogGate,
boundary: &'static str,
session_id: &SessionId,
build_message: impl FnOnce(&str) -> String,
action: impl FnOnce() -> T,
) -> Result<T, RuntimeDriverError> {
let gate_key = format!("{boundary}:{session_id}");
match std::panic::catch_unwind(std::panic::AssertUnwindSafe(action)) {
Ok(value) => {
gate.clear(&gate_key);
Ok(value)
}
Err(payload) => {
let detail = panic_payload_detail(payload.as_ref());
let log_decision = gate.observe(&gate_key, &detail);
if log_decision.should_log {
tracing::error!(
%session_id,
boundary,
panic = %detail,
repeated_sightings = log_decision.repeated_sightings,
"runtime attachment boundary panicked; payload recovered, sanitized, and converted to a typed driver error"
);
}
Err(RuntimeDriverError::Internal(build_message(&detail)))
}
}
}
#[cfg(all(test, not(target_arch = "wasm32")))]
#[allow(clippy::unwrap_used, clippy::expect_used, clippy::panic)]
mod tests {
use super::*;
use std::sync::{Arc, Mutex};
#[derive(Clone)]
struct SharedBuf(Arc<Mutex<Vec<u8>>>);
impl std::io::Write for SharedBuf {
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
self.0
.lock()
.expect("log buffer lock")
.extend_from_slice(buf);
Ok(buf.len())
}
fn flush(&mut self) -> std::io::Result<()> {
Ok(())
}
}
fn capture_error_logs() -> (Arc<Mutex<Vec<u8>>>, tracing::subscriber::DefaultGuard) {
let buf = Arc::new(Mutex::new(Vec::new()));
let writer_buf = SharedBuf(Arc::clone(&buf));
let subscriber = tracing_subscriber::fmt()
.with_max_level(tracing::Level::ERROR)
.with_ansi(false)
.with_writer(move || writer_buf.clone())
.finish();
let guard = tracing::subscriber::set_default(subscriber);
(buf, guard)
}
fn captured(buf: &Arc<Mutex<Vec<u8>>>) -> String {
String::from_utf8(buf.lock().expect("log buffer lock").clone())
.expect("captured logs should be utf8")
}
#[test]
fn panicking_boundary_yields_typed_error_with_payload_and_logs_it() {
let (buf, _guard) = capture_error_logs();
let gate = PanicPayloadLogGate::default();
let session_id = SessionId::new();
let result: Result<(), RuntimeDriverError> = run_boundary_guarded(
&gate,
"executor-factory",
&session_id,
|detail| {
format!("executor factory panicked while attaching session {session_id}: {detail}")
},
|| panic!("unregistered residue factory panic"),
);
let error = result.expect_err("a panicking boundary must fail");
let RuntimeDriverError::Internal(message) = &error else {
panic!("caught panic must classify as RuntimeDriverError::Internal, got {error:?}");
};
assert!(
message.contains("executor factory panicked while attaching session"),
"the historical message prefix must be preserved, got: {message}"
);
assert!(
message.contains("unregistered residue factory panic"),
"typed error must carry the panic payload, got: {message}"
);
let logs = captured(&buf);
assert!(
logs.contains("unregistered residue factory panic"),
"the panic payload must be logged, got: {logs}"
);
assert!(
logs.contains(&session_id.to_string()),
"the log line must carry the session context, got: {logs}"
);
}
#[test]
fn repeated_identical_panics_log_once_and_success_resets_the_gate() {
let (buf, _guard) = capture_error_logs();
let gate = PanicPayloadLogGate::default();
let session_id = SessionId::new();
for _ in 0..3 {
let result: Result<(), RuntimeDriverError> = run_boundary_guarded(
&gate,
"executor-factory",
&session_id,
|detail| format!("boundary panicked: {detail}"),
|| panic!("repeated boundary payload"),
);
let message = result.expect_err("panic must fail").to_string();
assert!(message.contains("repeated boundary payload"));
}
let logs = captured(&buf);
assert_eq!(
logs.matches("repeated boundary payload").count(),
1,
"identical repeated panics must log exactly once, got: {logs}"
);
run_boundary_guarded(
&gate,
"executor-factory",
&session_id,
|detail| format!("boundary panicked: {detail}"),
|| (),
)
.expect("non-panicking boundary must pass through");
let _: Result<(), RuntimeDriverError> = run_boundary_guarded(
&gate,
"executor-factory",
&session_id,
|detail| format!("boundary panicked: {detail}"),
|| panic!("repeated boundary payload"),
);
let logs = captured(&buf);
assert_eq!(
logs.matches("repeated boundary payload").count(),
2,
"a success between incidents must reset the rate limit, got: {logs}"
);
}
}