Skip to content

Commit dbc2824

Browse files
committed
Keep latest session selection tied to real session recency
The next repo-local sweep target was ROADMAP ultraworkers#72: the `latest` managed-session alias could depend on filesystem mtime before the session's own persisted recency markers, which made the selection path vulnerable to coarse or misleading file timestamps. The fix promotes `updated_at_ms` into the summary/order path, keeps CLI wrappers in sync, and locks the mtime-vs-session-recency case with regression coverage. Constraint: Preserve existing managed-session storage layout while changing only the ordering signal Rejected: Keep sorting by filesystem mtime and just sleep longer in tests | hides the semantic ordering bug instead of fixing it Confidence: high Scope-risk: narrow Reversibility: clean Directive: Any future managed-session ordering change must keep runtime and CLI summary structs aligned on the same recency fields Tested: cargo fmt --all --check; cargo clippy --workspace --all-targets -- -D warnings; cargo test --workspace; architect review APPROVE Not-tested: Cross-filesystem behavior where persisted session JSON cannot be read and fallback ordering uses mtime only
1 parent f309ff8 commit dbc2824

3 files changed

Lines changed: 48 additions & 6 deletions

File tree

ROADMAP.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -515,3 +515,5 @@ Model name prefix now wins unconditionally over env-var presence. Regression tes
515515
70. **Install-source ambiguity misleads real users****done (verified 2026-04-12):** repo-local Rust guidance now makes the source of truth explicit in `claw doctor` and `claw --help`, naming `ultraworkers/claw-code` as the canonical repo and warning that `cargo install claw-code` installs a deprecated stub rather than the `claw` binary. Regression coverage locks both the new doctor JSON check and the help-text warning. **Original filing below.**
516516

517517
71. **Wrong-task prompt receipt is not detected before execution****done (verified 2026-04-12):** worker boot prompt dispatch now accepts an optional structured `task_receipt` (`repo`, `task_kind`, `source_surface`, `expected_artifacts`, `objective_preview`) and treats mismatched visible prompt context as a `WrongTask` prompt-delivery failure before execution continues. The prompt-delivery payload now records `observed_prompt_preview` plus the expected receipt, and regression coverage locks both the existing shell/wrong-target paths and the new KakaoTalk-style wrong-task mismatch case. **Original filing below.**
518+
519+
72. **`latest` managed-session selection depends on filesystem mtime before semantic session recency****done (verified 2026-04-12):** managed-session summaries now carry `updated_at_ms`, `SessionStore::list_sessions()` sorts by semantic recency before filesystem mtime, and regression coverage locks the case where `latest` must prefer the newer session payload even when file mtimes point the other way. The CLI session-summary wrapper now stays in sync with the runtime field so `latest` resolution uses the same ordering signal everywhere. **Original filing below.**

rust/crates/runtime/src/session_control.rs

Lines changed: 43 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -144,12 +144,7 @@ impl SessionStore {
144144
if let Some(legacy_root) = self.legacy_sessions_root() {
145145
self.collect_sessions_from_dir(&legacy_root, &mut sessions)?;
146146
}
147-
sessions.sort_by(|left, right| {
148-
right
149-
.modified_epoch_millis
150-
.cmp(&left.modified_epoch_millis)
151-
.then_with(|| right.id.cmp(&left.id))
152-
});
147+
sort_managed_sessions(&mut sessions);
153148
Ok(sessions)
154149
}
155150

@@ -260,6 +255,7 @@ impl SessionStore {
260255
ManagedSessionSummary {
261256
id: session.session_id,
262257
path,
258+
updated_at_ms: session.updated_at_ms,
263259
modified_epoch_millis,
264260
message_count: session.messages.len(),
265261
parent_session_id: session
@@ -279,6 +275,7 @@ impl SessionStore {
279275
.unwrap_or("unknown")
280276
.to_string(),
281277
path,
278+
updated_at_ms: 0,
282279
modified_epoch_millis,
283280
message_count: 0,
284281
parent_session_id: None,
@@ -322,12 +319,23 @@ pub struct SessionHandle {
322319
pub struct ManagedSessionSummary {
323320
pub id: String,
324321
pub path: PathBuf,
322+
pub updated_at_ms: u64,
325323
pub modified_epoch_millis: u128,
326324
pub message_count: usize,
327325
pub parent_session_id: Option<String>,
328326
pub branch_name: Option<String>,
329327
}
330328

329+
fn sort_managed_sessions(sessions: &mut [ManagedSessionSummary]) {
330+
sessions.sort_by(|left, right| {
331+
right
332+
.updated_at_ms
333+
.cmp(&left.updated_at_ms)
334+
.then_with(|| right.modified_epoch_millis.cmp(&left.modified_epoch_millis))
335+
.then_with(|| right.id.cmp(&left.id))
336+
});
337+
}
338+
331339
#[derive(Debug, Clone, PartialEq, Eq)]
332340
pub struct LoadedManagedSession {
333341
pub handle: SessionHandle,
@@ -598,6 +606,35 @@ mod tests {
598606
.expect("session summary should exist")
599607
}
600608

609+
#[test]
610+
fn latest_session_prefers_semantic_updated_at_over_file_mtime() {
611+
let mut sessions = vec![
612+
ManagedSessionSummary {
613+
id: "older-file-newer-session".to_string(),
614+
path: PathBuf::from("/tmp/older"),
615+
updated_at_ms: 200,
616+
modified_epoch_millis: 100,
617+
message_count: 2,
618+
parent_session_id: None,
619+
branch_name: None,
620+
},
621+
ManagedSessionSummary {
622+
id: "newer-file-older-session".to_string(),
623+
path: PathBuf::from("/tmp/newer"),
624+
updated_at_ms: 100,
625+
modified_epoch_millis: 200,
626+
message_count: 1,
627+
parent_session_id: None,
628+
branch_name: None,
629+
},
630+
];
631+
632+
crate::session_control::sort_managed_sessions(&mut sessions);
633+
634+
assert_eq!(sessions[0].id, "older-file-newer-session");
635+
assert_eq!(sessions[1].id, "newer-file-older-session");
636+
}
637+
601638
#[test]
602639
fn creates_and_lists_managed_sessions() {
603640
// given

rust/crates/rusty-claude-cli/src/main.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3122,6 +3122,7 @@ struct SessionHandle {
31223122
struct ManagedSessionSummary {
31233123
id: String,
31243124
path: PathBuf,
3125+
updated_at_ms: u64,
31253126
modified_epoch_millis: u128,
31263127
message_count: usize,
31273128
parent_session_id: Option<String>,
@@ -4711,6 +4712,7 @@ fn list_managed_sessions() -> Result<Vec<ManagedSessionSummary>, Box<dyn std::er
47114712
.map(|session| ManagedSessionSummary {
47124713
id: session.id,
47134714
path: session.path,
4715+
updated_at_ms: session.updated_at_ms,
47144716
modified_epoch_millis: session.modified_epoch_millis,
47154717
message_count: session.message_count,
47164718
parent_session_id: session.parent_session_id,
@@ -4726,6 +4728,7 @@ fn latest_managed_session() -> Result<ManagedSessionSummary, Box<dyn std::error:
47264728
Ok(ManagedSessionSummary {
47274729
id: session.id,
47284730
path: session.path,
4731+
updated_at_ms: session.updated_at_ms,
47294732
modified_epoch_millis: session.modified_epoch_millis,
47304733
message_count: session.message_count,
47314734
parent_session_id: session.parent_session_id,

0 commit comments

Comments
 (0)