Files
hermes-webui/tests
happy5318 54f7d97d57 fix(models): bind stale-cancel suppression to turn identity and final answers
The #6366 suppression was too broad and could destroy data in two ways,
both verified with full `_apply_core_sync_or_error_marker()` probes:

1. A repeated prompt could be silently discarded. Checkpoint matching
   truncates timestamps to integer seconds and ignored
   `_active_turn_token`, so a prior identical prompt from a *different*
   stream could match the new pending turn — and the suppression branch
   then cleared the only durable copy of the prompt, with no recovered
   row and no error marker.

   Identity is now bound to the pending stream's exact active-turn token
   (`_transcript_user_row_is_pending_turn`). Integer-second timestamp
   equality never overrides a token mismatch, and a row that carries no
   token cannot be proven to be this turn. Legacy sessions with no token
   on either side fall back to the strict content + timestamp + source +
   attachments checkpoint.

2. Interrupted tool-running turns were treated as completed. Any
   unflagged assistant row counted as settled, including empty rows,
   tool-call-only rows, interim-progress rows and compaction rows, so
   `:3600` bypassed journal replay and the interruption marker.

   Completion now reuses the established final-answer semantics
   (`_pending_turn_has_final_assistant_answer` ->
   `_assistant_message_has_final_visible_text`) inside the matched
   user-turn boundary, rejecting tool-only, empty, interim, journal-
   recovered and compaction tails.

The suppression is deliberately asymmetric: a false negative leaves a
visible cosmetic duplicate the user can ignore, while a false positive
silently destroys a prompt or a response. Whenever identity or
completion cannot be positively proven, recovery runs instead of
suppression.

Tests: the 11 predicate unit cases are re-derived against the new
identity/completion helpers, plus full-recovery-path cases that exercise
`_apply_core_sync_or_error_marker()` itself — distinct same-second
repeated turns (token-bearing and legacy) proving the prompt survives,
and empty / tool-call-only / interim / compaction tails proving journal
replay and the interruption marker still run — plus a positive control
for the suppression branch itself.
2026-09-21 22:41:55 +08:00
..