magi-code 0.80.1

Repository-aware CLI coding agent for terminal work
Documentation
//! Opt-in, bounded TestBackend profiles; never emit session text or identifiers.
//! Synthetic nodes bypass the session loader: this is not disk-history hydration.
use super::*;
use crate::sessions::SessionEvent;
use std::io::{BufRead, Read};

const AREA: Rect = Rect::new(0, 0, 120, 36);
const FRAMES: usize = 30;
const TOTAL_NODES: usize = 30_000;
const BATCHES: usize = 120;
const TASKS_PER_BATCH: usize = 10;
const MAX_SESSION_BYTES: u64 = 16 * 1024 * 1024;
const MAX_SESSION_EVENTS: usize = 20_000;
const PREVIEWS: [&str; 4] = [
    "# Inspection\n\n- checked **synthetic** input\n- no external work\n",
    "```rust\nfn example() -> usize {\n    42\n}\n```\n",
    "diff --git a/example.rs b/example.rs\n--- a/example.rs\n+++ b/example.rs\n@@ -1 +1 @@\n-old\n+new\n",
    "stdout:\nsynthetic check started\nsynthetic check finished\nexit status: 0\n",
];

fn require_test_backend() {
    // Reject real-terminal output even when the parent harness is configured for it.
    // Do not call from_env here: its unknown-value warning echoes arbitrary input.
    let backend = std::env::var_os("PROFILE_TUI_RENDER_BACKEND");
    assert!(
        backend
            .as_deref()
            .is_none_or(|value| value == "test" || value.is_empty()),
        "mixed/private profiles require the test backend"
    );
}

fn add_synthetic_node(
    state: &mut MissionControlState,
    id: ActivityId,
    parent_id: Option<ActivityId>,
    kind: ActivityKind,
    index: usize,
) {
    let status = match index % 3 {
        0 => ActivityStatus::Success,
        1 => ActivityStatus::Failed, // The existing error status is named Failed.
        _ => ActivityStatus::Running,
    };
    let label = match kind {
        ActivityKind::Tool => ["read", "bash", "hash_edit", "grep"][index % 4],
        ActivityKind::Assistant => "reasoning • synthetic inspection",
        _ => "synthetic activity",
    };
    state.apply_activity_event(ActivityEvent::Started {
        id: id.clone(),
        parent_id,
        kind,
        status,
        metadata: ActivityMetadata::new(label),
    });
    state.apply_activity_event(ActivityEvent::Delta {
        id,
        preview: PREVIEWS[index % PREVIEWS.len()].repeat(4),
    });
}

fn synthetic_massive_state() -> MissionControlState {
    let mut state = MissionControlState::default();
    // User is an OutputEvent, not an ActivityKind. Keep text in the real transcript.
    for batch_index in 0..BATCHES {
        state.apply_output_event(&OutputEvent::UserPrompt {
            text: "Inspect the synthetic example.".into(),
        });
        let batch = ActivityId::new(format!("mixed-batch-{batch_index}"));
        add_synthetic_node(
            &mut state,
            batch.clone(),
            None,
            ActivityKind::SubagentBatch,
            batch_index,
        );
        for task_index in 0..TASKS_PER_BATCH {
            let task = batch.child(format!("task-{task_index}"));
            add_synthetic_node(
                &mut state,
                task.clone(),
                Some(batch.clone()),
                ActivityKind::SubagentTask,
                task_index,
            );
            // Give every popup a real child card, not just a task title.
            add_synthetic_node(
                &mut state,
                task.child("tool"),
                Some(task),
                ActivityKind::Tool,
                task_index,
            );
        }
    }
    let hierarchy_nodes = BATCHES * (1 + TASKS_PER_BATCH * 2);
    for index in hierarchy_nodes..TOTAL_NODES {
        let kind = match index % 6 {
            0 => ActivityKind::Tool,
            1 => ActivityKind::Assistant,
            2 => ActivityKind::Hook,
            3 => ActivityKind::ProviderContextInjection,
            4 => ActivityKind::Diagnostic,
            _ => ActivityKind::Compaction,
        };
        add_synthetic_node(
            &mut state,
            ActivityId::new(format!("mixed-root-{index}")),
            None,
            kind,
            index,
        );
        if index % 120 == 0 {
            state.apply_output_event(&OutputEvent::UserPrompt {
                text: "Review the synthetic changes.".into(),
            });
            state.apply_output_event(&OutputEvent::AssistantComplete {
                text: PREVIEWS[0].into(),
            });
        }
    }
    assert_eq!(state.nodes.len(), TOTAL_NODES);
    assert_eq!(
        state
            .nodes
            .values()
            .filter(|node| node.kind == ActivityKind::SubagentTask)
            .count(),
        1_200
    );
    state
}

fn print_state_counts(source: &'static str, state: &MissionControlState) {
    // Debug formatting is restricted to this fieldless enum, never a node or state.
    let mut kinds = BTreeMap::new();
    for kind in [
        ActivityKind::Tool,
        ActivityKind::Hook,
        ActivityKind::ProviderContextInjection,
        ActivityKind::SubagentBatch,
        ActivityKind::SubagentTask,
        ActivityKind::Assistant,
        ActivityKind::Diagnostic,
        ActivityKind::Compaction,
    ] {
        kinds.insert(format!("{kind:?}"), 0usize);
    }
    for node in state.nodes.values() {
        *kinds.entry(format!("{:?}", node.kind)).or_default() += 1;
    }
    println!(
        "mixed_render source={source} nodes={} kinds={kinds:?} retained_transcript_entries={}",
        state.nodes.len(),
        state.transcript.len()
    );
}

