chappi-core 0.1.0

Typed cognitive graph, patch, reentry, projection, and measurement engine for Chappi
Documentation
use chappi_core::{CognitiveState, TransitionKind, validate};
use chappi_syntax::parse_document;

fn decode(source: &str) -> CognitiveState {
    CognitiveState::try_from(parse_document(source).expect("fixture syntax should parse"))
        .expect("fixture semantics should decode")
}

#[test]
fn decodes_focus_streams_in_canonical_order_and_defaults_node_revisions() {
    let state = decode(
        r#"(focus memory 1 self associated 0 (fg c2) (bg self) (residue c1) (trigger recalled))
(chappi 1)
(claim c2 self working 60 likely (second))
(entity self self persistent 100 person (self))
(focus memory 0 self evoked none (fg c1) (bg c2) (residue) (trigger noticed))
(claim c1 self working 70 likely (first))
(focus work 0 self evoked none (fg c2) (bg) (residue c1) (trigger assigned))
"#,
    );

    assert!(state.nodes.iter().all(|node| node.revision == 0));
    assert_eq!(
        state
            .focus_streams
            .iter()
            .map(|stream| stream.id.as_str())
            .collect::<Vec<_>>(),
        ["memory", "work"]
    );
    assert_eq!(
        state.focus_streams[0]
            .frames
            .iter()
            .map(|frame| frame.sequence)
            .collect::<Vec<_>>(),
        [0, 1]
    );
    assert_eq!(
        state.focus_streams[0].frames[1].transition,
        TransitionKind::Associated
    );
    assert_eq!(state.focus_streams[0].frames[1].previous_sequence, Some(0));
    assert_eq!(state.focus_streams[0].frames[1].foreground, ["c2"]);
    assert!(validate(&state).is_empty());
}

#[test]
fn decodes_every_focus_transition_kind() {
    let state = decode(
        r#"(chappi 1)
(entity self self persistent 100 person (self))
(claim c self working 70 likely (claim))
(focus thought 0 self evoked none (fg c) (bg) (residue) (trigger start))
(focus thought 1 self associated 0 (fg c) (bg) (residue) (trigger link))
(focus thought 2 self interrupted 1 (fg self) (bg c) (residue) (trigger noise))
(focus thought 3 self returned 2 (fg c) (bg self) (residue) (trigger quiet))
(focus thought 4 self reframed 3 (fg c) (bg) (residue self) (trigger insight))
(focus thought 5 self resolved 4 (fg) (bg c) (residue self) (trigger done))
"#,
    );

    assert_eq!(
        state.focus_streams[0]
            .frames
            .iter()
            .map(|frame| frame.transition)
            .collect::<Vec<_>>(),
        [
            TransitionKind::Evoked,
            TransitionKind::Associated,
            TransitionKind::Interrupted,
            TransitionKind::Returned,
            TransitionKind::Reframed,
            TransitionKind::Resolved,
        ]
    );
    assert!(validate(&state).is_empty());
}

#[test]
fn reports_focus_sequence_reference_and_bucket_invariant_failures() {
    let state = decode(
        r#"(chappi 1)
(entity self self persistent 100 person (self))
(claim c self working 70 likely (claim))
(focus bad-start 1 self associated 0 (fg c c) (bg c missing) (residue self) (trigger bad))
(focus bad-next 0 self evoked none (fg c) (bg) (residue) (trigger start))
(focus bad-next 2 self returned 0 (fg c) (bg) (residue) (trigger skipped))
(focus bad-owner 0 missing evoked none (fg c) (bg) (residue) (trigger absent))
"#,
    );

    let diagnostics = validate(&state);
    let codes = diagnostics
        .iter()
        .map(|diagnostic| diagnostic.code.as_str())
        .collect::<Vec<_>>();

    assert!(codes.contains(&"invalid-focus-start"));
    assert!(codes.contains(&"duplicate-focus-reference"));
    assert!(codes.contains(&"overlapping-focus-reference"));
    assert!(codes.contains(&"undefined-focus-reference"));
    assert!(codes.contains(&"invalid-focus-sequence"));
    assert!(codes.contains(&"undefined-focus-owner"));
}

#[test]
fn distinguishes_bucket_duplicates_from_cross_bucket_overlap() {
    let state = decode(
        r#"(chappi 1)
(entity self self persistent 100 person (self))
(claim c self working 70 likely (claim))
(focus work 0 self evoked none (fg c c) (bg) (residue) (trigger duplicate))
"#,
    );

    let diagnostics = validate(&state);

    assert_eq!(
        diagnostics
            .iter()
            .filter(|item| item.code == "duplicate-focus-reference")
            .count(),
        1
    );
    assert!(
        !diagnostics
            .iter()
            .any(|item| item.code == "overlapping-focus-reference")
    );
}

#[test]
fn rejects_duplicate_streams_and_frames_attached_to_the_wrong_stream() {
    let mut state = decode(
        r#"(chappi 1)
(entity self self persistent 100 person (self))
(focus work 0 self evoked none (fg self) (bg) (residue) (trigger start))
"#,
    );
    let duplicate = state.focus_streams[0].clone();
    state.focus_streams.push(duplicate);
    state.focus_streams[0].frames[0].stream_id = "other".into();

    let diagnostics = validate(&state);

    assert!(
        diagnostics
            .iter()
            .any(|item| item.code == "duplicate-focus-stream")
    );
    assert!(
        diagnostics
            .iter()
            .any(|item| item.code == "focus-stream-mismatch")
    );
}

#[test]
fn rejects_an_empty_programmatic_focus_stream() {
    let mut state = decode("(chappi 1)\n(entity self self persistent 100 person (self))\n");
    state.focus_streams.push(chappi_core::FocusStream {
        id: "empty".into(),
        frames: Vec::new(),
    });

    let diagnostics = validate(&state);

    assert!(
        diagnostics
            .iter()
            .any(|item| item.code == "empty-focus-stream")
    );
}