magi-code 0.63.3

Repository-aware CLI coding agent for terminal work
Documentation
use crate::{
    instructions::InstructionFile,
    sessions::{
        MAX_METADATA_VISIT_BYTES, MAX_METADATA_VISIT_LINES, Session, SessionEventKind,
        report_session_diagnostic,
    },
};
use std::{collections::HashSet, path::PathBuf};

#[derive(Debug, Default)]
pub(crate) struct SubdirInstructionState {
    claimed: HashSet<PathBuf>,
}

impl SubdirInstructionState {
    pub(crate) fn new(initial_instructions: &[InstructionFile], session: Option<&Session>) -> Self {
        let mut state = Self::default();
        state.seed_initial(initial_instructions);
        if let Some(session) = session {
            state.seed_from_full_session_log(session);
        }
        state
    }

    pub(crate) fn claimed_mut(&mut self) -> &mut HashSet<PathBuf> {
        &mut self.claimed
    }

    pub(crate) fn seed_initial(&mut self, instructions: &[InstructionFile]) {
        for instruction in instructions {
            let canonical = instruction
                .path
                .canonicalize()
                .unwrap_or_else(|_| instruction.path.clone());
            self.claimed.insert(canonical);
        }
    }

    pub(crate) fn seed_from_full_session_log(&mut self, session: &Session) {
        let events = match session
            .read_events_tolerant_bounded(MAX_METADATA_VISIT_LINES, MAX_METADATA_VISIT_BYTES)
        {
            Ok(events) => events,
            Err(error) => {
                report_session_diagnostic(
                    crate::sessions::SessionDiagnosticOperation::Replay,
                    session.path(),
                    &error,
                );
                return;
            }
        };
        for event in events.events {
            match event.kind() {
                Some(SessionEventKind::SubdirInstructionLoad) => {
                    if event
                        .payload
                        .get("source")
                        .and_then(serde_json::Value::as_str)
                        == Some("subdir_agents")
                        && event
                            .payload
                            .get("status")
                            .and_then(serde_json::Value::as_str)
                            == Some("success")
                        && let Some(path) = event
                            .payload
                            .get("path")
                            .and_then(serde_json::Value::as_str)
                    {
                        self.claimed.insert(PathBuf::from(path));
                    }
                }
                Some(SessionEventKind::ProviderContextItem) => {
                    if event
                        .payload
                        .get("source")
                        .and_then(serde_json::Value::as_str)
                        == Some("subdir_agents")
                        && let Some(path) = event
                            .payload
                            .get("path")
                            .and_then(serde_json::Value::as_str)
                    {
                        self.claimed.insert(PathBuf::from(path));
                    }
                }
                _ => {}
            }
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::{
        instructions::{InstructionFile, InstructionSourceKind},
        sessions::{SessionManager, record_session_event},
    };
    use serde_json::json;

    #[test]
    fn oversized_session_log_does_not_seed_subdir_claims() {
        let temp = tempfile::TempDir::new().unwrap();
        let session = SessionManager::new(temp.path().join("sessions"))
            .open("oversized")
            .unwrap();
        std::fs::create_dir_all(session.path().parent().unwrap()).unwrap();
        std::fs::OpenOptions::new()
            .create(true)
            .write(true)
            .truncate(true)
            .open(session.path())
            .unwrap()
            .set_len((crate::sessions::MAX_METADATA_VISIT_BYTES as u64) + 1)
            .unwrap();

        let state = SubdirInstructionState::new(&[], Some(&session));

        assert!(state.claimed.is_empty());
    }

    #[test]
    fn seeds_initial_and_full_session_audit_paths() {
        let temp = tempfile::TempDir::new().unwrap();
        let session = SessionManager::new(temp.path().join("sessions"))
            .create()
            .unwrap();
        let initial = temp.path().join("AGENTS.md");
        std::fs::write(&initial, "root").unwrap();
        let subdir = temp.path().join("a/AGENTS.md");
        std::fs::create_dir_all(subdir.parent().unwrap()).unwrap();
        std::fs::write(&subdir, "a").unwrap();
        record_session_event(
            Some(&session),
            temp.path(),
            SessionEventKind::SubdirInstructionLoad,
            json!({
                "source":"subdir_agents",
                "path": subdir.canonicalize().unwrap(),
                "status":"success",
                "bytes":1,
                "content_hash":"sha256:test"
            }),
        )
        .unwrap();

        let state = SubdirInstructionState::new(
            &[InstructionFile {
                kind: InstructionSourceKind::Repository,
                path: initial.clone(),
                content: "root".to_string(),
            }],
            Some(&session),
        );

        assert!(state.claimed.contains(&initial.canonicalize().unwrap()));
        assert!(state.claimed.contains(&subdir.canonicalize().unwrap()));
    }
}