use std::collections::{HashMap, VecDeque};
use std::path::{Path, PathBuf};
use std::sync::{Mutex, MutexGuard, PoisonError};
use std::time::Duration;
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use tokio::sync::watch;
use crate::utils::awake_clock::AwakeClock;
#[cfg(unix)]
pub mod codex_app_server;
pub mod codex_watcher;
#[cfg(unix)]
pub mod journal;
#[cfg(unix)]
pub(crate) mod journal_watcher;
pub(crate) mod pid_liveness;
pub(crate) mod pid_watcher;
pub mod relocate;
pub mod stream;
pub mod watcher;
const DEFAULT_SESSION_TTL: Duration = Duration::from_secs(300);
const ENDED_SESSION_TTL: Duration = Duration::from_secs(10);
const DEFAULT_WINDOW_TTL: Duration = Duration::from_secs(30);
const MAX_SESSIONS: usize = 512;
const MAX_REPLACED_PIDS: usize = 8;
const MAX_WINDOWS: usize = 256;
const SOCKET_REORDER_WINDOW: chrono::Duration = chrono::Duration::seconds(10);
#[must_use]
pub fn is_session_uuid(id: &str) -> bool {
let mut groups = id.split('-');
[8, 4, 4, 4, 12].into_iter().all(|len| {
groups
.next()
.is_some_and(|g| g.len() == len && g.bytes().all(|b| b.is_ascii_hexdigit()))
}) && groups.next().is_none()
}
const MAX_RECENT_SEQS: usize = 32;
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct EventStamp {
pub ts: DateTime<Utc>,
pub seq: String,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum Origin {
Socket,
Local,
Journal,
Replay,
}
impl Origin {
fn is_journal(self) -> bool {
matches!(self, Self::Journal | Self::Replay)
}
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize)]
pub struct DeliveryStats {
pub socket: u64,
pub recovered: u64,
pub superseded: u64,
pub replayed: u64,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum Correction {
None,
RacedByJournal,
LateAfterSuperseded,
Superseded,
}
#[derive(Debug, Default)]
struct DeliveryCounters {
socket: std::sync::atomic::AtomicU64,
recovered: std::sync::atomic::AtomicU64,
superseded: std::sync::atomic::AtomicU64,
replayed: std::sync::atomic::AtomicU64,
}
impl DeliveryCounters {
fn record(&self, origin: Origin, applied: bool, correction: Correction) {
use std::sync::atomic::Ordering::Relaxed;
let route = match origin {
Origin::Socket => Some(&self.socket),
Origin::Journal => Some(&self.recovered),
Origin::Replay => Some(&self.replayed),
Origin::Local => None,
};
if let (true, Some(counter)) = (applied, route) {
counter.fetch_add(1, Relaxed);
}
match correction {
Correction::None => {}
Correction::RacedByJournal => saturating_decrement(&self.recovered),
Correction::LateAfterSuperseded => saturating_decrement(&self.superseded),
Correction::Superseded => {
self.superseded.fetch_add(1, Relaxed);
}
}
}
fn snapshot(&self) -> DeliveryStats {
use std::sync::atomic::Ordering::Relaxed;
DeliveryStats {
socket: self.socket.load(Relaxed),
recovered: self.recovered.load(Relaxed),
superseded: self.superseded.load(Relaxed),
replayed: self.replayed.load(Relaxed),
}
}
}
fn saturating_decrement(counter: &std::sync::atomic::AtomicU64) {
use std::sync::atomic::Ordering::Relaxed;
let mut current = counter.load(Relaxed);
while current > 0 {
match counter.compare_exchange_weak(current, current - 1, Relaxed, Relaxed) {
Ok(_) => break,
Err(actual) => current = actual,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum SessionState {
Starting,
Working,
Idle,
WaitingForInput,
WaitingForPermission,
Ended,
}
impl SessionState {
#[must_use]
pub fn for_event(event: &SessionEvent, current: Option<Self>) -> Self {
match event {
SessionEvent::SessionStart => Self::Starting,
SessionEvent::UserPromptSubmit
| SessionEvent::PreToolUse
| SessionEvent::PostToolUse => Self::Working,
SessionEvent::TranscriptGrew | SessionEvent::RolloutTurn(RolloutTurn::Open) => {
match current {
Some(
held @ (Self::Starting
| Self::WaitingForInput
| Self::WaitingForPermission
| Self::Ended),
) => held,
_ => Self::Working,
}
}
SessionEvent::RolloutTurn(RolloutTurn::Closed) => match current {
Some(Self::Working) | None => Self::Idle,
Some(other) => other,
},
SessionEvent::Stop => Self::Idle,
SessionEvent::StreamState(state) => *state,
SessionEvent::Notification(NotificationKind::PermissionPrompt) => {
Self::WaitingForPermission
}
SessionEvent::Notification(
NotificationKind::IdlePrompt | NotificationKind::AgentNeedsInput,
) => Self::WaitingForInput,
SessionEvent::Notification(NotificationKind::Other)
| SessionEvent::TranscriptDiscovered => current.unwrap_or(Self::Idle),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum NotificationKind {
PermissionPrompt,
IdlePrompt,
AgentNeedsInput,
Other,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum RolloutTurn {
Open,
Closed,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum SessionEvent {
SessionStart,
UserPromptSubmit,
PreToolUse,
PostToolUse,
Stop,
Notification(NotificationKind),
TranscriptGrew,
TranscriptDiscovered,
RolloutTurn(RolloutTurn),
StreamState(SessionState),
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(tag = "kind", rename_all = "snake_case")]
pub enum Source {
Terminal,
VsCode {
window_key: String,
},
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum Agent {
#[default]
Claude,
Pi,
Codex,
}
impl Agent {
#[must_use]
pub fn display_name(self) -> &'static str {
match self {
Self::Claude => "Claude",
Self::Pi => "pi",
Self::Codex => "Codex",
}
}
#[must_use]
pub fn is_claude(&self) -> bool {
*self == Self::Claude
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ObserveRequest {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub agent_id: Option<String>,
pub session_id: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub cwd: Option<PathBuf>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub transcript_path: Option<PathBuf>,
pub event: SessionEvent,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub repo: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub model: Option<String>,
#[serde(default, skip_serializing_if = "Agent::is_claude")]
pub agent: Agent,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub pid: Option<u32>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct WindowReport {
pub key: String,
#[serde(default)]
pub folders: Vec<PathBuf>,
#[serde(default)]
pub tabs: usize,
#[serde(default)]
pub terminals: usize,
}
impl WindowReport {
#[must_use]
fn has_embedding(&self) -> bool {
self.tabs > 0 || self.terminals > 0
}
}
#[derive(Debug, Clone, Serialize)]
pub struct SessionEntry {
#[serde(skip)]
pub(crate) subagent_base: Option<SessionState>,
#[serde(skip)]
pub(crate) subagent_waits: HashMap<String, SessionState>,
pub session_id: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub cwd: Option<PathBuf>,
#[serde(skip_serializing_if = "Option::is_none")]
pub transcript_path: Option<PathBuf>,
#[serde(skip_serializing_if = "Option::is_none")]
pub repo: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub model: Option<String>,
pub agent: Agent,
pub state: SessionState,
pub source: Source,
pub last_event: SessionEvent,
pub started_at: DateTime<Utc>,
pub last_seen: DateTime<Utc>,
#[serde(skip)]
pub(crate) last_active: Duration,
#[serde(skip)]
pub(crate) pid: Option<u32>,
#[serde(skip)]
pub(crate) pid_start: Option<String>,
#[serde(skip)]
pub(crate) prompted: bool,
#[serde(skip)]
pub(crate) streamed: bool,
#[serde(skip)]
pub(crate) replaced_pids: VecDeque<u32>,
#[serde(skip)]
pub(crate) recent_seqs: VecDeque<(String, Origin)>,
#[serde(skip)]
pub(crate) superseded_seqs: VecDeque<String>,
#[serde(skip)]
pub(crate) latest_stamp_ts: Option<DateTime<Utc>>,
#[serde(skip)]
pub(crate) reported: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum Skip {
Duplicate,
JournalStale,
SocketReordered,
}
impl Skip {
fn outcome(self) -> &'static str {
match self {
Self::Duplicate => "duplicate_ignored",
Self::JournalStale => "journal_stale_ignored",
Self::SocketReordered => "socket_reordered_ignored",
}
}
}
impl SessionEntry {
fn stamp_skip(&self, stamp: Option<&EventStamp>, origin: Origin) -> Option<Skip> {
let stamp = stamp?;
if self.seq_origin(&stamp.seq).is_some() {
return Some(Skip::Duplicate);
}
let behind = self.latest_stamp_ts.map(|latest| latest - stamp.ts);
match (origin, behind) {
(Origin::Journal | Origin::Replay, Some(behind))
if behind > chrono::Duration::zero() =>
{
Some(Skip::JournalStale)
}
(Origin::Socket, Some(behind))
if behind > chrono::Duration::zero() && behind <= SOCKET_REORDER_WINDOW =>
{
Some(Skip::SocketReordered)
}
_ => None,
}
}
fn seq_origin(&self, seq: &str) -> Option<Origin> {
self.recent_seqs
.iter()
.find(|(recent, _)| recent == seq)
.map(|(_, origin)| *origin)
}
fn settle_stamp(
&mut self,
stamp: &EventStamp,
origin: Origin,
skip: Option<Skip>,
) -> Correction {
let correction =
if origin == Origin::Socket && self.seq_origin(&stamp.seq) == Some(Origin::Journal) {
self.confirm_by_socket(&stamp.seq);
Correction::RacedByJournal
} else if origin == Origin::Socket && self.forget_superseded(&stamp.seq) {
Correction::LateAfterSuperseded
} else if origin == Origin::Journal
&& skip == Some(Skip::JournalStale)
&& self.remember_superseded(&stamp.seq)
{
Correction::Superseded
} else {
Correction::None
};
if matches!(skip, None | Some(Skip::SocketReordered)) {
self.record_stamp(stamp, origin);
}
correction
}
fn confirm_by_socket(&mut self, seq: &str) {
if let Some((_, origin)) = self.recent_seqs.iter_mut().find(|(s, _)| s == seq) {
*origin = Origin::Socket;
}
}
fn remember_superseded(&mut self, seq: &str) -> bool {
if self.superseded_seqs.iter().any(|s| s == seq) {
return false;
}
if self.superseded_seqs.len() >= MAX_RECENT_SEQS {
self.superseded_seqs.pop_front();
}
self.superseded_seqs.push_back(seq.to_owned());
true
}
fn forget_superseded(&mut self, seq: &str) -> bool {
let Some(at) = self.superseded_seqs.iter().position(|s| s == seq) else {
return false;
};
self.superseded_seqs.remove(at);
true
}
fn record_stamp(&mut self, stamp: &EventStamp, origin: Origin) {
if self.recent_seqs.len() >= MAX_RECENT_SEQS {
self.recent_seqs.pop_front();
}
self.recent_seqs.push_back((stamp.seq.clone(), origin));
self.latest_stamp_ts = Some(self.latest_stamp_ts.map_or(stamp.ts, |t| t.max(stamp.ts)));
}
}
fn seen_at(stamp: Option<&EventStamp>, origin: Origin, now: DateTime<Utc>) -> DateTime<Utc> {
match (origin, stamp) {
(Origin::Journal | Origin::Replay, Some(stamp)) => stamp.ts.min(now),
_ => now,
}
}
fn asserts_state(event: SessionEvent) -> bool {
!matches!(
event,
SessionEvent::TranscriptDiscovered
| SessionEvent::TranscriptGrew
| SessionEvent::RolloutTurn(_)
)
}
fn open_turn_over_reported_idle(entry: &SessionEntry, event: SessionEvent) -> bool {
event == SessionEvent::RolloutTurn(RolloutTurn::Open)
&& entry.state == SessionState::Idle
&& entry.reported
}
fn subagent_state(
entry: &mut SessionEntry,
event: SessionEvent,
agent_id: Option<&str>,
) -> SessionState {
if let Some(id) = agent_id.filter(|id| !id.trim().is_empty()) {
match event {
SessionEvent::Notification(
NotificationKind::PermissionPrompt
| NotificationKind::IdlePrompt
| NotificationKind::AgentNeedsInput,
) => {
entry.subagent_base.get_or_insert(entry.state);
entry
.subagent_waits
.insert(id.to_owned(), SessionState::for_event(&event, None));
}
SessionEvent::PostToolUse => {
entry.subagent_waits.remove(id);
}
_ => {}
}
if entry
.subagent_waits
.values()
.any(|s| *s == SessionState::WaitingForPermission)
{
return SessionState::WaitingForPermission;
}
if !entry.subagent_waits.is_empty() {
return SessionState::WaitingForInput;
}
return entry.subagent_base.take().unwrap_or(entry.state);
}
let next = SessionState::for_event(&event, Some(entry.state));
if !matches!(
event,
SessionEvent::TranscriptDiscovered
| SessionEvent::TranscriptGrew
| SessionEvent::RolloutTurn(_)
| SessionEvent::Notification(NotificationKind::Other)
) {
entry.subagent_waits.clear();
entry.subagent_base = None;
}
next
}
#[derive(Debug, Clone)]
pub(crate) struct PidCandidate {
pub(crate) session_id: String,
pub(crate) pid: u32,
pub(crate) pid_start: Option<String>,
pub(crate) prompted: bool,
pub(crate) streamed: bool,
pub(crate) last_seen: DateTime<Utc>,
}
#[derive(Debug, Clone)]
struct WindowEntry {
report: WindowReport,
last_active: Duration,
registered_at: DateTime<Utc>,
}
pub struct SessionsRegistry {
sessions: Mutex<HashMap<String, SessionEntry>>,
windows: Mutex<HashMap<String, WindowEntry>>,
session_ttl: Duration,
ended_ttl: Duration,
window_ttl: Duration,
clock: AwakeClock,
changes: watch::Sender<u64>,
delivery: DeliveryCounters,
}
impl SessionsRegistry {
#[must_use]
pub fn new() -> Self {
Self {
sessions: Mutex::new(HashMap::new()),
windows: Mutex::new(HashMap::new()),
session_ttl: DEFAULT_SESSION_TTL,
ended_ttl: ENDED_SESSION_TTL,
window_ttl: DEFAULT_WINDOW_TTL,
clock: AwakeClock::new(),
changes: watch::channel(0).0,
delivery: DeliveryCounters::default(),
}
}
#[must_use]
pub fn delivery(&self) -> DeliveryStats {
self.delivery.snapshot()
}
#[must_use]
pub(crate) fn scratch() -> Self {
Self {
session_ttl: Duration::MAX,
ended_ttl: Duration::MAX,
window_ttl: Duration::MAX,
..Self::new()
}
}
pub(crate) fn live_session_ids(&self) -> std::collections::HashSet<String> {
self.lock_sessions()
.values()
.filter(|entry| entry.state != SessionState::Ended)
.map(|entry| entry.session_id.to_ascii_lowercase())
.collect()
}
#[must_use]
pub fn subscribe_changes(&self) -> watch::Receiver<u64> {
self.changes.subscribe()
}
pub(crate) fn bump(&self) {
self.changes.send_modify(|v| *v = v.wrapping_add(1));
}
fn lock_sessions(&self) -> MutexGuard<'_, HashMap<String, SessionEntry>> {
self.sessions.lock().unwrap_or_else(PoisonError::into_inner)
}
fn lock_windows(&self) -> MutexGuard<'_, HashMap<String, WindowEntry>> {
self.windows.lock().unwrap_or_else(PoisonError::into_inner)
}
fn settle_delivery(
&self,
sessions: &mut HashMap<String, SessionEntry>,
session_id: &str,
stamp: Option<&EventStamp>,
origin: Origin,
skip: Option<Skip>,
applied: bool,
) {
let correction = match (stamp, sessions.get_mut(session_id)) {
(Some(stamp), Some(entry)) => entry.settle_stamp(stamp, origin, skip),
_ => Correction::None,
};
self.delivery.record(origin, applied, correction);
}
pub fn observe(&self, req: ObserveRequest) {
self.observe_stamped(req, None, Origin::Local);
}
pub(crate) fn observe_stamped(
&self,
req: ObserveRequest,
stamp: Option<EventStamp>,
origin: Origin,
) {
let agent_id = req
.agent_id
.as_deref()
.filter(|id| req.agent == Agent::Claude && !id.trim().is_empty());
let session_id = req.session_id.clone();
let event = req.event;
let agent = req.agent;
let pid = req.pid;
let now = Utc::now();
let seen = seen_at(stamp.as_ref(), origin, now);
let awake = self.clock.now();
let (changed, old_state, new_state, outcome, reaped) = {
let mut sessions = self.lock_sessions();
let reaped = reap_sessions(&mut sessions, self.session_ttl, self.ended_ttl, awake);
let old_state = sessions.get(&session_id).map(|entry| entry.state);
let skip = sessions
.get(&session_id)
.and_then(|entry| entry.stamp_skip(stamp.as_ref(), origin));
let mut outcome = "created";
let mutated = match sessions.get_mut(&req.session_id) {
Some(_) if skip.is_some() => {
outcome = skip.map_or("skipped", Skip::outcome);
false
}
Some(entry)
if entry.state == SessionState::Ended
&& (matches!(
req.event,
SessionEvent::TranscriptDiscovered | SessionEvent::RolloutTurn(_)
) || agent_id.is_some()) =>
{
outcome = "ended_passive_ignored";
false
}
Some(entry)
if req
.pid
.is_some_and(|pid| entry.replaced_pids.contains(&pid)) =>
{
outcome = "replaced_pid_ignored";
false
}
Some(entry) => {
track_pid(entry, req.pid);
entry.prompted |= req.event == SessionEvent::UserPromptSubmit;
entry.streamed |= is_claude_stream_report(&req);
let next = if open_turn_over_reported_idle(entry, req.event) {
entry.state
} else {
subagent_state(entry, req.event, agent_id)
};
let state_changed = next != entry.state;
if asserts_state(req.event) {
entry.reported = true;
} else if state_changed {
entry.reported = false;
}
entry.state = next;
entry.last_event = req.event;
entry.last_seen = if origin.is_journal() {
entry.last_seen.max(seen)
} else {
now
};
entry.last_active = awake;
let filled_cwd = fill(&mut entry.cwd, req.cwd);
let filled_transcript = fill(&mut entry.transcript_path, req.transcript_path);
let filled_repo = fill(&mut entry.repo, req.repo);
let filled_model = fill(&mut entry.model, req.model);
let metadata_changed =
filled_cwd || filled_transcript || filled_repo || filled_model;
outcome = if state_changed {
"state_changed"
} else if metadata_changed {
"metadata_enriched"
} else {
"heartbeat_only"
};
state_changed || metadata_changed
}
None => {
if sessions.len() >= MAX_SESSIONS {
evict_oldest_session(&mut sessions);
}
let state = if agent_id.is_some() {
SessionState::Idle
} else {
SessionState::for_event(&req.event, None)
};
let prompted = req.event == SessionEvent::UserPromptSubmit;
let streamed = is_claude_stream_report(&req);
let session_id = req.session_id.clone();
let mut entry = SessionEntry {
subagent_base: None,
subagent_waits: HashMap::new(),
session_id: req.session_id,
cwd: req.cwd,
transcript_path: req.transcript_path,
repo: req.repo,
model: req.model,
agent: req.agent,
state,
source: Source::Terminal,
last_event: req.event,
started_at: seen,
last_seen: seen,
last_active: awake,
pid: req.pid,
pid_start: None,
prompted,
streamed,
replaced_pids: VecDeque::new(),
recent_seqs: VecDeque::new(),
superseded_seqs: VecDeque::new(),
latest_stamp_ts: None,
reported: asserts_state(req.event),
};
entry.state = subagent_state(&mut entry, req.event, agent_id);
sessions.insert(session_id, entry);
true
}
};
self.settle_delivery(
&mut sessions,
&session_id,
stamp.as_ref(),
origin,
skip,
skip.is_none(),
);
(
mutated || reaped > 0,
old_state,
sessions.get(&session_id).map(|entry| entry.state),
outcome,
reaped,
)
};
if changed {
self.bump();
}
tracing::debug!(%session_id, ?agent, ?pid, ?agent_id, ?event, ?old_state, ?new_state, outcome,
?origin, reaped, bumped = changed, "session_observed");
}
pub fn end(&self, session_id: &str, reason: Option<&str>, pid: Option<u32>) -> bool {
self.end_stamped(session_id, reason, pid, None, Origin::Local)
}
pub(crate) fn end_stamped(
&self,
session_id: &str,
_reason: Option<&str>,
pid: Option<u32>,
stamp: Option<EventStamp>,
origin: Origin,
) -> bool {
let now = Utc::now();
let seen = seen_at(stamp.as_ref(), origin, now);
let awake = self.clock.now();
let (known, reaped, old_state, outcome) = {
let mut sessions = self.lock_sessions();
let reaped = reap_sessions(&mut sessions, self.session_ttl, self.ended_ttl, awake);
let old_state = sessions.get(session_id).map(|entry| entry.state);
let skip = sessions
.get(session_id)
.and_then(|entry| entry.stamp_skip(stamp.as_ref(), origin));
let mut outcome = "unknown";
let known = match sessions.get_mut(session_id) {
Some(_) if skip.is_some() => {
outcome = skip.map_or("skipped", Skip::outcome);
(true, false)
}
Some(entry) if entry.state == SessionState::Ended => {
outcome = "already_ended";
(true, false)
}
Some(entry) if pid.is_some_and(|pid| entry.replaced_pids.contains(&pid)) => {
outcome = "replaced_pid_ignored";
(true, false)
}
Some(entry) => {
outcome = "ended";
entry.state = SessionState::Ended;
entry.last_event = SessionEvent::Stop;
entry.last_seen = if origin.is_journal() {
entry.last_seen.max(seen)
} else {
now
};
entry.last_active = awake;
(true, true)
}
None => (false, false),
};
self.settle_delivery(
&mut sessions,
session_id,
stamp.as_ref(),
origin,
skip,
skip.is_none() && (origin == Origin::Socket || known.0),
);
(known, reaped, old_state, outcome)
};
let (known, flipped) = known;
let bumped = flipped || reaped > 0;
if bumped {
self.bump();
}
tracing::debug!(
session_id,
?pid,
?old_state,
outcome,
?origin,
reaped,
bumped,
"session_end"
);
known
}
pub(crate) fn pid_liveness_candidates(&self) -> Vec<PidCandidate> {
self.lock_sessions()
.values()
.filter(|e| e.state != SessionState::Ended)
.filter_map(|e| {
Some(PidCandidate {
session_id: e.session_id.clone(),
pid: e.pid?,
pid_start: e.pid_start.clone(),
prompted: e.prompted,
streamed: e.streamed,
last_seen: e.last_seen,
})
})
.collect()
}
pub(crate) fn confirm_pid_liveness(
&self,
session_id: &str,
now: DateTime<Utc>,
pid_start: &str,
) {
let awake = self.clock.now();
let mut sessions = self.lock_sessions();
if let Some(entry) = sessions.get_mut(session_id) {
if entry.state != SessionState::Ended {
entry.last_seen = now;
entry.last_active = awake;
if entry.pid_start.is_none() {
entry.pid_start = Some(pid_start.to_string());
}
}
}
}
pub fn report_window(&self, report: WindowReport) {
let window_key = report.key.clone();
let folder_count = report.folders.len();
let now = Utc::now();
let awake = self.clock.now();
let (changed, outcome, reaped) = {
let mut windows = self.lock_windows();
let reaped = reap_windows(&mut windows, self.window_ttl, awake);
let outcome = if windows.contains_key(&report.key) {
"refresh"
} else {
"registered"
};
let (mutated, registered_at) = if let Some(previous) = windows.get(&report.key) {
(
previous.report.folders != report.folders
|| previous.report.has_embedding() != report.has_embedding(),
previous.registered_at,
)
} else {
if windows.len() >= MAX_WINDOWS {
evict_oldest_window(&mut windows);
}
(true, now)
};
windows.insert(
report.key.clone(),
WindowEntry {
report,
last_active: awake,
registered_at,
},
);
(
mutated || reaped > 0,
if outcome == "refresh" && mutated {
"embedding_changed"
} else {
outcome
},
reaped,
)
};
if changed {
self.bump();
}
tracing::debug!(%window_key, folder_count, outcome, reaped, bumped = changed, "session_window_reported");
}
pub fn unregister_window(&self, key: &str) -> bool {
let removed = {
let mut windows = self.lock_windows();
windows.remove(key).is_some()
};
if removed {
self.bump();
}
tracing::debug!(
window_key = key,
removed,
bumped = removed,
"session_window_unregistered"
);
removed
}
pub fn credit_window_outage(&self, outage: Duration) {
let awake = self.clock.now();
for entry in self.lock_windows().values_mut() {
entry.last_active = entry
.last_active
.checked_add(outage)
.map_or(awake, |credited| credited.min(awake));
}
}
pub fn list(&self) -> Vec<SessionEntry> {
let awake = self.clock.now();
let mut sessions: Vec<SessionEntry> = {
let mut guard = self.lock_sessions();
reap_sessions(&mut guard, self.session_ttl, self.ended_ttl, awake);
guard.values().cloned().collect()
};
let windows: Vec<WindowEntry> = {
let mut guard = self.lock_windows();
reap_windows(&mut guard, self.window_ttl, awake);
guard
.values()
.filter(|e| e.report.has_embedding())
.cloned()
.collect()
};
for session in &mut sessions {
session.source = resolve_source(session.cwd.as_deref(), &windows);
}
sessions.sort_by(|a, b| {
a.repo
.cmp(&b.repo)
.then_with(|| a.session_id.cmp(&b.session_id))
});
sessions
}
pub fn focus_folder(&self, session_id: &str) -> Option<PathBuf> {
let cwd = {
let sessions = self.lock_sessions();
sessions.get(session_id).and_then(|e| e.cwd.clone())
}?;
let awake = self.clock.now();
let mut windows = self.lock_windows();
reap_windows(&mut windows, self.window_ttl, awake);
pick_window(&cwd, windows.values().filter(|e| e.report.has_embedding()))
.and_then(|w| w.folders.first().cloned())
}
}
impl Default for SessionsRegistry {
fn default() -> Self {
Self::new()
}
}
fn track_pid(entry: &mut SessionEntry, pid: Option<u32>) {
let Some(pid) = pid else {
return;
};
if entry.pid == Some(pid) {
return;
}
if let Some(owner) = entry.pid.replace(pid) {
if entry.replaced_pids.len() >= MAX_REPLACED_PIDS {
entry.replaced_pids.pop_front();
}
entry.replaced_pids.push_back(owner);
entry.streamed = false;
}
entry.pid_start = None;
}
fn fill<T: PartialEq>(slot: &mut Option<T>, incoming: Option<T>) -> bool {
match incoming {
Some(value) if slot.as_ref() != Some(&value) => {
*slot = Some(value);
true
}
_ => false,
}
}
fn resolve_source(cwd: Option<&Path>, windows: &[WindowEntry]) -> Source {
let Some(cwd) = cwd else {
tracing::trace!(outcome = "missing_cwd", "session_attribution_miss");
return Source::Terminal;
};
if let Some(window) = pick_window(cwd, windows) {
Source::VsCode {
window_key: window.key.clone(),
}
} else {
tracing::debug!(cwd = %cwd.display(), window_count = windows.len(), "session_attribution_miss");
for window in windows {
tracing::trace!(window_key = %window.report.key, folders = ?window.report.folders, "session_attribution_candidate");
}
Source::Terminal
}
}
fn pick_window<'a>(
cwd: &Path,
windows: impl IntoIterator<Item = &'a WindowEntry>,
) -> Option<&'a WindowReport> {
windows
.into_iter()
.filter_map(|e| Some((e, match_depth(cwd, &e.report.folders)?)))
.min_by(|(a, a_depth), (b, b_depth)| {
b_depth
.cmp(a_depth)
.then_with(|| b.registered_at.cmp(&a.registered_at))
.then_with(|| a.report.key.cmp(&b.report.key))
})
.map(|(e, _)| &e.report)
}
fn match_depth(cwd: &Path, folders: &[PathBuf]) -> Option<usize> {
folders
.iter()
.filter(|f| cwd.starts_with(f))
.map(|f| f.components().count())
.max()
}
fn is_claude_stream_report(req: &ObserveRequest) -> bool {
req.agent == Agent::Claude && matches!(req.event, SessionEvent::StreamState(_))
}
fn reap_sessions(
sessions: &mut HashMap<String, SessionEntry>,
session_ttl: Duration,
ended_ttl: Duration,
awake: Duration,
) -> usize {
let before = sessions.len();
sessions.retain(|_, e| {
let max_age = if e.state == SessionState::Ended {
ended_ttl
} else {
session_ttl
};
let keep = awake.saturating_sub(e.last_active) <= max_age;
if !keep {
tracing::trace!(session_id = %e.session_id, reason = if e.state == SessionState::Ended { "ended_ttl" } else { "session_ttl" }, "session_reaped");
}
keep
});
let count = before - sessions.len();
if count > 0 {
tracing::debug!(count, "sessions_reaped");
}
count
}
fn reap_windows(
windows: &mut HashMap<String, WindowEntry>,
ttl: Duration,
awake: Duration,
) -> usize {
let before = windows.len();
windows.retain(|key, e| {
let keep = awake.saturating_sub(e.last_active) <= ttl;
if !keep {
tracing::trace!(window_key = %key, reason = "window_ttl", "session_window_reaped");
}
keep
});
let count = before - windows.len();
if count > 0 {
tracing::debug!(count, "session_windows_reaped");
}
count
}
fn evict_oldest_session(sessions: &mut HashMap<String, SessionEntry>) {
let oldest = sessions
.values()
.min_by(|a, b| {
a.last_active
.cmp(&b.last_active)
.then_with(|| a.session_id.cmp(&b.session_id))
})
.map(|e| e.session_id.clone());
if let Some(key) = oldest {
sessions.remove(&key);
tracing::debug!(session_id = %key, reason = "capacity", "session_evicted");
}
}
fn evict_oldest_window(windows: &mut HashMap<String, WindowEntry>) {
let oldest = windows
.iter()
.min_by(|a, b| {
a.1.last_active
.cmp(&b.1.last_active)
.then_with(|| a.0.cmp(b.0))
})
.map(|(k, _)| k.clone());
if let Some(key) = oldest {
windows.remove(&key);
tracing::debug!(window_key = %key, reason = "capacity", "session_window_evicted");
}
}
#[cfg(test)]
#[allow(clippy::unwrap_used, clippy::expect_used)]
mod tests {
use super::*;
#[test]
fn registry_diagnostics_distinguish_transition_heartbeat_and_unknown_end() {
let registry = SessionsRegistry::new();
let logs = crate::test_support::capture_at(tracing::Level::DEBUG, || {
registry.observe(observe_request(
"diagnostics",
SessionEvent::UserPromptSubmit,
Some("/project"),
));
registry.observe(observe_request(
"diagnostics",
SessionEvent::UserPromptSubmit,
Some("/project"),
));
registry.end("missing", None, None);
registry.end("diagnostics", None, None);
});
assert!(logs.contains("state_changed") || logs.contains("created"));
assert!(logs.contains("heartbeat_only"));
assert!(logs.contains("unknown"));
assert!(logs.contains("outcome=\"ended\""), "{logs}");
assert!(logs.contains("bumped=true"));
assert!(logs.contains("bumped=false"));
}
#[test]
fn attribution_misses_are_logged_with_each_candidate_window() {
let windows = vec![window_entry("w1", 20), window_entry("w2", 5)];
let logs = crate::test_support::capture_at(tracing::Level::TRACE, || {
assert_eq!(
resolve_source(Some(Path::new("/elsewhere/x")), &windows),
Source::Terminal
);
assert_eq!(resolve_source(None, &windows), Source::Terminal);
});
assert!(logs.contains("session_attribution_miss"), "{logs}");
assert!(logs.contains("missing_cwd"), "{logs}");
assert_eq!(
logs.matches("session_attribution_candidate").count(),
2,
"{logs}"
);
assert!(
logs.contains("window_key=w1") && logs.contains("window_key=w2"),
"{logs}"
);
}
fn observe_request(session_id: &str, event: SessionEvent, cwd: Option<&str>) -> ObserveRequest {
ObserveRequest {
agent_id: None,
pid: None,
agent: Agent::Claude,
session_id: session_id.to_string(),
cwd: cwd.map(PathBuf::from),
transcript_path: None,
event,
repo: None,
model: None,
}
}
#[test]
fn list_is_empty_initially() {
let reg = SessionsRegistry::new();
assert!(reg.list().is_empty());
}
#[test]
fn observe_then_list_round_trips_and_infers_state() {
let reg = SessionsRegistry::new();
reg.observe(observe_request(
"s1",
SessionEvent::SessionStart,
Some("/tmp/a"),
));
let sessions = reg.list();
assert_eq!(sessions.len(), 1);
assert_eq!(sessions[0].session_id, "s1");
assert_eq!(sessions[0].state, SessionState::Starting);
assert_eq!(sessions[0].source, Source::Terminal);
}
#[test]
fn observe_is_idempotent_upsert_advancing_state() {
let reg = SessionsRegistry::new();
reg.observe(observe_request(
"s1",
SessionEvent::SessionStart,
Some("/tmp/a"),
));
reg.observe(observe_request("s1", SessionEvent::PreToolUse, None));
let sessions = reg.list();
assert_eq!(sessions.len(), 1, "same session_id upserts, not duplicates");
assert_eq!(sessions[0].state, SessionState::Working);
assert_eq!(sessions[0].cwd.as_deref(), Some(Path::new("/tmp/a")));
}
#[test]
fn state_machine_covers_every_event() {
use NotificationKind::*;
use SessionEvent::*;
let cases = [
(SessionStart, SessionState::Starting),
(UserPromptSubmit, SessionState::Working),
(PreToolUse, SessionState::Working),
(PostToolUse, SessionState::Working),
(Stop, SessionState::Idle),
(
Notification(PermissionPrompt),
SessionState::WaitingForPermission,
),
(Notification(IdlePrompt), SessionState::WaitingForInput),
(Notification(AgentNeedsInput), SessionState::WaitingForInput),
(TranscriptGrew, SessionState::Working),
(TranscriptDiscovered, SessionState::Idle),
(RolloutTurn(super::RolloutTurn::Open), SessionState::Working),
(RolloutTurn(super::RolloutTurn::Closed), SessionState::Idle),
(
StreamState(SessionState::WaitingForPermission),
SessionState::WaitingForPermission,
),
(StreamState(SessionState::Idle), SessionState::Idle),
];
for (event, expected) in cases {
assert_eq!(
SessionState::for_event(&event, None),
expected,
"event {event:?}"
);
}
assert_eq!(
SessionState::for_event(&Notification(Other), Some(SessionState::Working)),
SessionState::Working
);
assert_eq!(
SessionState::for_event(&TranscriptDiscovered, Some(SessionState::Working)),
SessionState::Working
);
assert_eq!(
SessionState::for_event(
&StreamState(SessionState::Idle),
Some(SessionState::Working)
),
SessionState::Idle
);
for held in [
SessionState::Starting,
SessionState::WaitingForInput,
SessionState::WaitingForPermission,
SessionState::Ended,
] {
assert_eq!(
SessionState::for_event(&TranscriptGrew, Some(held)),
held,
"growth must not overwrite {held:?}"
);
}
for other in [SessionState::Working, SessionState::Idle] {
assert_eq!(
SessionState::for_event(&TranscriptGrew, Some(other)),
SessionState::Working,
"growth from {other:?}"
);
}
}
#[test]
fn end_marks_ended_and_reaps_quickly() {
let reg = SessionsRegistry::new();
reg.observe(observe_request(
"s1",
SessionEvent::PreToolUse,
Some("/tmp/a"),
));
assert!(reg.end("s1", Some("clear"), None));
assert!(!reg.end("ghost", None, None));
let sessions = reg.list();
assert_eq!(sessions.len(), 1);
assert_eq!(sessions[0].state, SessionState::Ended);
reg.clock.advance(Duration::from_secs(30));
assert!(reg.list().is_empty(), "ended entry reaps after ended TTL");
}
#[test]
fn pid_liveness_candidates_lists_only_non_ended_pid_bearing_sessions() {
let reg = SessionsRegistry::new();
reg.observe(observe_from(
"with_pid",
SessionEvent::UserPromptSubmit,
100,
));
reg.observe(observe_request(
"no_pid",
SessionEvent::UserPromptSubmit,
None,
));
reg.observe(observe_from("ended", SessionEvent::UserPromptSubmit, 200));
reg.end("ended", None, Some(200));
let candidates = reg.pid_liveness_candidates();
assert_eq!(candidates.len(), 1);
assert_eq!(candidates[0].session_id, "with_pid");
assert_eq!(candidates[0].pid, 100);
assert!(candidates[0].prompted);
assert_eq!(candidates[0].pid_start, None);
}
#[test]
fn only_a_claude_stream_report_marks_a_session_streamed() {
let reg = SessionsRegistry::new();
reg.observe(observe_from("hooked", SessionEvent::UserPromptSubmit, 100));
let candidates = reg.pid_liveness_candidates();
assert!(candidates[0].prompted);
assert!(!candidates[0].streamed);
reg.observe(observe_from(
"hooked",
SessionEvent::StreamState(SessionState::Idle),
100,
));
reg.observe(observe_from("hooked", SessionEvent::Stop, 100));
let candidates = reg.pid_liveness_candidates();
assert!(candidates[0].prompted && candidates[0].streamed);
let reg = SessionsRegistry::new();
reg.observe(observe_from(
"wrapped",
SessionEvent::StreamState(SessionState::Working),
100,
));
assert!(reg.pid_liveness_candidates()[0].streamed);
let reg = SessionsRegistry::new();
reg.observe(observe_from(
"resumed",
SessionEvent::StreamState(SessionState::Idle),
100,
));
assert!(reg.pid_liveness_candidates()[0].streamed);
reg.observe(observe_from("resumed", SessionEvent::SessionStart, 200));
assert!(!reg.pid_liveness_candidates()[0].streamed);
for agent in [Agent::Codex, Agent::Pi] {
let reg = SessionsRegistry::new();
reg.observe(ObserveRequest {
agent,
..observe_from("other", SessionEvent::StreamState(SessionState::Idle), 100)
});
assert!(!reg.pid_liveness_candidates()[0].streamed, "{agent:?}");
}
}
#[test]
fn confirm_pid_liveness_refreshes_last_seen_and_captures_the_token_once() {
let reg = SessionsRegistry::new();
reg.observe(observe_from("s1", SessionEvent::UserPromptSubmit, 100));
let old = Utc::now() - chrono::Duration::seconds(1000);
let old_active = reg.lock_sessions()["s1"].last_active;
{
let mut guard = reg.lock_sessions();
guard.get_mut("s1").unwrap().last_seen = old;
}
reg.clock.advance(Duration::from_secs(1000));
reg.confirm_pid_liveness("s1", Utc::now(), "tok-1");
{
let guard = reg.lock_sessions();
let entry = &guard["s1"];
assert!(entry.last_seen > old, "last_seen should be refreshed");
assert!(
entry.last_active > old_active + Duration::from_secs(999),
"the awake stamp the TTL reads should be refreshed too"
);
assert_eq!(entry.pid_start.as_deref(), Some("tok-1"));
}
reg.confirm_pid_liveness("s1", Utc::now(), "tok-2");
assert_eq!(
reg.lock_sessions()["s1"].pid_start.as_deref(),
Some("tok-1")
);
}
#[test]
fn confirm_pid_liveness_is_a_noop_for_an_unknown_or_ended_session() {
let reg = SessionsRegistry::new();
reg.confirm_pid_liveness("ghost", Utc::now(), "tok");
reg.observe(observe_from("s1", SessionEvent::UserPromptSubmit, 100));
reg.end("s1", None, Some(100));
let before = reg.lock_sessions()["s1"].last_seen;
let before_active = reg.lock_sessions()["s1"].last_active;
reg.clock.advance(Duration::from_secs(1));
reg.confirm_pid_liveness("s1", Utc::now(), "tok");
assert_eq!(
reg.lock_sessions()["s1"].last_seen,
before,
"an ended session must not be revived by a liveness confirmation"
);
assert_eq!(reg.lock_sessions()["s1"].last_active, before_active);
}
#[test]
fn stale_working_session_reaps_but_recent_survives() {
let reg = SessionsRegistry::new();
reg.observe(observe_request("stale", SessionEvent::PreToolUse, None));
reg.clock.advance(Duration::from_secs(1000));
reg.observe(observe_request("fresh", SessionEvent::PreToolUse, None));
let ids: Vec<String> = reg.list().into_iter().map(|s| s.session_id).collect();
assert_eq!(ids, vec!["fresh".to_string()]);
}
#[test]
fn source_is_vscode_when_cwd_is_under_a_reporting_window() {
let reg = SessionsRegistry::new();
reg.observe(observe_request(
"s1",
SessionEvent::PreToolUse,
Some("/home/me/proj/sub"),
));
reg.report_window(WindowReport {
key: "w1".to_string(),
folders: vec![PathBuf::from("/home/me/proj")],
tabs: 1,
terminals: 0,
});
let sessions = reg.list();
assert_eq!(
sessions[0].source,
Source::VsCode {
window_key: "w1".to_string()
}
);
}
#[test]
fn source_is_terminal_when_window_has_no_embedding() {
let reg = SessionsRegistry::new();
reg.observe(observe_request(
"s1",
SessionEvent::PreToolUse,
Some("/home/me/proj"),
));
reg.report_window(WindowReport {
key: "w1".to_string(),
folders: vec![PathBuf::from("/home/me/proj")],
tabs: 0,
terminals: 0,
});
assert_eq!(reg.list()[0].source, Source::Terminal);
}
#[test]
fn window_report_is_upsert_and_unregister_removes() {
let reg = SessionsRegistry::new();
reg.report_window(WindowReport {
key: "w1".to_string(),
folders: vec![PathBuf::from("/p")],
tabs: 1,
terminals: 0,
});
reg.report_window(WindowReport {
key: "w1".to_string(),
folders: vec![PathBuf::from("/p")],
tabs: 2,
terminals: 1,
});
assert!(reg.unregister_window("w1"));
assert!(!reg.unregister_window("w1"));
}
#[test]
fn stale_window_stops_tagging_source() {
let reg = SessionsRegistry::new();
reg.observe(observe_request(
"s1",
SessionEvent::PreToolUse,
Some("/p/sub"),
));
reg.report_window(WindowReport {
key: "w1".to_string(),
folders: vec![PathBuf::from("/p")],
tabs: 1,
terminals: 0,
});
reg.clock.advance(Duration::from_secs(120));
assert_eq!(reg.list()[0].source, Source::Terminal);
}
#[test]
fn credit_window_outage_keeps_a_report_the_outage_alone_would_have_expired() {
let reg = SessionsRegistry::new();
let report = |key: &str| WindowReport {
key: key.to_string(),
folders: vec![PathBuf::from("/p")],
tabs: 1,
terminals: 0,
};
reg.report_window(report("kept"));
reg.report_window(report("long-gone"));
reg.clock.advance(Duration::from_secs(1000));
{
let awake = reg.clock.now();
let mut guard = reg.lock_windows();
guard.get_mut("kept").unwrap().last_active =
awake.saturating_sub(Duration::from_secs(40));
guard.get_mut("long-gone").unwrap().last_active =
awake.saturating_sub(Duration::from_secs(300));
}
reg.credit_window_outage(Duration::from_secs(25));
reg.list();
let guard = reg.lock_windows();
assert!(guard.contains_key("kept"), "the outage must not age it out");
assert!(
!guard.contains_key("long-gone"),
"a report 300 s silent is dead however long the outage was"
);
}
#[test]
fn credit_window_outage_never_moves_last_active_into_the_future() {
let reg = SessionsRegistry::new();
reg.report_window(WindowReport {
key: "w".to_string(),
folders: vec![PathBuf::from("/p")],
tabs: 1,
terminals: 0,
});
reg.credit_window_outage(Duration::from_secs(3600));
assert!(reg.lock_windows()["w"].last_active <= reg.clock.now());
reg.credit_window_outage(Duration::MAX);
assert!(reg.lock_windows()["w"].last_active <= reg.clock.now());
}
#[test]
fn credit_window_outage_is_not_a_visible_change() {
let reg = SessionsRegistry::new();
reg.report_window(WindowReport {
key: "w".to_string(),
folders: vec![PathBuf::from("/p")],
tabs: 1,
terminals: 0,
});
let rx = reg.subscribe_changes();
reg.credit_window_outage(Duration::from_secs(5));
assert!(!rx.has_changed().unwrap());
}
fn window_entry(key: &str, age_secs: i64) -> WindowEntry {
let now = Utc::now();
WindowEntry {
report: window_report(key, "/p", true),
last_active: Duration::ZERO,
registered_at: now - chrono::Duration::seconds(age_secs),
}
}
fn vscode(key: &str) -> Source {
Source::VsCode {
window_key: key.to_string(),
}
}
#[test]
fn resolve_source_prefers_newest_registration_over_key_order() {
let windows = vec![window_entry("w1", 20), window_entry("w2", 5)];
assert_eq!(
resolve_source(Some(Path::new("/p/x")), &windows),
vscode("w2")
);
assert_eq!(resolve_source(None, &windows), Source::Terminal);
}
#[test]
fn resolve_source_breaks_equal_registrations_by_lowest_key() {
let now = Utc::now();
let at_now = |key: &str| WindowEntry {
report: window_report(key, "/p", true),
last_active: Duration::ZERO,
registered_at: now,
};
let windows = vec![at_now("w2"), at_now("w1")];
assert_eq!(
resolve_source(Some(Path::new("/p/x")), &windows),
vscode("w1")
);
}
#[test]
fn reload_overlap_attributes_to_the_new_window_even_if_its_key_sorts_higher() {
let reg = SessionsRegistry::new();
reg.observe(observe_request(
"s1",
SessionEvent::PreToolUse,
Some("/p/sub"),
));
reg.report_window(window_report("39-old", "/p", true));
{
let mut guard = reg.lock_windows();
guard.get_mut("39-old").unwrap().registered_at =
Utc::now() - chrono::Duration::seconds(60);
}
reg.report_window(window_report("52-new", "/p", true));
assert_eq!(reg.list()[0].source, vscode("52-new"));
reg.report_window(window_report("39-old", "/p", true));
assert_eq!(reg.list()[0].source, vscode("52-new"));
}
#[test]
fn a_more_specific_folder_outranks_a_newer_parent_window() {
let now = Utc::now();
let entry = |key: &str, folder: &str, age_secs: i64| WindowEntry {
report: window_report(key, folder, true),
last_active: Duration::ZERO,
registered_at: now - chrono::Duration::seconds(age_secs),
};
let windows = vec![
entry("a-parent", "/repo", 1),
entry("z-sub", "/repo/sub", 50),
];
assert_eq!(
resolve_source(Some(Path::new("/repo/sub/x")), &windows),
vscode("z-sub")
);
assert_eq!(
resolve_source(Some(Path::new("/repo/other")), &windows),
vscode("a-parent")
);
}
#[test]
fn refreshing_a_window_preserves_its_registration_time() {
let reg = SessionsRegistry::new();
reg.report_window(window_report("w1", "/p", true));
let first = reg.lock_windows()["w1"].registered_at;
let first_active = reg.lock_windows()["w1"].last_active;
reg.clock.advance(Duration::from_secs(5));
reg.report_window(window_report("w1", "/p", true));
let guard = reg.lock_windows();
assert_eq!(guard["w1"].registered_at, first);
assert!(guard["w1"].last_active > first_active);
}
#[test]
fn reregistering_after_unregister_gets_a_fresh_registration_time() {
let reg = SessionsRegistry::new();
reg.report_window(window_report("w1", "/p", true));
let first = reg.lock_windows()["w1"].registered_at;
std::thread::sleep(std::time::Duration::from_millis(5));
assert!(reg.unregister_window("w1"));
reg.report_window(window_report("w1", "/p", true));
assert!(reg.lock_windows()["w1"].registered_at > first);
}
#[test]
fn focus_folder_agrees_with_list_on_overlap() {
let reg = SessionsRegistry::new();
reg.observe(observe_request(
"s1",
SessionEvent::PreToolUse,
Some("/p/sub"),
));
let mut old = window_report("a-old", "/p", true);
old.folders = vec![PathBuf::from("/p/old-root"), PathBuf::from("/p")];
reg.report_window(old);
{
let mut guard = reg.lock_windows();
guard.get_mut("a-old").unwrap().registered_at =
Utc::now() - chrono::Duration::seconds(60);
}
reg.report_window(window_report("z-new", "/p", true));
assert_eq!(reg.list()[0].source, vscode("z-new"));
assert_eq!(reg.focus_folder("s1"), Some(PathBuf::from("/p")));
}
#[test]
fn focus_folder_resolves_matching_window_folder() {
let reg = SessionsRegistry::new();
reg.observe(observe_request(
"s1",
SessionEvent::PreToolUse,
Some("/home/me/proj/sub"),
));
assert!(reg.focus_folder("s1").is_none(), "no window yet");
reg.report_window(WindowReport {
key: "w1".to_string(),
folders: vec![PathBuf::from("/home/me/proj")],
tabs: 1,
terminals: 0,
});
assert_eq!(reg.focus_folder("s1"), Some(PathBuf::from("/home/me/proj")));
assert!(reg.focus_folder("ghost").is_none());
}
#[test]
fn evict_oldest_session_drops_the_longest_silent() {
let now = Utc::now();
let mut sessions = HashMap::new();
for (id, age) in [("young", 0_u64), ("old", 100), ("older", 200)] {
sessions.insert(
id.to_string(),
SessionEntry {
subagent_base: None,
subagent_waits: HashMap::new(),
pid: None,
pid_start: None,
prompted: false,
streamed: false,
replaced_pids: VecDeque::new(),
recent_seqs: VecDeque::new(),
superseded_seqs: VecDeque::new(),
latest_stamp_ts: None,
reported: false,
agent: Agent::Claude,
session_id: id.to_string(),
cwd: None,
transcript_path: None,
repo: None,
model: None,
state: SessionState::Working,
source: Source::Terminal,
last_event: SessionEvent::PreToolUse,
started_at: now,
last_seen: now,
last_active: Duration::from_secs(1000 - age),
},
);
}
evict_oldest_session(&mut sessions);
assert!(!sessions.contains_key("older"));
assert!(sessions.contains_key("young"));
assert!(sessions.contains_key("old"));
let mut empty: HashMap<String, SessionEntry> = HashMap::new();
evict_oldest_session(&mut empty);
assert!(empty.is_empty());
}
#[test]
fn list_sorts_by_repo_then_session_id() {
let reg = SessionsRegistry::new();
for (id, repo) in [("z", "repo-a"), ("a", "repo-b"), ("m", "repo-a")] {
reg.observe(ObserveRequest {
agent_id: None,
pid: None,
agent: Agent::Claude,
session_id: id.to_string(),
cwd: None,
transcript_path: None,
event: SessionEvent::PreToolUse,
repo: Some(repo.to_string()),
model: None,
});
}
let ordered: Vec<(String, String)> = reg
.list()
.into_iter()
.map(|s| (s.session_id, s.repo.unwrap()))
.collect();
assert_eq!(
ordered,
vec![
("m".to_string(), "repo-a".to_string()),
("z".to_string(), "repo-a".to_string()),
("a".to_string(), "repo-b".to_string()),
]
);
}
#[test]
fn serialized_session_shapes_are_stable() {
let reg = SessionsRegistry::new();
reg.observe(ObserveRequest {
agent_id: None,
pid: None,
agent: Agent::Claude,
session_id: "s1".to_string(),
cwd: Some(PathBuf::from("/p")),
transcript_path: None,
event: SessionEvent::Notification(NotificationKind::PermissionPrompt),
repo: Some("proj".to_string()),
model: None,
});
let value = serde_json::to_value(®.list()[0]).unwrap();
assert_eq!(value["state"], "waiting_for_permission");
assert_eq!(value["source"]["kind"], "terminal");
assert_eq!(value["repo"], "proj");
assert!(value.get("model").is_none());
assert!(value.get("transcript_path").is_none());
}
#[test]
fn stream_state_is_authoritative_and_round_trips() {
let reg = SessionsRegistry::new();
reg.observe(observe_request("s1", SessionEvent::PreToolUse, Some("/p")));
assert_eq!(reg.list()[0].state, SessionState::Working);
reg.observe(observe_request(
"s1",
SessionEvent::StreamState(SessionState::WaitingForPermission),
None,
));
assert_eq!(reg.list()[0].state, SessionState::WaitingForPermission);
let value = serde_json::to_value(SessionEvent::StreamState(
SessionState::WaitingForPermission,
))
.unwrap();
assert_eq!(
value,
serde_json::json!({ "stream_state": "waiting_for_permission" })
);
let event: SessionEvent = serde_json::from_value(value).unwrap();
assert_eq!(
event,
SessionEvent::StreamState(SessionState::WaitingForPermission)
);
}
#[test]
fn default_constructs_an_empty_registry() {
let reg = SessionsRegistry::default();
assert!(reg.list().is_empty());
}
#[test]
fn agent_is_omitted_for_claude_and_tagged_for_pi_on_the_wire() {
let claude = serde_json::to_value(observe_request("s", SessionEvent::Stop, None)).unwrap();
assert!(claude.get("agent").is_none(), "{claude}");
let parsed: ObserveRequest =
serde_json::from_value(serde_json::json!({ "session_id": "s", "event": "stop" }))
.unwrap();
assert_eq!(parsed.agent, Agent::Claude);
let pi: ObserveRequest = serde_json::from_value(serde_json::json!({
"session_id": "019a0000-0000-7000-8000-000000000000",
"cwd": "/work/repo",
"agent": "pi",
"event": { "stream_state": "waiting_for_input" },
}))
.unwrap();
assert_eq!(pi.agent, Agent::Pi);
assert_eq!(
pi.event,
SessionEvent::StreamState(SessionState::WaitingForInput)
);
}
#[test]
fn agents_have_display_names() {
assert_eq!(Agent::Claude.display_name(), "Claude");
assert_eq!(Agent::Pi.display_name(), "pi");
assert_eq!(Agent::Codex.display_name(), "Codex");
}
#[test]
fn an_ended_session_is_not_refreshed_by_a_passive_sighting_or_a_second_end() {
let reg = SessionsRegistry::new();
reg.observe(observe_request("s", SessionEvent::Stop, None));
assert!(reg.end("s", None, None));
let ended_at = reg.list()[0].last_seen;
let mut changes = reg.subscribe_changes();
changes.mark_unchanged();
std::thread::sleep(std::time::Duration::from_millis(5));
reg.observe(observe_request(
"s",
SessionEvent::TranscriptDiscovered,
None,
));
assert!(reg.end("s", None, None), "still a known session");
let listed = reg.list();
assert_eq!(listed[0].state, SessionState::Ended);
assert_eq!(
listed[0].last_seen, ended_at,
"the linger window did not restart"
);
assert!(
!changes.has_changed().unwrap(),
"no consumer-visible change"
);
reg.observe(observe_request("s", SessionEvent::UserPromptSubmit, None));
assert_eq!(reg.list()[0].state, SessionState::Working);
}
#[test]
fn codex_is_a_tagged_agent_on_the_wire() {
let codex: ObserveRequest = serde_json::from_value(serde_json::json!({
"session_id": "019a0000-0000-7000-8000-000000000001",
"agent": "codex",
"event": { "notification": "permission_prompt" },
}))
.unwrap();
assert_eq!(codex.agent, Agent::Codex);
assert!(!codex.agent.is_claude());
let json = serde_json::to_value(&codex).unwrap();
assert_eq!(json["agent"], "codex");
}
#[test]
fn a_pi_session_is_listed_with_its_agent_and_reported_state() {
let reg = SessionsRegistry::new();
let mut req = observe_request(
"pi-1",
SessionEvent::StreamState(SessionState::Working),
Some("/work/repo"),
);
req.agent = Agent::Pi;
reg.observe(req);
reg.observe(observe_request(
"pi-1",
SessionEvent::StreamState(SessionState::Idle),
None,
));
let listed = reg.list();
assert_eq!(listed.len(), 1);
assert_eq!(listed[0].agent, Agent::Pi);
assert_eq!(listed[0].state, SessionState::Idle);
let json = serde_json::to_value(&listed[0]).unwrap();
assert_eq!(json["agent"], "pi");
}
#[test]
fn fill_only_overwrites_with_a_present_value() {
let mut slot = Some("keep");
fill(&mut slot, None);
assert_eq!(slot, Some("keep"));
fill(&mut slot, Some("new"));
assert_eq!(slot, Some("new"));
let mut empty: Option<&str> = None;
fill(&mut empty, Some("filled"));
assert_eq!(empty, Some("filled"));
}
#[test]
fn observe_at_session_cap_evicts_the_longest_silent() {
let reg = SessionsRegistry::new();
{
let mut sessions = reg.lock_sessions();
let base = Utc::now();
for i in 0..MAX_SESSIONS {
let id = format!("s{i:04}");
sessions.insert(
id.clone(),
SessionEntry {
subagent_base: None,
subagent_waits: HashMap::new(),
pid: None,
pid_start: None,
prompted: false,
streamed: false,
replaced_pids: VecDeque::new(),
recent_seqs: VecDeque::new(),
superseded_seqs: VecDeque::new(),
latest_stamp_ts: None,
reported: false,
agent: Agent::Claude,
session_id: id.clone(),
cwd: None,
transcript_path: None,
repo: None,
model: None,
state: SessionState::Working,
source: Source::Terminal,
last_event: SessionEvent::PreToolUse,
started_at: base,
last_seen: base,
last_active: Duration::from_millis(1_000_000 - i as u64),
},
);
}
}
reg.observe(observe_request("fresh", SessionEvent::PreToolUse, None));
let sessions = reg.lock_sessions();
assert_eq!(sessions.len(), MAX_SESSIONS);
assert!(sessions.contains_key("fresh"));
assert!(!sessions.contains_key(&format!("s{:04}", MAX_SESSIONS - 1)));
assert!(sessions.contains_key("s0000"));
}
#[test]
fn report_window_at_cap_evicts_the_longest_silent() {
let reg = SessionsRegistry::new();
{
let mut windows = reg.lock_windows();
let base = Utc::now();
for i in 0..MAX_WINDOWS {
let key = format!("w{i:04}");
windows.insert(
key.clone(),
WindowEntry {
report: WindowReport {
key: key.clone(),
folders: vec![],
tabs: 1,
terminals: 0,
},
last_active: Duration::from_millis(1_000_000 - i as u64),
registered_at: base,
},
);
}
}
reg.report_window(WindowReport {
key: "fresh".to_string(),
folders: vec![],
tabs: 1,
terminals: 0,
});
let windows = reg.lock_windows();
assert_eq!(windows.len(), MAX_WINDOWS);
assert!(windows.contains_key("fresh"));
assert!(!windows.contains_key(&format!("w{:04}", MAX_WINDOWS - 1)));
assert!(windows.contains_key("w0000"));
}
#[test]
fn evict_oldest_window_breaks_ties_by_key() {
let now = Utc::now();
let mut windows = HashMap::new();
let at = |key: &str, secs: u64| WindowEntry {
report: WindowReport {
key: key.to_string(),
folders: vec![],
tabs: 1,
terminals: 0,
},
last_active: Duration::from_secs(100 - secs),
registered_at: now,
};
windows.insert("young".to_string(), at("young", 0));
windows.insert("old-b".to_string(), at("old-b", 10));
windows.insert("old-a".to_string(), at("old-a", 10));
evict_oldest_window(&mut windows);
assert!(!windows.contains_key("old-a"));
assert!(windows.contains_key("old-b"));
assert!(windows.contains_key("young"));
let mut empty: HashMap<String, WindowEntry> = HashMap::new();
evict_oldest_window(&mut empty);
assert!(empty.is_empty());
}
fn window_report(key: &str, folder: &str, embedded: bool) -> WindowReport {
WindowReport {
key: key.to_string(),
folders: vec![PathBuf::from(folder)],
tabs: usize::from(embedded),
terminals: 0,
}
}
#[test]
fn subscribe_changes_starts_seen_and_a_new_session_bumps() {
let reg = SessionsRegistry::new();
let mut rx = reg.subscribe_changes();
assert!(!rx.has_changed().unwrap());
reg.observe(observe_request("s1", SessionEvent::SessionStart, None));
assert!(rx.has_changed().unwrap(), "a new session should bump");
rx.borrow_and_update();
assert!(!rx.has_changed().unwrap());
}
#[test]
fn observe_bumps_on_a_state_transition_but_not_on_a_repeat_sighting() {
let reg = SessionsRegistry::new();
reg.observe(observe_request(
"s1",
SessionEvent::PreToolUse,
Some("/tmp/a"),
));
let mut rx = reg.subscribe_changes();
reg.observe(observe_request(
"s1",
SessionEvent::PreToolUse,
Some("/tmp/a"),
));
assert!(
!rx.has_changed().unwrap(),
"a repeat sighting with no visible change must not bump"
);
reg.observe(observe_request("s1", SessionEvent::Stop, None));
assert!(rx.has_changed().unwrap(), "a state transition should bump");
rx.borrow_and_update();
reg.observe(observe_request("s1", SessionEvent::Stop, Some("/tmp/b")));
assert!(
rx.has_changed().unwrap(),
"a newly-filled `cwd` should bump"
);
}
#[test]
fn transcript_growth_does_not_clobber_a_waiting_session() {
let reg = SessionsRegistry::new();
reg.observe(observe_request(
"s1",
SessionEvent::Notification(NotificationKind::PermissionPrompt),
Some("/tmp/a"),
));
let rx = reg.subscribe_changes();
reg.observe(observe_request(
"s1",
SessionEvent::TranscriptGrew,
Some("/tmp/a"),
));
assert_eq!(reg.list()[0].state, SessionState::WaitingForPermission);
assert!(
!rx.has_changed().unwrap(),
"state did not change, so nothing a consumer renders did either (#1414)"
);
reg.observe(observe_request("s1", SessionEvent::PostToolUse, None));
assert_eq!(reg.list()[0].state, SessionState::Working);
assert!(
rx.has_changed().unwrap(),
"the release is a real transition"
);
}
#[test]
fn transcript_growth_does_not_revive_an_ended_session() {
let reg = SessionsRegistry::new();
reg.observe(observe_request(
"s1",
SessionEvent::PreToolUse,
Some("/tmp/a"),
));
assert!(reg.end("s1", Some("clear"), None));
let rx = reg.subscribe_changes();
reg.observe(observe_request(
"s1",
SessionEvent::TranscriptGrew,
Some("/tmp/a"),
));
assert_eq!(reg.list()[0].state, SessionState::Ended);
assert!(
!rx.has_changed().unwrap(),
"state did not change, so nothing a consumer renders did either (#1414)"
);
}
#[test]
fn a_rollout_turn_marker_moves_working_and_idle_and_nothing_else() {
let open = SessionEvent::RolloutTurn(RolloutTurn::Open);
let closed = SessionEvent::RolloutTurn(RolloutTurn::Closed);
for held in [
SessionState::Starting,
SessionState::WaitingForInput,
SessionState::WaitingForPermission,
SessionState::Ended,
] {
assert_eq!(SessionState::for_event(&open, Some(held)), held, "{held:?}");
assert_eq!(
SessionState::for_event(&closed, Some(held)),
held,
"{held:?}"
);
}
for other in [SessionState::Working, SessionState::Idle] {
assert_eq!(
SessionState::for_event(&open, Some(other)),
SessionState::Working,
"open from {other:?}"
);
assert_eq!(
SessionState::for_event(&closed, Some(other)),
SessionState::Idle,
"closed from {other:?}"
);
}
}
#[test]
fn a_hookless_codex_session_follows_its_rollouts_turns() {
let reg = SessionsRegistry::new();
let turn = |turn| ObserveRequest {
agent: Agent::Codex,
..observe_request(
"019a0000-0000-7000-8000-00000000c0de",
SessionEvent::RolloutTurn(turn),
Some("/work/repo"),
)
};
reg.observe(turn(RolloutTurn::Open));
assert_eq!(reg.list()[0].state, SessionState::Working);
reg.observe(turn(RolloutTurn::Closed));
assert_eq!(reg.list()[0].state, SessionState::Idle);
reg.observe(turn(RolloutTurn::Open));
assert_eq!(reg.list()[0].state, SessionState::Working);
}
#[test]
fn a_rollout_turn_marker_neither_overrides_nor_releases_a_reported_wait() {
let reg = SessionsRegistry::new();
reg.observe(observe_request(
"s1",
SessionEvent::Notification(NotificationKind::PermissionPrompt),
Some("/tmp/a"),
));
let rx = reg.subscribe_changes();
for turn in [RolloutTurn::Open, RolloutTurn::Closed] {
reg.observe(observe_request(
"s1",
SessionEvent::RolloutTurn(turn),
Some("/tmp/a"),
));
assert_eq!(reg.list()[0].state, SessionState::WaitingForPermission);
}
assert!(
!rx.has_changed().unwrap(),
"state did not change, so nothing a consumer renders did either (#1414)"
);
reg.observe(observe_request(
"s1",
SessionEvent::StreamState(SessionState::Idle),
None,
));
assert_eq!(reg.list()[0].state, SessionState::Idle);
}
#[test]
fn an_open_rollout_turn_does_not_start_working_over_a_reported_idle() {
let open = SessionEvent::RolloutTurn(RolloutTurn::Open);
let closed = SessionEvent::RolloutTurn(RolloutTurn::Closed);
for reported in [
SessionEvent::Stop,
SessionEvent::StreamState(SessionState::Idle),
] {
let reg = SessionsRegistry::new();
reg.observe(observe_request("s", reported, None));
assert_eq!(reg.list()[0].state, SessionState::Idle);
reg.observe(observe_request(
"s",
SessionEvent::TranscriptDiscovered,
None,
));
reg.observe(observe_request("s", open, None));
assert_eq!(reg.list()[0].state, SessionState::Idle, "{reported:?}");
reg.observe(observe_request("s", SessionEvent::UserPromptSubmit, None));
assert_eq!(reg.list()[0].state, SessionState::Working);
}
let reg = SessionsRegistry::new();
reg.observe(observe_request("s", SessionEvent::UserPromptSubmit, None));
reg.observe(observe_request("s", closed, None));
assert_eq!(reg.list()[0].state, SessionState::Idle);
reg.observe(observe_request("s", open, None));
assert_eq!(reg.list()[0].state, SessionState::Working);
}
#[test]
fn a_session_first_seen_from_its_rollout_has_no_reported_state_to_defend() {
for first in [
SessionEvent::TranscriptDiscovered,
SessionEvent::RolloutTurn(RolloutTurn::Closed),
] {
let reg = SessionsRegistry::new();
reg.observe(observe_request("s", first, None));
assert_eq!(reg.list()[0].state, SessionState::Idle);
reg.observe(observe_request(
"s",
SessionEvent::RolloutTurn(RolloutTurn::Open),
None,
));
assert_eq!(reg.list()[0].state, SessionState::Working, "{first:?}");
}
}
#[test]
fn a_rollout_turn_marker_does_not_revive_an_ended_session() {
let reg = SessionsRegistry::new();
reg.observe(observe_request("s", SessionEvent::Stop, None));
assert!(reg.end("s", None, None));
let ended_at = reg.list()[0].last_seen;
let rx = reg.subscribe_changes();
std::thread::sleep(std::time::Duration::from_millis(5));
reg.observe(observe_request(
"s",
SessionEvent::RolloutTurn(RolloutTurn::Open),
None,
));
let listed = reg.list();
assert_eq!(listed[0].state, SessionState::Ended);
assert_eq!(
listed[0].last_seen, ended_at,
"the linger window did not restart"
);
assert!(!rx.has_changed().unwrap(), "no consumer-visible change");
}
#[test]
fn a_rollout_turn_marker_is_a_wire_event() {
for (turn, name) in [(RolloutTurn::Open, "open"), (RolloutTurn::Closed, "closed")] {
let event = SessionEvent::RolloutTurn(turn);
let json = serde_json::to_value(event).unwrap();
assert_eq!(json, serde_json::json!({ "rollout_turn": name }));
assert_eq!(serde_json::from_value::<SessionEvent>(json).unwrap(), event);
}
}
#[test]
fn a_resumed_session_stays_starting_until_its_first_prompt() {
let reg = SessionsRegistry::new();
reg.observe(observe_request(
"s1",
SessionEvent::UserPromptSubmit,
Some("/tmp/a"),
));
assert!(reg.end("s1", Some("other"), None));
reg.observe(observe_request(
"s1",
SessionEvent::SessionStart,
Some("/tmp/a"),
));
assert_eq!(reg.list()[0].state, SessionState::Starting);
reg.observe(observe_request(
"s1",
SessionEvent::TranscriptGrew,
Some("/tmp/a"),
));
assert_eq!(reg.list()[0].state, SessionState::Starting);
reg.observe(observe_request("s1", SessionEvent::UserPromptSubmit, None));
assert_eq!(reg.list()[0].state, SessionState::Working);
}
#[test]
fn end_bumps_only_for_a_known_session() {
let reg = SessionsRegistry::new();
reg.observe(observe_request("s1", SessionEvent::PreToolUse, None));
let mut rx = reg.subscribe_changes();
assert!(
!reg.end("ghost", None, None),
"an unknown session is a no-op"
);
assert!(
!rx.has_changed().unwrap(),
"ending an unknown session must not bump"
);
assert!(reg.end("s1", None, None));
assert!(rx.has_changed().unwrap(), "a real end should bump");
rx.borrow_and_update();
}
fn observe_from(session_id: &str, event: SessionEvent, pid: u32) -> ObserveRequest {
ObserveRequest {
agent_id: None,
pid: Some(pid),
..observe_request(session_id, event, None)
}
}
#[test]
fn a_late_end_from_the_replaced_process_keeps_a_resumed_session_live() {
let reg = SessionsRegistry::new();
reg.observe(observe_from("s1", SessionEvent::SessionStart, 100));
reg.observe(observe_from("s1", SessionEvent::Stop, 100));
reg.observe(observe_from("s1", SessionEvent::SessionStart, 200));
let rx = reg.subscribe_changes();
assert!(reg.end("s1", Some("other"), Some(100)));
assert!(!rx.has_changed().unwrap(), "an ignored end must not bump");
let sessions = reg.list();
assert_eq!(sessions.len(), 1);
assert_eq!(sessions[0].state, SessionState::Starting);
assert!(reg.end("s1", Some("exit"), Some(200)));
assert_eq!(reg.list()[0].state, SessionState::Ended);
}
#[test]
fn a_straggling_hook_from_the_replaced_process_does_not_take_the_session_back() {
let reg = SessionsRegistry::new();
reg.observe(observe_from("s1", SessionEvent::SessionStart, 100));
reg.observe(observe_from("s1", SessionEvent::SessionStart, 200));
reg.observe(observe_from("s1", SessionEvent::PostToolUse, 100));
assert_eq!(reg.list()[0].state, SessionState::Starting);
reg.end("s1", None, Some(100));
assert_ne!(reg.list()[0].state, SessionState::Ended);
}
#[test]
fn a_straggling_non_end_hook_from_the_replaced_process_does_not_change_state() {
let reg = SessionsRegistry::new();
reg.observe(observe_from("s1", SessionEvent::UserPromptSubmit, 100));
assert_eq!(reg.list()[0].state, SessionState::Working);
reg.observe(observe_from("s1", SessionEvent::SessionStart, 200));
assert_eq!(reg.list()[0].state, SessionState::Starting);
let rx = reg.subscribe_changes();
reg.observe(observe_from("s1", SessionEvent::Stop, 100));
assert_eq!(reg.list()[0].state, SessionState::Starting);
assert!(
!rx.has_changed().unwrap(),
"an ignored sighting must not bump"
);
}
#[test]
fn an_end_not_from_the_replaced_process_still_ends_the_session() {
for end_pid in [None, Some(200), Some(999)] {
let reg = SessionsRegistry::new();
reg.observe(observe_from("s1", SessionEvent::SessionStart, 100));
reg.observe(observe_from("s1", SessionEvent::SessionStart, 200));
assert!(reg.end("s1", None, end_pid));
assert_eq!(
reg.list()[0].state,
SessionState::Ended,
"end with pid {end_pid:?}"
);
}
let reg = SessionsRegistry::new();
reg.observe(observe_from("s1", SessionEvent::SessionStart, 100));
assert!(reg.end("s1", None, Some(100)));
assert_eq!(reg.list()[0].state, SessionState::Ended);
}
#[test]
fn every_process_of_a_reload_burst_is_remembered_as_replaced() {
let reg = SessionsRegistry::new();
for pid in [100, 200, 300] {
reg.observe(observe_from("s1", SessionEvent::SessionStart, pid));
}
assert!(reg.end("s1", None, Some(100)));
assert!(reg.end("s1", None, Some(200)));
assert_eq!(reg.list()[0].state, SessionState::Starting);
}
#[test]
fn a_stream_report_from_a_new_process_takes_the_session_over() {
let idle = || SessionEvent::StreamState(SessionState::Idle);
let reg = SessionsRegistry::new();
reg.observe(observe_from("s1", idle(), 100));
reg.observe(observe_from("s1", idle(), 200));
reg.end("s1", None, Some(100));
assert_eq!(reg.list()[0].state, SessionState::Idle);
}
#[test]
fn replaced_pids_are_capped_oldest_first() {
let reg = SessionsRegistry::new();
let last = u32::try_from(MAX_REPLACED_PIDS).unwrap() + 1;
for pid in 0..=last {
reg.observe(observe_from("s1", SessionEvent::PostToolUse, pid));
}
let guard = reg.lock_sessions();
let replaced = &guard["s1"].replaced_pids;
assert_eq!(replaced.len(), MAX_REPLACED_PIDS);
assert_eq!(replaced.front(), Some(&1), "pid 0 was forgotten first");
assert_eq!(replaced.back(), Some(&(last - 1)));
}
#[test]
fn a_sessions_first_pid_sighting_is_not_a_replacement() {
let reg = SessionsRegistry::new();
reg.observe(observe_request(
"s1",
SessionEvent::TranscriptDiscovered,
None,
));
reg.observe(observe_from("s1", SessionEvent::PostToolUse, 100));
let guard = reg.lock_sessions();
assert_eq!(guard["s1"].pid, Some(100));
assert!(guard["s1"].replaced_pids.is_empty());
}
#[test]
fn end_then_start_resume_ordering_is_unchanged() {
let reg = SessionsRegistry::new();
reg.observe(observe_from("s1", SessionEvent::SessionStart, 100));
assert!(reg.end("s1", None, Some(100)));
reg.observe(observe_from("s1", SessionEvent::SessionStart, 200));
assert_eq!(reg.list()[0].state, SessionState::Starting);
}
#[test]
fn window_report_bumps_only_when_it_changes_the_source_join() {
let reg = SessionsRegistry::new();
reg.report_window(window_report("w1", "/p", true));
let mut rx = reg.subscribe_changes();
reg.report_window(window_report("w1", "/p", true));
assert!(
!rx.has_changed().unwrap(),
"an unchanged window refresh must not bump"
);
reg.report_window(window_report("w1", "/p", false));
assert!(
rx.has_changed().unwrap(),
"an embedding that vanished should bump"
);
rx.borrow_and_update();
reg.report_window(window_report("w1", "/q", false));
assert!(rx.has_changed().unwrap(), "changed folders should bump");
rx.borrow_and_update();
reg.report_window(window_report("w2", "/r", true));
assert!(rx.has_changed().unwrap(), "a new window should bump");
}
#[test]
fn unregister_window_bumps_only_when_it_removes() {
let reg = SessionsRegistry::new();
reg.report_window(window_report("w1", "/p", true));
let rx = reg.subscribe_changes();
assert!(!reg.unregister_window("ghost"));
assert!(
!rx.has_changed().unwrap(),
"a no-op unregister must not bump"
);
assert!(reg.unregister_window("w1"));
assert!(
rx.has_changed().unwrap(),
"a removing unregister should bump"
);
}
#[tokio::test]
async fn a_burst_of_bumps_coalesces_into_one_wakeup() {
let reg = SessionsRegistry::new();
let mut rx = reg.subscribe_changes();
reg.observe(observe_request("s1", SessionEvent::SessionStart, None));
reg.observe(observe_request("s2", SessionEvent::SessionStart, None));
reg.observe(observe_request("s3", SessionEvent::SessionStart, None));
rx.changed().await.unwrap();
assert!(
!rx.has_changed().unwrap(),
"a burst should collapse into a single wakeup"
);
}
#[test]
fn list_does_not_bump_even_when_it_reaps() {
let reg = SessionsRegistry::new();
reg.observe(observe_request("s1", SessionEvent::PreToolUse, None));
reg.clock.advance(Duration::from_secs(600));
let rx = reg.subscribe_changes();
assert!(reg.list().is_empty(), "the stale session should be reaped");
assert!(!rx.has_changed().unwrap(), "`list` must never bump");
}
fn just_inside_the_ttl() -> Duration {
DEFAULT_SESSION_TTL
.checked_sub(Duration::from_secs(1))
.unwrap()
}
fn jump_wall_clock_forward(reg: &SessionsRegistry, by: chrono::Duration) {
for entry in reg.lock_sessions().values_mut() {
entry.last_seen -= by;
}
}
#[test]
fn a_wall_clock_jump_with_no_awake_time_reaps_nothing() {
let reg = SessionsRegistry::new();
reg.observe(observe_request("working", SessionEvent::PreToolUse, None));
reg.observe(observe_request("idle", SessionEvent::Stop, None));
reg.observe(observe_request("ended", SessionEvent::PreToolUse, None));
reg.end("ended", None, None);
reg.report_window(window_report("w1", "/p", true));
jump_wall_clock_forward(®, chrono::Duration::days(3));
let ids: Vec<String> = reg.list().into_iter().map(|s| s.session_id).collect();
assert_eq!(
ids,
vec![
"ended".to_string(),
"idle".to_string(),
"working".to_string()
],
"sleeping must not age a session, not even an ended one"
);
assert_eq!(reg.lock_windows().len(), 1, "nor a window report");
assert!(reg.list()[0].last_seen < Utc::now() - chrono::Duration::days(2));
}
#[test]
fn awake_time_past_the_ttl_still_reaps() {
let reg = SessionsRegistry::new();
reg.observe(observe_request("s1", SessionEvent::PreToolUse, None));
reg.report_window(window_report("w1", "/p", true));
reg.clock.advance(just_inside_the_ttl());
assert_eq!(reg.list().len(), 1, "inside the TTL it survives");
assert_eq!(reg.lock_windows().len(), 0, "the window TTL is shorter");
reg.clock.advance(Duration::from_secs(2));
assert!(
reg.list().is_empty(),
"past the TTL of awake silence it reaps"
);
}
#[test]
fn activity_after_a_sleep_restarts_the_awake_ttl() {
let reg = SessionsRegistry::new();
reg.observe(observe_request("s1", SessionEvent::PreToolUse, None));
reg.clock.advance(just_inside_the_ttl());
jump_wall_clock_forward(®, chrono::Duration::hours(8));
reg.observe(observe_request("s1", SessionEvent::PostToolUse, None));
reg.clock.advance(just_inside_the_ttl());
assert_eq!(reg.list().len(), 1);
}
fn at(ts: &str) -> DateTime<Utc> {
ts.parse().unwrap()
}
fn stamp(ts: &str, seq: &str) -> EventStamp {
EventStamp {
ts: at(ts),
seq: seq.to_string(),
}
}
fn state_of(reg: &SessionsRegistry, id: &str) -> SessionState {
reg.lock_sessions()[id].state
}
#[test]
fn the_second_copy_of_a_stamped_event_is_dropped_whatever_its_route() {
let reg = SessionsRegistry::new();
let work = stamp("2026-10-03T03:40:00Z", "1-a");
let stop = stamp("2026-10-03T03:40:05Z", "1-b");
reg.observe_stamped(
observe_request("s", SessionEvent::PreToolUse, Some("/p")),
Some(work.clone()),
Origin::Socket,
);
reg.observe_stamped(
observe_request("s", SessionEvent::Stop, None),
Some(stop),
Origin::Socket,
);
assert_eq!(state_of(®, "s"), SessionState::Idle);
for origin in [Origin::Journal, Origin::Socket] {
reg.observe_stamped(
observe_request("s", SessionEvent::PreToolUse, None),
Some(work.clone()),
origin,
);
assert_eq!(state_of(®, "s"), SessionState::Idle, "{origin:?}");
}
}
#[test]
fn a_journal_event_older_than_one_already_applied_is_stale() {
let reg = SessionsRegistry::new();
reg.observe_stamped(
observe_request("s", SessionEvent::Stop, Some("/p")),
Some(stamp("2026-10-03T03:40:05Z", "1-b")),
Origin::Socket,
);
reg.observe_stamped(
observe_request("s", SessionEvent::PreToolUse, None),
Some(stamp("2026-10-03T03:40:00Z", "1-a")),
Origin::Journal,
);
assert_eq!(state_of(®, "s"), SessionState::Idle);
reg.observe_stamped(
observe_request("s", SessionEvent::PreToolUse, None),
Some(stamp("2026-10-03T03:41:00Z", "1-c")),
Origin::Journal,
);
assert_eq!(state_of(®, "s"), SessionState::Working);
}
#[test]
fn a_socket_event_overtaken_by_a_newer_one_is_dropped_but_never_for_a_clock_step() {
let reg = SessionsRegistry::new();
reg.observe_stamped(
observe_request("s", SessionEvent::Stop, Some("/p")),
Some(stamp("2026-10-03T03:40:05Z", "1-b")),
Origin::Journal,
);
reg.observe_stamped(
observe_request("s", SessionEvent::PreToolUse, None),
Some(stamp("2026-10-03T03:40:03Z", "1-a")),
Origin::Socket,
);
assert_eq!(state_of(®, "s"), SessionState::Idle);
reg.observe_stamped(
observe_request("s", SessionEvent::PreToolUse, None),
Some(stamp("2026-10-03T03:30:00Z", "1-c")),
Origin::Socket,
);
assert_eq!(state_of(®, "s"), SessionState::Working);
}
#[test]
fn unstamped_events_are_never_skipped() {
let reg = SessionsRegistry::new();
reg.observe(observe_request("s", SessionEvent::Stop, Some("/p")));
reg.observe(observe_request("s", SessionEvent::PreToolUse, None));
assert_eq!(state_of(®, "s"), SessionState::Working);
reg.observe(observe_request("s", SessionEvent::Stop, None));
assert_eq!(state_of(®, "s"), SessionState::Idle);
}
#[test]
fn a_journal_end_older_than_a_resume_does_not_end_the_resumed_session() {
let reg = SessionsRegistry::new();
reg.observe_stamped(
observe_request("s", SessionEvent::SessionStart, Some("/p")),
Some(stamp("2026-10-03T03:40:00Z", "1-a")),
Origin::Socket,
);
reg.observe_stamped(
observe_request("s", SessionEvent::SessionStart, None),
Some(stamp("2026-10-03T03:42:00Z", "1-c")),
Origin::Socket,
);
let ended = reg.end_stamped(
"s",
Some("clear"),
None,
Some(stamp("2026-10-03T03:41:00Z", "1-b")),
Origin::Journal,
);
assert!(ended, "the session is known");
assert_eq!(state_of(®, "s"), SessionState::Starting);
reg.end_stamped(
"s",
None,
None,
Some(stamp("2026-10-03T03:43:00Z", "1-d")),
Origin::Journal,
);
assert_eq!(state_of(®, "s"), SessionState::Ended);
let ended_at = reg.lock_sessions()["s"].last_seen;
reg.end_stamped(
"s",
None,
None,
Some(stamp("2026-10-03T03:43:00Z", "1-d")),
Origin::Socket,
);
assert_eq!(reg.lock_sessions()["s"].last_seen, ended_at);
}
#[test]
fn a_journal_event_keeps_its_own_timestamp_but_not_its_own_age() {
let reg = SessionsRegistry::new();
let long_ago = Utc::now() - chrono::Duration::hours(2);
let st = EventStamp {
ts: long_ago,
seq: "1-a".to_string(),
};
reg.observe_stamped(
observe_request("s", SessionEvent::UserPromptSubmit, Some("/p")),
Some(st),
Origin::Journal,
);
let listed = reg.list();
assert_eq!(listed.len(), 1, "replay is not reaped on arrival");
assert_eq!(listed[0].last_seen, long_ago, "history must not look fresh");
assert_eq!(listed[0].started_at, long_ago);
reg.clock.advance(just_inside_the_ttl());
assert_eq!(reg.list().len(), 1);
reg.clock.advance(Duration::from_secs(2));
assert!(reg.list().is_empty());
}
#[test]
fn a_journal_timestamp_in_the_future_is_clamped_to_now() {
let reg = SessionsRegistry::new();
let future = Utc::now() + chrono::Duration::hours(1);
reg.observe_stamped(
observe_request("s", SessionEvent::Stop, Some("/p")),
Some(EventStamp {
ts: future,
seq: "1-a".to_string(),
}),
Origin::Journal,
);
assert!(reg.list()[0].last_seen <= Utc::now());
}
#[test]
fn only_the_newest_seqs_are_remembered() {
let reg = SessionsRegistry::new();
for i in 0..=MAX_RECENT_SEQS {
reg.observe_stamped(
observe_request("s", SessionEvent::PreToolUse, Some("/p")),
Some(stamp("2026-10-03T03:40:00Z", &format!("1-{i}"))),
Origin::Socket,
);
}
let guard = reg.lock_sessions();
assert_eq!(guard["s"].recent_seqs.len(), MAX_RECENT_SEQS);
assert!(guard["s"].seq_origin("1-0").is_none());
assert_eq!(
guard["s"].seq_origin(&format!("1-{MAX_RECENT_SEQS}")),
Some(Origin::Socket)
);
}
#[test]
fn a_duplicate_does_not_bump_and_does_not_refresh_liveness() {
let reg = SessionsRegistry::new();
let st = stamp("2026-10-03T03:40:00Z", "1-a");
reg.observe_stamped(
observe_request("s", SessionEvent::PreToolUse, Some("/p")),
Some(st.clone()),
Origin::Socket,
);
let active = reg.lock_sessions()["s"].last_active;
let rx = reg.subscribe_changes();
reg.clock.advance(Duration::from_secs(10));
reg.observe_stamped(
observe_request("s", SessionEvent::PreToolUse, Some("/p")),
Some(st),
Origin::Journal,
);
assert!(!rx.has_changed().unwrap());
assert_eq!(reg.lock_sessions()["s"].last_active, active);
}
#[test]
fn socket_events_are_counted_stamped_or_not() {
let reg = SessionsRegistry::new();
reg.observe_stamped(
observe_request("s", SessionEvent::PreToolUse, Some("/p")),
None,
Origin::Socket,
);
reg.observe_stamped(
observe_request("s", SessionEvent::Stop, None),
Some(stamp("2026-10-03T03:40:00Z", "1-a")),
Origin::Socket,
);
reg.end_stamped("s", None, None, None, Origin::Socket);
assert_eq!(
reg.delivery(),
DeliveryStats {
socket: 3,
recovered: 0,
superseded: 0,
replayed: 0
}
);
}
#[test]
fn the_daemons_own_feeds_are_applied_but_not_counted_as_deliveries() {
let reg = SessionsRegistry::new();
reg.observe(observe_request("s", SessionEvent::PreToolUse, Some("/p")));
reg.observe(observe_request("s", SessionEvent::TranscriptGrew, None));
assert_eq!(reg.lock_sessions()["s"].state, SessionState::Working);
assert!(reg.end("s", None, None));
assert_eq!(reg.lock_sessions()["s"].state, SessionState::Ended);
assert_eq!(reg.delivery(), DeliveryStats::default());
}
#[test]
fn a_journal_event_whose_post_never_came_is_recovered_and_a_replay_is_not() {
let reg = SessionsRegistry::new();
reg.observe_stamped(
observe_request("s", SessionEvent::SessionStart, Some("/p")),
Some(stamp("2026-10-03T03:40:00Z", "1-a")),
Origin::Replay,
);
reg.observe_stamped(
observe_request("s", SessionEvent::PreToolUse, None),
Some(stamp("2026-10-03T03:40:05Z", "1-b")),
Origin::Journal,
);
reg.end_stamped(
"s",
None,
None,
Some(stamp("2026-10-03T03:40:09Z", "1-c")),
Origin::Journal,
);
assert_eq!(
reg.delivery(),
DeliveryStats {
socket: 0,
recovered: 2,
superseded: 0,
replayed: 1
}
);
}
#[test]
fn a_socket_copy_that_lost_the_race_to_the_poll_is_not_a_recovery() {
let reg = SessionsRegistry::new();
let st = stamp("2026-10-03T03:40:00Z", "1-a");
reg.observe_stamped(
observe_request("s", SessionEvent::PreToolUse, Some("/p")),
Some(st.clone()),
Origin::Journal,
);
assert_eq!(reg.delivery().recovered, 1, "provisionally recovered");
reg.observe_stamped(
observe_request("s", SessionEvent::PreToolUse, None),
Some(st),
Origin::Socket,
);
assert_eq!(
reg.delivery(),
DeliveryStats {
socket: 0,
recovered: 0,
superseded: 0,
replayed: 0
}
);
}
#[test]
fn the_journal_copy_of_a_socket_event_counts_for_nothing() {
let reg = SessionsRegistry::new();
let st = stamp("2026-10-03T03:40:00Z", "1-a");
reg.observe_stamped(
observe_request("s", SessionEvent::PreToolUse, Some("/p")),
Some(st.clone()),
Origin::Socket,
);
reg.observe_stamped(
observe_request("s", SessionEvent::PreToolUse, None),
Some(st.clone()),
Origin::Journal,
);
reg.observe_stamped(
observe_request("s", SessionEvent::PreToolUse, None),
Some(st),
Origin::Socket,
);
assert_eq!(
reg.delivery(),
DeliveryStats {
socket: 1,
recovered: 0,
superseded: 0,
replayed: 0
}
);
}
#[test]
fn an_orphan_journal_end_is_not_counted_as_a_recovery() {
let reg = SessionsRegistry::new();
reg.end_stamped(
"ghost",
None,
None,
Some(stamp("2026-10-03T03:41:00Z", "2-a")),
Origin::Journal,
);
assert_eq!(reg.delivery(), DeliveryStats::default());
}
fn session_at_0340_05() -> SessionsRegistry {
let reg = SessionsRegistry::new();
reg.observe_stamped(
observe_request("s", SessionEvent::Stop, Some("/p")),
Some(stamp("2026-10-03T03:40:05Z", "1-b")),
Origin::Socket,
);
reg
}
#[test]
fn a_dropped_post_that_a_newer_event_overtook_is_superseded() {
let reg = session_at_0340_05();
let dropped = stamp("2026-10-03T03:40:00Z", "1-a");
reg.observe_stamped(
observe_request("s", SessionEvent::PreToolUse, None),
Some(dropped.clone()),
Origin::Journal,
);
assert_eq!(
reg.delivery(),
DeliveryStats {
socket: 1,
recovered: 0,
superseded: 1,
replayed: 0
}
);
reg.observe_stamped(
observe_request("s", SessionEvent::PreToolUse, None),
Some(dropped),
Origin::Journal,
);
assert_eq!(reg.delivery().superseded, 1);
assert_eq!(
reg.lock_sessions()["s"].state,
SessionState::Idle,
"and the newer event still stands"
);
}
#[test]
fn a_stale_journal_end_whose_post_never_came_is_superseded() {
let reg = session_at_0340_05();
reg.end_stamped(
"s",
None,
None,
Some(stamp("2026-10-03T03:40:00Z", "1-a")),
Origin::Journal,
);
assert_eq!(reg.delivery().superseded, 1);
assert_eq!(reg.lock_sessions()["s"].state, SessionState::Idle);
}
#[test]
fn a_late_post_takes_a_superseded_event_back_out() {
let reg = session_at_0340_05();
let late = stamp("2026-10-03T03:40:00Z", "1-a");
reg.observe_stamped(
observe_request("s", SessionEvent::PreToolUse, None),
Some(late.clone()),
Origin::Journal,
);
assert_eq!(reg.delivery().superseded, 1, "provisionally dropped");
for _ in 0..2 {
reg.observe_stamped(
observe_request("s", SessionEvent::PreToolUse, None),
Some(late.clone()),
Origin::Socket,
);
}
assert_eq!(
reg.delivery(),
DeliveryStats {
socket: 1,
recovered: 0,
superseded: 0,
replayed: 0
}
);
}
#[test]
fn a_post_late_enough_to_be_applied_is_a_delivery_after_all() {
let reg = session_at_0340_05();
let late = stamp("2026-10-03T03:39:00Z", "1-a");
reg.observe_stamped(
observe_request("s", SessionEvent::PreToolUse, None),
Some(late.clone()),
Origin::Journal,
);
reg.observe_stamped(
observe_request("s", SessionEvent::PreToolUse, None),
Some(late),
Origin::Socket,
);
assert_eq!(
reg.delivery(),
DeliveryStats {
socket: 2,
recovered: 0,
superseded: 0,
replayed: 0
}
);
}
#[test]
fn the_journal_copy_of_a_socket_event_dropped_as_out_of_order_is_not_a_drop() {
let reg = session_at_0340_05();
let reordered = stamp("2026-10-03T03:40:00Z", "1-a");
reg.observe_stamped(
observe_request("s", SessionEvent::PreToolUse, None),
Some(reordered.clone()),
Origin::Socket,
);
reg.observe_stamped(
observe_request("s", SessionEvent::PreToolUse, None),
Some(reordered),
Origin::Journal,
);
assert_eq!(
reg.delivery(),
DeliveryStats {
socket: 1,
recovered: 0,
superseded: 0,
replayed: 0
}
);
}
#[test]
fn a_stale_replayed_event_is_counted_as_nothing() {
let reg = session_at_0340_05();
reg.observe_stamped(
observe_request("s", SessionEvent::PreToolUse, None),
Some(stamp("2026-10-03T03:40:00Z", "1-a")),
Origin::Replay,
);
assert_eq!(
reg.delivery(),
DeliveryStats {
socket: 1,
recovered: 0,
superseded: 0,
replayed: 0
}
);
}
#[test]
fn superseded_events_are_remembered_only_up_to_the_ring_size() {
let reg = session_at_0340_05();
for i in 0..=MAX_RECENT_SEQS {
reg.observe_stamped(
observe_request("s", SessionEvent::PreToolUse, None),
Some(stamp("2026-10-03T03:40:00Z", &format!("9-{i}"))),
Origin::Journal,
);
}
assert_eq!(reg.delivery().superseded, MAX_RECENT_SEQS as u64 + 1);
let guard = reg.lock_sessions();
assert_eq!(guard["s"].superseded_seqs.len(), MAX_RECENT_SEQS);
assert!(!guard["s"].superseded_seqs.contains(&"9-0".to_string()));
}
#[test]
fn delivery_stats_serialize_with_the_documented_names() {
let value = serde_json::to_value(DeliveryStats {
socket: 5,
recovered: 2,
superseded: 3,
replayed: 1,
})
.unwrap();
assert_eq!(
value,
serde_json::json!({ "socket": 5, "recovered": 2, "superseded": 3, "replayed": 1 })
);
}
#[test]
fn the_socket_copy_of_a_replayed_event_takes_nothing_back_from_recoveries() {
let reg = SessionsRegistry::new();
let replayed = stamp("2026-10-03T03:40:00Z", "1-a");
reg.observe_stamped(
observe_request("s", SessionEvent::SessionStart, Some("/p")),
Some(replayed.clone()),
Origin::Replay,
);
reg.observe_stamped(
observe_request("s", SessionEvent::PreToolUse, None),
Some(stamp("2026-10-03T03:40:05Z", "1-b")),
Origin::Journal,
);
reg.observe_stamped(
observe_request("s", SessionEvent::SessionStart, None),
Some(replayed),
Origin::Socket,
);
assert_eq!(
reg.delivery(),
DeliveryStats {
socket: 0,
recovered: 1,
superseded: 0,
replayed: 1
},
"the real recovery is not masked"
);
}
#[test]
fn repeated_socket_copies_of_one_raced_event_take_it_back_only_once() {
let reg = SessionsRegistry::new();
let raced = stamp("2026-10-03T03:40:00Z", "1-a");
reg.observe_stamped(
observe_request("s", SessionEvent::PreToolUse, Some("/p")),
Some(raced.clone()),
Origin::Journal,
);
reg.observe_stamped(
observe_request("s", SessionEvent::PostToolUse, None),
Some(stamp("2026-10-03T03:40:05Z", "1-b")),
Origin::Journal,
);
assert_eq!(reg.delivery().recovered, 2);
for _ in 0..2 {
reg.observe_stamped(
observe_request("s", SessionEvent::PreToolUse, None),
Some(raced.clone()),
Origin::Socket,
);
}
assert_eq!(reg.delivery().recovered, 1, "the other recovery stands");
}
#[test]
fn a_socket_end_counts_even_for_a_session_the_registry_does_not_hold() {
let reg = SessionsRegistry::new();
reg.end_stamped(
"ghost",
None,
None,
Some(stamp("2026-10-03T03:41:00Z", "9-a")),
Origin::Socket,
);
assert_eq!(reg.delivery().socket, 1);
}
}