fn representative_selection_indices(state: &MissionControlState) -> Vec<usize> {
    let mut kinds = Vec::new();
    state
        .cached_visible_nodes()
        .iter()
        .enumerate()
        .filter_map(|(index, (_, id))| {
            let kind = &state.nodes.get(id)?.kind;
            if kinds.contains(kind) {
                None
            } else {
                kinds.push(kind.clone());
                Some(index)
            }
        })
        .collect()
}

fn open_representative_popup(state: &mut MissionControlState) -> bool {
    // Follow stored hierarchy order, not randomized HashMap order.
    let pair = state.roots.iter().find_map(|batch| {
        if state.nodes.get(batch)?.kind != ActivityKind::SubagentBatch {
            return None;
        }
        let task = state.children.get(batch)?.iter().find(|task| {
            state
                .nodes
                .get(*task)
                .is_some_and(|node| node.kind == ActivityKind::SubagentTask)
        })?;
        Some((batch.clone(), task.clone()))
    });
    pair.is_some_and(|(batch, task)| state.open_subagent_viewer(batch, task))
}

fn run_mixed_profiles(source: &'static str, mut make_state: impl FnMut() -> MissionControlState) {
    require_test_backend();
    let mut setup = Duration::ZERO;
    let mut summaries = Vec::new();
    // The parent harness consumes state. Rebuild sequentially rather than clone a
    // huge state or hold several copies; every scenario uses the supplied fixture.
    for scenario in [
        "mixed_steady",
        "mixed_scroll_selected_detail",
        "mixed_subagent_popup",
    ] {
        let started = Instant::now();
        let mut state = make_state();
        setup += started.elapsed();
        if summaries.is_empty() {
            print_state_counts(source, &state);
        }
        let selections = representative_selection_indices(&state);
        if scenario == "mixed_subagent_popup" && !open_representative_popup(&mut state) {
            println!(
                "mixed_render source={source} scenario=mixed_subagent_popup skipped=no_subagent_batch"
            );
            continue;
        }
        if scenario == "mixed_scroll_selected_detail" {
            state.focus_activity_detail();
        }
        summaries.push(run_render_scenario(
            scenario,
            AREA,
            FRAMES,
            state,
            |state, frame, area| match scenario {
                "mixed_scroll_selected_detail" => {
                    if let Some(index) = selections.get(frame % selections.len().max(1)) {
                        state.selected = *index;
                        state.activity_follow_tail = false;
                        state.reset_detail();
                        state.ensure_activity_selection_visible(area.height / 2);
                    }
                    state.set_scroll_offset(&state.scroll_views.transcript, frame * 3);
                    state.scroll_activity_tree_up_by(3, area.height / 2);
                }
                "mixed_subagent_popup" => {
                    if let Some(timeline) = crate::tui::render::subagent_viewer_timeline_area(area)
                    {
                        state.scroll_subagent_viewer_up(
                            3,
                            timeline.height,
                            timeline.width.saturating_sub(1).max(1),
                        );
                    }
                }
                _ => {}
            },
        ));
    }
    let metric = if source == "private_session" {
        "hydration_ms"
    } else {
        "setup_ms"
    };
    println!(
        "mixed_render source={source} aggregate_{metric}={} state_builds=3",
        setup.as_millis()
    );
    assert_render_summaries(&summaries);
}

#[test]
#[ignore = "synthetic in-memory render profile; not disk-history hydration"]
fn profile_mixed_massive_activity_render() {
    run_mixed_profiles(
        "synthetic_in_memory_bypasses_session_loader",
        synthetic_massive_state,
    );
}

fn read_private_events(path: &std::ffi::OsStr) -> Vec<SessionEvent> {
    let file = fs::File::open(path)
        .unwrap_or_else(|_| panic!("private profile input could not be opened"));
    let metadata = file
        .metadata()
        .unwrap_or_else(|_| panic!("private profile metadata unavailable"));
    assert!(
        metadata.is_file(),
        "private profile input must be a regular file"
    );
    assert!(
        metadata.len() <= MAX_SESSION_BYTES,
        "private profile exceeds byte limit"
    );
    let mut bytes = Vec::new();
    file.take(MAX_SESSION_BYTES)
        .read_to_end(&mut bytes)
        .unwrap_or_else(|_| panic!("private profile read failed"));
    assert_eq!(
        bytes.len() as u64,
        metadata.len(),
        "private profile input changed during read"
    );
    let mut events = Vec::new();
    for line in bytes.as_slice().lines() {
        let line = line.unwrap_or_else(|_| panic!("private profile line decoding failed"));
        if line.trim().is_empty() {
            continue;
        }
        assert!(
            events.len() < MAX_SESSION_EVENTS,
            "private profile exceeds event limit"
        );
        let event = serde_json::from_str::<SessionEvent>(&line)
            .unwrap_or_else(|_| panic!("private profile contains an invalid session event"));
        events.push(event);
    }
    println!(
        "mixed_render source=private_session input_events={}",
        events.len()
    );
    events
}

#[test]
#[ignore = "private render profile; requires explicit PROFILE_PRIVATE_SESSION_PATH"]
fn profile_private_session_render() {
    let Some(path) = std::env::var_os("PROFILE_PRIVATE_SESSION_PATH") else {
        println!("mixed_render source=private_session skipped=explicit_path_required");
        return;
    };
    require_test_backend();
    let events = read_private_events(&path);
    run_mixed_profiles("private_session", || {
        crate::tui::hydrate_profile_session_events(&events)
    });
}