magi-code 0.82.1

Repository-aware CLI coding agent for terminal work
Documentation
//! Exclusive writer admission, separate from shared retention protection.
use super::Session;
use crate::persistence::CrossProcessFileLock;

impl Session {
    pub(crate) fn try_frontend_writer(&self) -> anyhow::Result<Option<CrossProcessFileLock>> {
        let root = self
            .path
            .parent()
            .ok_or_else(|| anyhow::anyhow!("missing root"))?;
        super::store::prepare_session_root(root)?;
        CrossProcessFileLock::try_acquire(&self.path.with_extension("writer"))
    }
}

#[cfg(test)]
mod tests {
    use super::super::SessionManager;
    use tempfile::TempDir;

    #[test]
    fn writer_conflict_and_dead_owner_recovery_cross_process() {
        const ROOT: &str = "MAGI_TEST_SESSION_WRITER_ROOT";
        const MODE: &str = "MAGI_TEST_SESSION_WRITER_MODE";
        if let Some(root) = std::env::var_os(ROOT) {
            let session = SessionManager::new(root.into())
                .open("writer-test")
                .unwrap();
            let lease = session.try_frontend_writer().unwrap();
            if std::env::var(MODE).unwrap() == "conflict" {
                assert!(lease.is_none());
                return;
            }
            assert!(lease.is_some());
            // Leave the lock behind, like a killed TUI process.
            std::process::exit(0);
        }
        let temp = TempDir::new().unwrap();
        let root = temp.path().join("sessions");
        let session = SessionManager::new(root.clone())
            .open("writer-test")
            .unwrap();
        let owner = session.try_frontend_writer().unwrap().unwrap();
        let child = |mode: &str| {
            let output = std::process::Command::new(std::env::current_exe().unwrap())
                .args(["--exact", "sessions::writer::tests::writer_conflict_and_dead_owner_recovery_cross_process", "--quiet"])
                .env(ROOT, &root).env(MODE, mode).output().unwrap();
            assert!(
                output.status.success(),
                "{}",
                String::from_utf8_lossy(&output.stderr)
            );
        };
        child("conflict");
        drop(owner);
        child("abandon");
        assert!(session.try_frontend_writer().unwrap().is_some());
    }
}