ralph-workflow 0.7.18

PROMPT-driven multi-agent orchestrator for git repos
Documentation
#[test]
fn merge_delta_tracks_first_delta_and_accumulates_content() {
    let mut accumulated = std::collections::HashMap::new();
    let mut key_order = Vec::new();
    let mut output_started_for_key = std::collections::HashSet::new();

    let first = merge_delta(
        &mut accumulated,
        &mut key_order,
        &mut output_started_for_key,
        ContentType::Thinking,
        "reasoning",
        "Hello",
    );
    let second = merge_delta(
        &mut accumulated,
        &mut key_order,
        &mut output_started_for_key,
        ContentType::Thinking,
        "reasoning",
        " World",
    );

    assert!(first);
    assert!(!second);
    assert_eq!(
        accumulated.get(&(ContentType::Thinking, "reasoning".to_string())),
        Some(&"Hello World".to_string())
    );
    assert_eq!(
        key_order,
        vec![(ContentType::Thinking, "reasoning".to_string())]
    );
}

#[test]
fn sorted_content_keys_orders_numeric_then_lexical() {
    let mut accumulated = std::collections::HashMap::new();
    accumulated.insert((ContentType::Text, "10".to_string()), "a".to_string());
    accumulated.insert((ContentType::Text, "2".to_string()), "b".to_string());
    accumulated.insert((ContentType::Text, "alpha".to_string()), "c".to_string());
    accumulated.insert(
        (ContentType::Thinking, "0".to_string()),
        "ignored".to_string(),
    );

    let keys = sorted_content_keys(&accumulated, ContentType::Text);

    assert_eq!(
        keys,
        vec!["2".to_string(), "10".to_string(), "alpha".to_string()]
    );
}

#[test]
fn build_tool_use_reconstruction_prefers_hints_then_session_names() {
    let mut accumulated = std::collections::HashMap::new();
    accumulated.insert((ContentType::ToolInput, "0".to_string()), "{}".to_string());
    accumulated.insert(
        (ContentType::ToolInput, "1".to_string()),
        "{\"x\":1}".to_string(),
    );

    let mut tool_names = std::collections::HashMap::new();
    tool_names.insert(1_u64, Some("fallback".to_string()));

    let mut hints = std::collections::HashMap::new();
    hints.insert(0_usize, "hinted".to_string());

    let reconstructed = build_tool_use_reconstruction(&accumulated, &tool_names, Some(&hints));

    assert_eq!(
        reconstructed,
        "TOOL_USE:hinted:{}TOOL_USE:fallback:{\"x\":1}"
    );
}

#[test]
fn compute_hash_is_stable_for_same_input() {
    assert_eq!(compute_hash("same"), compute_hash("same"));
}

#[test]
fn compute_content_hash_from_accumulated_is_order_independent() {
    let mut first = std::collections::HashMap::new();
    first.insert((ContentType::Text, "1".to_string()), "alpha".to_string());
    first.insert((ContentType::ToolInput, "2".to_string()), "{}".to_string());

    let mut second = std::collections::HashMap::new();
    second.insert((ContentType::ToolInput, "2".to_string()), "{}".to_string());
    second.insert((ContentType::Text, "1".to_string()), "alpha".to_string());

    assert_eq!(
        compute_content_hash_from_accumulated(&first),
        compute_content_hash_from_accumulated(&second)
    );
}

#[test]
fn is_duplicate_text_content_matches_concatenated_text_stream() {
    let mut accumulated = std::collections::HashMap::new();
    accumulated.insert((ContentType::Text, "0".to_string()), "Hello".to_string());
    accumulated.insert((ContentType::Text, "1".to_string()), " World".to_string());

    assert!(is_duplicate_text_content(&accumulated, "Hello World"));
    assert!(!is_duplicate_text_content(&accumulated, "Hello there"));
}

#[test]
fn build_mixed_content_reconstruction_orders_text_then_tool_use() {
    let mut accumulated = std::collections::HashMap::new();
    accumulated.insert((ContentType::Text, "0".to_string()), "Answer ".to_string());
    accumulated.insert((ContentType::ToolInput, "0".to_string()), "{}".to_string());
    accumulated.insert(
        (ContentType::Thinking, "0".to_string()),
        "hidden".to_string(),
    );

    let mut tool_names = std::collections::HashMap::new();
    tool_names.insert(0_u64, Some("lookup".to_string()));

    let reconstructed = build_mixed_content_reconstruction(&accumulated, &tool_names, None);

    assert_eq!(reconstructed, "Answer TOOL_USE:lookup:{}");
}

#[test]
fn snapshot_helpers_detect_and_extract_new_suffix() {
    let previous = "0123456789012345678901234567890123456789";
    let snapshot = "0123456789012345678901234567890123456789-new";

    let mut accumulated = std::collections::HashMap::new();
    accumulated.insert(
        (ContentType::Text, "main".to_string()),
        previous.to_string(),
    );

    assert!(is_likely_snapshot(&accumulated, snapshot, "main"));
    let delta_start = match extract_delta_from_snapshot(&accumulated, snapshot, "main") {
        Ok(index) => index,
        Err(error) => panic!("snapshot should produce suffix index: {error}"),
    };
    assert_eq!(&snapshot[delta_start..], "-new");
}