harn-vm 0.10.124

Async bytecode virtual machine for the Harn programming language
Documentation
use serde_json::json;

use super::*;

fn snapshot() -> SessionRecapSnapshot {
    SessionRecapSnapshot {
        schema_version: SESSION_RECAP_SCHEMA_VERSION,
        session_id: "session-1".to_string(),
        query: SessionRecapQuery::for_session("session-1"),
        cursor: SessionRecapCursor::default(),
        coverage: SessionRecapCoverage::default(),
        source: SessionRecapSource::default(),
        content_hash: "sha256:content".to_string(),
        projection_hash: "sha256:projection".to_string(),
        turns: vec![PromptTurnRecap {
            turn_id: "turn-1".to_string(),
            run_id: "run-1".to_string(),
            state: RecapCompletionState::Complete,
            prompts: Vec::new(),
            iterations: Vec::new(),
            terminal: None,
            source_event_ids: Vec::new(),
        }],
        extensions: Default::default(),
    }
}

fn candidate(source_projection_hash: &str) -> SessionRecapEnrichment {
    SessionRecapEnrichment {
        source_projection_hash: source_projection_hash.to_string(),
        summary: "One completed prompt turn.".to_string(),
        turn_headlines: vec![SessionRecapTurnHeadline {
            turn_id: "turn-1".to_string(),
            headline: "Finished the requested work".to_string(),
        }],
    }
}

#[test]
fn matching_bounded_enrichment_attaches_without_changing_projection_identity() {
    let mut recap = snapshot();
    let base_hash = recap.projection_hash.clone();

    assert_eq!(
        recap.apply_optional_enrichment(Some(candidate(&base_hash))),
        SessionRecapEnrichmentDisposition::Applied
    );
    assert_eq!(recap.projection_hash, base_hash);
    assert_eq!(
        recap.extensions[SESSION_RECAP_ENRICHMENT_EXTENSION]["sourceProjectionHash"],
        json!(base_hash)
    );
    assert_eq!(
        recap.extensions[SESSION_RECAP_ENRICHMENT_EXTENSION]["turnHeadlines"][0]["turnId"],
        json!("turn-1")
    );
}

#[test]
fn stale_enrichment_falls_back_to_the_unchanged_deterministic_recap() {
    let mut recap = snapshot();
    let before = recap.clone();

    assert_eq!(
        recap.apply_optional_enrichment(Some(candidate("sha256:stale"))),
        SessionRecapEnrichmentDisposition::DeterministicFallback {
            reason: SessionRecapEnrichmentFallbackReason::ProjectionMismatch,
        }
    );
    assert_eq!(recap, before);
}

#[test]
fn malformed_or_unbounded_enrichment_cannot_displace_the_base_recap() {
    let cases = [
        (
            SessionRecapEnrichment {
                summary: " ".to_string(),
                ..candidate("sha256:projection")
            },
            SessionRecapEnrichmentFallbackReason::InvalidContent,
        ),
        (
            SessionRecapEnrichment {
                summary: "x".repeat(4_097),
                ..candidate("sha256:projection")
            },
            SessionRecapEnrichmentFallbackReason::BoundExceeded,
        ),
        (
            SessionRecapEnrichment {
                turn_headlines: vec![SessionRecapTurnHeadline {
                    turn_id: "unknown-turn".to_string(),
                    headline: "Invented grouping".to_string(),
                }],
                ..candidate("sha256:projection")
            },
            SessionRecapEnrichmentFallbackReason::InvalidContent,
        ),
        (
            SessionRecapEnrichment {
                turn_headlines: vec![
                    SessionRecapTurnHeadline {
                        turn_id: "turn-1".to_string(),
                        headline: "First headline".to_string(),
                    },
                    SessionRecapTurnHeadline {
                        turn_id: "turn-1".to_string(),
                        headline: "Conflicting headline".to_string(),
                    },
                ],
                ..candidate("sha256:projection")
            },
            SessionRecapEnrichmentFallbackReason::InvalidContent,
        ),
    ];

    for (candidate, reason) in cases {
        let mut recap = snapshot();
        let before = recap.clone();
        assert_eq!(
            recap.apply_optional_enrichment(Some(candidate)),
            SessionRecapEnrichmentDisposition::DeterministicFallback { reason }
        );
        assert_eq!(recap, before);
    }
}

#[test]
fn missing_enrichment_is_an_explicit_spend_free_fallback() {
    let mut recap = snapshot();
    let before = recap.clone();
    assert_eq!(
        recap.apply_optional_enrichment(None),
        SessionRecapEnrichmentDisposition::DeterministicFallback {
            reason: SessionRecapEnrichmentFallbackReason::NotRequested,
        }
    );
    assert_eq!(recap, before);
}

#[test]
fn enrichment_text_is_redacted_before_it_becomes_public_recap_data() {
    let mut recap = snapshot();
    let projection_hash = recap.projection_hash.clone();
    let mut enrichment = candidate(&projection_hash);
    enrichment.summary = "Used sk-proj-abcdefghijklmnopqrstuvwxyz1234567890 to finish.".to_string();
    enrichment.turn_headlines[0].headline =
        "Token sk-proj-abcdefghijklmnopqrstuvwxyz1234567890 worked".to_string();

    assert_eq!(
        recap.apply_optional_enrichment(Some(enrichment)),
        SessionRecapEnrichmentDisposition::Applied
    );
    let public = &recap.extensions[SESSION_RECAP_ENRICHMENT_EXTENSION];
    assert!(!public.to_string().contains("abcdefghijklmnopqrstuvwxyz"));
    assert!(public.to_string().contains("<redacted:openai_key:"));
}