layered-soul/skills/colibri-development/references/pi-event-state-mapping.md
Sam & Claude 4d8ce07fa7 docs: apply Prettier to current markdown (Sam & Codex)
Normalize markdown formatting after the latest main updates.\n\nChecks: python3 scripts/layered_soul.py validate .; npx --yes prettier@3 --check '**/*.md'; git diff --check.
2026-06-14 01:48:32 +02:00

2.6 KiB

Pi JSONL Event → AgentState Mapping

Known event types and their effect on apply_pi_event(current_state, event_type):

Event Type Target State Notes
session / session_started Idle (no transition unless paired) Sets pi_session_id
turn_start Working Only transitions from Idle/SessionActive
message_update Working Stays Working
turn_end Done
queue_update Blocked
tool_execution_start Working
tool_error Idle (no transition) Unrecognized, silently ignored
unknown / future current state (unchanged) Forward-compatible, no crash

Known-Good Integration Test Recipe

use colibri_glasspane::{PiJsonlIngestor, AgentState, GlasspaneSnapshot, Pane};
use std::time::{Duration, SystemTime};

#[test]
fn lifecycle_idle_to_done() {
    let mut ingestor = PiJsonlIngestor::default();
    let t = SystemTime::UNIX_EPOCH + Duration::from_secs(1716800000);

    ingestor.ingest_line_at(r#"{"type":"session","id":"s1"}"#, t);
    ingestor.ingest_line_at(r#"{"type":"turn_start"}"#, t + Duration::from_secs(1));
    ingestor.ingest_line_at(r#"{"type":"message_update"}"#, t + Duration::from_secs(2));
    ingestor.ingest_line_at(r#"{"type":"turn_end"}"#, t + Duration::from_secs(3));

    assert_eq!(ingestor.state(), AgentState::Done);
    assert_eq!(ingestor.pi_session_id(), Some("s1"));
}

Snapshot Serialization Round-Trip

let snapshot = GlasspaneSnapshot::new(
    "test-host",
    "2026-05-27T10:00:00Z",
    vec![Pane {
        id: "p1".into(),
        agent: "pi".into(),
        state: AgentState::Working,
        pi_session_id: Some("sess-1".into()),
        last_event_at: Some("2026-05-27T10:00:00Z".into()),
        cwd: None,
        stalled: false,
    }],
);
let json = serde_json::to_string(&snapshot).unwrap();
let parsed: GlasspaneSnapshot = serde_json::from_str(&json).unwrap();
assert_eq!(parsed.count(AgentState::Working), 1);

Serialized states are lowercase: "working", "idle", "blocked", "done", "error".