supercode-frontend-model 0.4.8

Donor-neutral client state and semantic intents for Supercode frontends.
Documentation
use serde::Deserialize;
use serde_json::Value;
use supercode_frontend_model::{CellState, TranscriptKind, TranscriptModel};

#[derive(Deserialize)]
struct Fixture {
    history_cursor: u64,
    history: Vec<supercode::ChatMessage>,
    events: Vec<supercode::FrontendEvent>,
    expected: Vec<ExpectedCell>,
}

#[derive(Debug, Deserialize, PartialEq)]
struct ExpectedCell {
    kind: String,
    state: String,
    title: String,
    body: String,
}

fn kind_name(kind: TranscriptKind) -> &'static str {
    match kind {
        TranscriptKind::User => "user",
        TranscriptKind::Assistant => "assistant",
        TranscriptKind::Shell => "shell",
        TranscriptKind::FileRead => "file-read",
        TranscriptKind::FileWrite => "file-write",
        TranscriptKind::Patch => "diff",
        TranscriptKind::Mcp => "mcp",
        TranscriptKind::Approval => "approval",
        TranscriptKind::Subagent => "subagent",
        TranscriptKind::Scheduled => "scheduled",
        TranscriptKind::Reduction => "reduction",
        TranscriptKind::Reasoning => "reasoning",
        TranscriptKind::Notice => "notice",
        TranscriptKind::Usage => "usage",
        TranscriptKind::Generic => "generic",
    }
}

fn state_name(state: CellState) -> &'static str {
    match state {
        CellState::Pending => "pending",
        CellState::Complete => "complete",
        CellState::Failed => "failed",
    }
}

#[test]
fn terminal_and_browser_use_one_language_neutral_semantic_fixture() {
    let fixture: Fixture = serde_json::from_str(include_str!(
        "../../../sdk/frontend-browser/fixtures/semantic-conformance.json"
    ))
    .unwrap();
    let mut model = TranscriptModel::from_history(&fixture.history, fixture.history_cursor);
    for event in &fixture.events {
        assert!(model.apply_event(event));
    }
    let actual = model
        .cells()
        .iter()
        .map(|cell| ExpectedCell {
            kind: kind_name(cell.kind).into(),
            state: state_name(cell.state).into(),
            title: cell.title.clone(),
            body: cell.body.clone(),
        })
        .collect::<Vec<_>>();
    assert_eq!(actual, fixture.expected);
    assert_eq!(
        model.last_sequence(),
        fixture
            .events
            .last()
            .map(|event| event.sequence)
            .unwrap_or(fixture.history_cursor)
    );
}

#[test]
fn shared_fixture_is_contract_data_not_a_native_session() {
    let value: Value = serde_json::from_str(include_str!(
        "../../../sdk/frontend-browser/fixtures/semantic-conformance.json"
    ))
    .unwrap();
    assert_eq!(
        value["schema"],
        "supercode.frontend.semantic-conformance.v1"
    );
    assert!(value.get("source_harness").is_none());
    assert!(value.get("native_session").is_none());
}