aaif-goose / aaif-goose/goose

Resumed sessions stamp new messages with stale timestamp from previous last message

Abierto
#12,003 0 comentarios 0 reacciones 1 asignado Reclamado por @jbg Ver en GitHub
Lenguaje dominante
Rust
Estrellas
54.2k
Forks
6.2k
Merge medio
3 d 2 h
PR fusionados (30 d)
262

Descripción

## Summary

When a user reopens an existing chat session after restarting Goose, every new message's integer column gets stamped with the timestamp of the session's last pre-resume message instead of the actual current time. The string column remains correct. Since the UI sorts and filters by the integer column, hours or days of new conversation sort into the past and appear missing from the chat view.

## Root cause

In `crates/goose/src/session/session_manager.rs`, line ~1925:

```rust
let latest: Option =
sqlx::query_scalar("SELECT MAX(created_timestamp) FROM messages WHERE session_id = ?")
.bind(session_id)
.fetch_one(&mut *tx)
.await?;
let created = message.created.max(latest.unwrap_or(message.created));
```

`Message::user()` and `Message::assistant()` both set `created = Utc::now().timestamp()` (current time in seconds). But the persist code takes `.max()` of the new message's timestamp and `MAX(created_timestamp)` from existing messages to ensure messages never sort ahead of what's already stored.

The bug fires when the existing max timestamp is stale or uses a different unit (milliseconds from an older Goose version). If `MAX(created_timestamp)` returns a stale value that is larger than the current time (e.g. a millisecond timestamp from an older version, or a previously corrupted value), `.max()` picks the stale value and every new message inherits it.

## Reproduction

1. Create a session and send several messages
2. Note the last message's `created_timestamp` value
3. Quit Goose
4. Reopen Goose and resume the same session
5. Send new messages
6. Check `created_timestamp` vs `timestamp` in the messages table for the new rows

All new rows carry the same stale value from the session's last pre-resume message, while the string `timestamp` column shows correct per-message times.

## Evidence

- 32 of 319 sessions on the reporting machine exhibit this stale-stamp signature
- The stale value always equals the session's final pre-resume message timestamp
- The behavior recurs on every resume of affected sessions, surviving upgrades from 1.48.0 to 1.50.0
- Users believe data is lost because the UI sorts by the corrupted column, hiding recent messages

## Proposed fix

Normalize the `latest` value to seconds before comparing with `message.created`, using the existing `MILLISECOND_TIMESTAMP_THRESHOLD` constant (10_000_000_000):

```rust
let latest_normalized = latest.map(|t| {
if t > MILLISECOND_TIMESTAMP_THRESHOLD { t / 1000 } else { t }
}).unwrap_or(message.created);
let created = message.created.max(latest_normalized);
```

This ensures the `.max()` comparison operates on the same unit scale, preventing stale or mismatched-unit values from clobbering new timestamps.

Guía de contribución

Abrir la guía de contribución

Evaluación

Este issue todavía no se ha evaluado.

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.