pub(super) fn startup_recovery() {
let daemon = crate::app::load_config().unwrap_or_default().daemon;
if let Ok(store) = mermaid_runtime::RuntimeStore::open_default() {
match store.reconcile_after_restart() {
Ok((tasks, claims)) if tasks + claims > 0 => {
tracing::info!(
tasks,
claims,
"reconciled state stranded by a previous daemon"
);
},
Ok(_) => {},
Err(error) => tracing::warn!(error = %error, "startup reconcile failed"),
}
match crate::session::rebuild_session_index(&store) {
Ok(report) if report.backfilled + report.pruned > 0 => tracing::info!(
projects = report.projects,
backfilled = report.backfilled,
pruned = report.pruned,
"rebuilt the session index from disk"
),
Ok(_) => {},
Err(error) => tracing::warn!(error = %error, "session index rebuild failed"),
}
match store.gc(daemon.retention_days, daemon.outcomes_retention_days) {
Ok(removed) if removed > 0 => tracing::info!(removed, "gc pruned old runtime rows"),
Ok(_) => {},
Err(error) => tracing::warn!(error = %error, "startup gc failed"),
}
}
if let Ok(removed) = mermaid_runtime::gc_old_checkpoint_dirs(daemon.retention_days)
&& removed > 0
{
tracing::info!(removed, "gc removed old checkpoint directories");
}
if let Ok(removed) = mermaid_runtime::gc_orphaned_worktrees(daemon.retention_days)
&& removed > 0
{
tracing::info!(removed, "gc removed orphaned subagent worktrees");
}
if let Ok(removed) = sweep_stale_bg_logs(daemon.retention_days)
&& removed > 0
{
tracing::info!(removed, "reaped stale background-command logs");
}
if let Ok(removed) =
crate::session::scratchpad::sweep_stale(daemon.scratchpad_retention_days.max(0) as u64)
&& removed > 0
{
tracing::info!(removed, "reaped stale session scratchpads");
}
}
pub(super) fn sweep_stale_bg_logs(retention_days: i64) -> std::io::Result<u64> {
sweep_stale_bg_logs_in(&mermaid_model::utils::private_temp_dir()?, retention_days)
}
pub(super) fn sweep_stale_bg_logs_in(
dir: &std::path::Path,
retention_days: i64,
) -> std::io::Result<u64> {
let cutoff = std::time::SystemTime::now()
.checked_sub(std::time::Duration::from_secs(
retention_days.max(0) as u64 * 24 * 60 * 60,
))
.unwrap_or(std::time::UNIX_EPOCH);
let mut removed = 0u64;
for entry in std::fs::read_dir(dir)?.flatten() {
let name = entry.file_name();
let Some(name) = name.to_str() else {
continue;
};
if !(name.starts_with("mermaid-bg-") && name.ends_with(".log")) {
continue;
}
let stale = entry
.metadata()
.and_then(|m| m.modified())
.map(|mtime| mtime < cutoff)
.unwrap_or(false);
if stale && std::fs::remove_file(entry.path()).is_ok() {
removed += 1;
}
}
Ok(removed)
}