master
1
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
e8f1bb10da |
Prune orphan native-WebUI zero-message sessions from sidebar (#4985)
Sidebar rows whose backing state.db session has zero messages (a '+'-click that opened a row but the first turn never committed, or a sidebar nav that opened then closed before any message landed) now get pruned instead of lingering forever. The existing #3238/#4591 orphan-prune path only handled CLI/API-server sidecars; this adds a parallel pass for native-WebUI rows. - api/models.py: add agent_session_zero_message_sids() batch helper that mirrors agent_session_rows_existing()'s safe-degrade contract (returns frozenset() on any error so a transient failure never causes data loss). Add a small tombstone file at SESSION_DIR/_pruned_webui_orphans.json (capped at the last 500 sids) so the next poll's recover_missing_index_sidecars does not re-add a pruned orphan to the sidebar index — the per-poll fsync'd index write + state.db probe loop the original PR would have hit on every poll for every orphan. The helper, the load, and the full-scan fallback all honor the tombstone. new_session() and import_cli_session() clear any matching tombstone entry so a fresh session with the same id isn't shadowed. - api/routes.py: extract the webui zero-message prune into a private _prune_orphaned_webui_zero_message_sessions() helper, then call it from BOTH branches of _build_session_list_cache_payload. Established installs pin show_cli_sessions=False (per api/config.py:7637-7648) and are exactly the long-time users who accumulated the #4985 404 orphans — the original PR only ran the prune in the if branch and silently skipped the else branch (maintainer review IC_kwDOR1LuPM8AAAABHsyFGg). The helper also honors the tombstone defensively so the prune can't thrash on a row the index still carries from a prior poll. - tests/test_issue4985_orphaned_webui_zero_message.py: 39 regression tests — the original 25 (7 helper-direct + 12 monkeypatch predicate tests + 6 real-pipeline end-to-end tests) plus 14 follow-up tests driven by the MUST-FIX / tombstone / r6 signal-swap reviews: * test_helper_prunes_orphan_keeps_safety_retained_rows — direct call to the extracted helper with a row set covering every shape the gate is responsible for. * test_real_all_sessions_post1171_prune_fires_when_show_cli_sessions_false — explicit regression for the established-install case the original PR missed. * test_real_all_sessions_post1171_prune_fires_when_show_cli_sessions_true — companion positive control. * test_tombstone_persists_across_polls — drives two pipeline builds, asserts prune fires once total and the tombstoned sid is NOT re-added to SESSION_INDEX_FILE. * test_tombstone_does_not_block_new_session_with_same_id — defensive cover for the new_session() / import_cli_session() tombstone clear. * test_tombstone_trimmed_to_last_N_entries — verifies the cap. * test_real_all_sessions_post1171_prune_fires_when_show_cli_sessions_true — companion positive control (show_cli_sessions=True path). * test_tombstone_self_heals_when_message_added — confirms Session.save()'s belt-and-suspenders tombstone clear fires when a sidecar commit lands with len(messages) > 0. * test_tombstone_loader_is_fail_open_on_corrupt_file[non-json|version-mismatch|non-dict-root] — 3-parameter regression for the fail-open corrupt-file contract (#5023 lesson). * test_sidecar_only_webui_session_is_retained — sidecar JSON carries real messages but state.db.messages is empty (mirroring lag); the r6 loaded-sidecar probe retains the row. * test_sidecar_only_webui_session_self_heals_tombstone — the r4.5 self-heal path combined with the r6 sidecar-only retention rule. * test_truly_empty_sidecar_with_title_still_pruned — companion negative test that the r6 fix does NOT over-retain empty sidecars with non-Untitled titles. * test_sidecar_only_webui_session_retained_when_show_cli_sessions_false — established-install path also respects the r6 retention rule. - CHANGELOG.md: Unreleased entry under Fixed. fix: target post-#1171 survivors + hoist + tombstone + r6 sidecar-load probe The r5 sidecar-only retention signal keyed off the row's cached ``message_count`` — which is stale-positive on the very phantom rows #4985 exists to prune (sidecar ``messages`` empty but cached count > 0). The r6 signal swaps that cached-count probe for a ``Session.load(sid) .messages`` probe, gated to ``state.db``-empty candidates only so the common live-row path stays a no-op. Three retain tests now write real populated ``messages`` arrays (not just ``[]``) so the loaded-sidecar check has something to find; the stale-count phantom test is left as the canonical r6 regression (its ``messages=[]`` + ``message_count=5`` sidecar is exactly the shape the r6 loaded-sidecar check correctly classifies as orphan). Maintainer review 4584722701. |