HERMES-GIELIE/api
nesquena-hermes 057307b3f9
fix(session): retire stale truncation watermark on new committed turn (#3831) (#3890)
* fix(session): retire stale truncation watermark on new committed turn (#3831)

retry_last / undo_last / the Edit-truncate handler set truncation_watermark
to suppress the *replaced* tail from the append-only state.db merge.
Session.save() deliberately never auto-clears it (#2914), but nothing retired
it when the user then sent a genuinely NEW turn either — so it froze at the old
edit boundary. A frozen watermark then dropped post-watermark state.db rows
whenever the sidecar was later reconstructed empty (recovery/reconcile),
permanently losing the turns sent after the edit (state.db still had them).

Retire a POSITIVE watermark to None once the new user turn is COMMITTED to
session.messages — at the success-merge (3 sites), eager-checkpoint, error/
recovery materialization, and cold-load repair commit points. Not at chat-start:
in deferred mode the new row isn't in messages yet, so a merge in that window
would resurrect the replaced tail (the max-sidecar guard hasn't risen past the
old boundary). Once committed, max_sidecar_timestamp rises past the replaced
tail and the merge suppresses it without the watermark, so retiring is safe.

Cleared to None, never 0.0 — 0.0 is the truncate-to-empty sentinel (#2914) that
must keep blocking all state replay, so the clear is falsy-gated.

Closes #3831

* chore(changelog): clarify watermark-retirement timing to commit-time

Greptile review noted the original phrase "retires the watermark at the
start of a new user turn" was timing-imprecise. The retirement actually
fires when the new turn is durably committed to session.messages —
at the agent-result merge, the eager user-message checkpoint, or the
cold-load recovery commit. Reword for accuracy; semantics unchanged.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* test(#3831): add regression tests for the two inline watermark-clear paths (greptile P2)

Cover the error/cancel materialization path (_materialize_pending_user_turn_before_error)
and the eager first-turn checkpoint path (_checkpoint_user_message_for_eager_session_save),
which inline the falsy-gated watermark clear instead of calling the tested helper.
The error path is precisely the #3831 failure mode (recovery/reconcile after a
crash), so a dedicated regression test closes that gap. Both assert a positive
watermark clears to None while the 0.0 truncate-to-empty sentinel (#2914) is
preserved.

---------

Co-authored-by: nesquena-hermes <[email protected]>
Co-authored-by: Nathan Esquenazi <nesquena@gmail.com>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-09 12:39:32 -07:00
..
__init__.py
agent_health.py Release v0.51.260 — Release IB (stage-r8) (#3614) 2026-06-04 15:21:41 -07:00
agent_sessions.py fix(sidebar): prime idx_messages_session before CLI-session scan (#3887) (#3888) 2026-06-09 12:35:13 -07:00
auth.py Release v0.51.260 — Release IB (stage-r8) (#3614) 2026-06-04 15:21:41 -07:00
background.py
background_process.py [HELD — independent review pending] Release v0.51.340 — bg_task agent wakeup (trio #2968+#2971+#2979) (#3867) 2026-06-08 22:36:18 -07:00
clarify.py feat: add distinct attention request sound 2026-05-30 19:40:40 +00:00
commands.py Release v0.51.263 — Release IE (stage-r13) (#3625) 2026-06-04 23:56:44 -07:00
compression_anchor.py fix(sidebar): keep newer continuation visible over older snapshot 2026-05-28 13:38:06 -06:00
config.py [HELD — independent review pending] Release v0.51.340 — bg_task agent wakeup (trio #2968+#2971+#2979) (#3867) 2026-06-08 22:36:18 -07:00
dashboard_probe.py fix: preserve IPv6 dashboard link brackets 2026-05-19 21:35:10 -04:00
extensions.py
gateway_chat.py [HELD — independent review pending] Release v0.51.294 — stage-3401 (live-to-final redesign #3401 + 4 deep-review fixes) (#3741) 2026-06-06 12:12:37 -07:00
gateway_watcher.py Release v0.51.256 — Release HX (stage-r4) (#3596) 2026-06-04 12:28:06 -07:00
goals.py
helpers.py Release v0.51.317 — Release KG (Phase 3 light: align CSP enforcement with report policy, #3727) (#3786) 2026-06-07 10:52:07 -07:00
kanban_bridge.py Release v0.51.330 — Release KT (#3716 api docstring backfill, partial) (#3841) 2026-06-08 12:22:22 -07:00
metering.py
models.py fix(session): retire stale truncation watermark on new committed turn (#3831) (#3890) 2026-06-09 12:39:32 -07:00
oauth.py Release v0.51.330 — Release KT (#3716 api docstring backfill, partial) (#3841) 2026-06-08 12:22:22 -07:00
onboarding.py
passkeys.py Release v0.51.270 — Release IL (stage-u1 — un-hold batch: #3517 #3624 #3613) (#3674) 2026-06-05 11:35:26 -07:00
paths.py fix(profiles): avoid config import cycle 2026-06-01 05:05:16 +00:00
plugin_providers.py Release v0.51.270 — Release IL (stage-u1 — un-hold batch: #3517 #3624 #3613) (#3674) 2026-06-05 11:35:26 -07:00
plugins.py fix(#2622): plugin card UX — legible Open button, single badge, visible toggle + reject protocol-relative tab.path 2026-06-02 03:58:02 +00:00
profiles.py Release v0.51.313 — Release KC (instant profile switcher — skip per-profile alias scan) (#3779) 2026-06-07 09:27:32 -07:00
providers.py Release v0.51.318 — Release KH (Phase 3 light: warm account-usage probe worker pool, #3722) (#3792) 2026-06-07 12:07:13 -07:00
request_diagnostics.py
rollback.py
route_approvals.py Release v0.51.275 — Release IQ (stage-p3d — approval SSE state extraction #3575) (#3681) 2026-06-05 13:10:45 -07:00
routes.py fix(session): retire stale truncation watermark on new committed turn (#3831) (#3890) 2026-06-09 12:39:32 -07:00
run_journal.py Release v0.51.326 — Release KP (#3618 + #3802 + #3762 + #3810) (#3816) 2026-06-07 23:18:51 -07:00
runner_client.py harden: runner client rejects non-http(s) base_url + no redirect-follow (#3073) 2026-05-31 17:49:26 +00:00
runtime_adapter.py Stage 398: PR #2696 — feat(runtime): add runner-local adapter selection (RuntimeAdapter slice 4c, feature-flagged) 2026-05-21 17:43:54 +00:00
session_discoverability.py Merge PR #3048 2026-05-28 17:47:33 +00:00
session_events.py Release v0.51.266 — Release IH (stage-r16) (#3641) 2026-06-05 00:44:03 -07:00
session_lifecycle.py Release v0.51.256 — Release HX (stage-r4) (#3596) 2026-06-04 12:28:06 -07:00
session_ops.py Release v0.51.276 — Release IR (stage-p3e — preserve manually-named session titles #3542) (#3682) 2026-06-05 13:34:54 -07:00
session_recovery.py Release v0.51.329 — Release KS (#3814 O(n) index map + #3815 startup recovery skip) (#3840) 2026-06-08 11:49:26 -07:00
skill_usage.py fix: remove webui-side writer to avoid conflict with agent 2026-05-27 20:57:32 +08:00
startup.py
state_sync.py fix: use Optional[T] for default None parameters in state_sync.py 2026-06-01 17:20:17 +00:00
streaming.py fix(session): retire stale truncation watermark on new committed turn (#3831) (#3890) 2026-06-09 12:39:32 -07:00
system_health.py
terminal.py Release v0.51.304 — Release JT (stage-p2a — un-held terminal reaper + opt-in Docker GPU) (#3757) 2026-06-06 18:04:16 -07:00
todo_state.py Release v0.51.261 — Release IC (stage-r11) (#3616) 2026-06-04 16:46:20 -07:00
turn_journal.py Release v0.51.326 — Release KP (#3618 + #3802 + #3762 + #3810) (#3816) 2026-06-07 23:18:51 -07:00
updates.py Release v0.51.312 — Release KB (brick-wave: purge stale __pycache__ after self-update, fixes #3774) (#3778) 2026-06-07 08:26:36 -07:00
upload.py Release v0.51.326 — Release KP (#3618 + #3802 + #3762 + #3810) (#3816) 2026-06-07 23:18:51 -07:00
usage.py fix: display canonical cache hit percentage 2026-05-19 02:27:12 -06:00
webui_session_db.py Release v0.51.305 — Release JU (stage-p2b — dormant unified-SessionDB adapter) (#3759) 2026-06-06 18:16:07 -07:00
workspace.py Release v0.51.296 — stage-3731 (remote-workspace blocked-root security fix #3731) (#3744) 2026-06-06 13:49:59 -07:00
workspace_git.py Release v0.51.311 — Release KA (brick-wave: workspace Git RCE hardening #3769 + stale-snapshot sidebar visibility #3770) (#3776) 2026-06-07 08:05:30 -07:00
worktrees.py