Skip to content

fix(desktop): promptly evict externally deleted sidebar sessions - #135468

Open
ljhade24 wants to merge 5 commits into
NousResearch:mainfrom
ljhade24:fix/desktop-session-deletion-sync
Open

ljhade24 wants to merge 5 commits into
NousResearch:mainfrom
ljhade24:fix/desktop-session-deletion-sync

Conversation

@ljhade24

@ljhade24 ljhade24 commented Oct 9, 2026 •

Copy link
Copy Markdown

What does this PR do?

Makes committed, externally deleted sessions disappear promptly from Desktop's ordinary, cron, and messaging sidebar lists without waiting for a generic list refresh or interrupting a live transcript/composer.

The existing watcher samples session-table content every 0.5 seconds, but its generic sessions.changed broadcast has a two-second floor and Desktop can defer refetches during interaction. Exact committed row disappearance can evict sidebar IDs without a transcript refresh.

Ready for maintainer review, not a claim of merge readiness: relevant expanded local verification and independent automated code review are complete. Upstream Actions require maintainer action; no green upstream CI, whole-suite success, or native packaged-app deletion E2E is claimed.

Related Issue

No issue is closed by this PR. Related proposals were inspected:

Type of Change

  • Bug fix
  • Tests

Changes Made

  • Retain session IDs alongside the existing mtime-gated SQLite digest and emit typed sessions.deleted { session_ids, profile } between two readable ID snapshots, independently of the generic refresh floor. First sightings, missing stores, foreign schemas, locked reads, and uncommitted writes do not authorize deletion.
  • Declare the event in the public wire model and regenerate shared contracts.
  • Route the event through an active-source-gated Desktop lifecycle handler table. Keep live-sync.ts signal-only; the sidebar action lives in a topical sibling.
  • Evict only matching-profile IDs from ordinary/cron/messaging lists, without mutating transcript state. Protect busy, awaiting-response, live-turn, and input-waiting writers, including runtime-only IDs and compression aliases in all three lists. Unknown owners remain protected; proven foreign-profile/source writers do not block an owned twin.
  • Fence pre-event list and project-tree/drill-in reads with profile-owned immutable removal generations, including unseen IDs. Do not put external deletions into the legacy ID-only optimistic tombstone overlay: a different profile can own the same stored ID. Fresh post-event authoritative reads can admit restored IDs.
  • Extend behavior tests for unseen foreign twins, loaded collisions, source isolation, stale pages, project previews, live dedicated-list lineage tips, and unreadable/uncommitted SQLite snapshots.

How to Test

  1. Connect Desktop to a backend/profile and leave a completed session visible. Keep unsent text in a composer.
  2. Delete the stored session using another supported Hermes surface/process.
  3. Verify its ordinary/cron/messaging sidebar row disappears on the next watcher sample without navigation, focus changes, or transcript rehydration. A live local writer and another profile's row must remain untouched.

Verification

Final head: 9097781471a47ae71513c9bd0e445249aeee02ed.
Baseline: 1744a19e0df568c647e4f3ff9c37f2a284a282fb.

Tests were run on macOS arm64 in an isolated development checkout. Baseline checks used a separate detached worktree, run serially after head checks; no stash/restore overlap. Source remained unchanged during every final verification step.

Check Head result Baseline / bounds
Relevant expanded Python: watcher, contracts, profile listing, projects/tree, lineage RPC, shell/exit/encoding; 14 files 141 passed, 12 failed 135 passed, the same 12 exact failing test IDs on base; no new failures in this scope
Expanded Desktop renderer: all src/store/ and src/app/session/hooks/ tests 3,240 passed, 274 files 3,228 passed, 273 files on base
Four directly affected renderer files 119 passed Includes the new ownership, project-page and writer-lineage regressions
Desktop typecheck Passed Renderer, Electron, E2E config, builder config
ESLint on changed Desktop TypeScript Passed Changed project tests also checked
python scripts/check --base <baseline> 11 checks passed Contract/code-health/guard checks
Desktop source production build Passed Not packaging or a native app launch
git diff --check; added-line credential/path/injection scan Passed; no scan matches Not a security audit
Fresh-context, read-only independent automated review Passed, no blocking findings Not maintainer approval

The original committed-deletion regression was demonstrated RED on the unmodified watcher. Follow-up RED tests exposed profile-blind stale-page fencing, a foreign writer blocking an owned twin, stale project-tree previews, and live lineage tips in dedicated lists; the final focused run passes.

Verification limits and CI

  • The 12 Python failures are baseline-reproduced profile-list/shell/exit/encoding failures, including isolated-checkout home-I/O guard errors. Neither the full Python suite nor a fully green Python run is claimed.
  • The Desktop result above is the complete store + session-hook scope, not the entire Desktop UI suite. Earlier incomplete whole-suite attempts are not used as evidence.
  • The baseline UI initially failed during config loading because the dependency copy lacked workspace-local node_modules. After restoring those isolated dependencies, the same scope ran to completion and passed; that startup failure is not counted as a test result.
  • Native packaged-app launch, real end-to-end websocket/native deletion UX, and cross-platform native E2E have not been verified.
  • On this head, upstream CI, Docker and Nix runs report action_required with no reported check runs. The contributor cannot approve upstream workflows; the repository's approval-policy details return HTTP 403. Please approve/inspect the pending workflow runs. An empty checks list is not a green build.

Checklist

  • Read CONTRIBUTING and applicable AGENTS guides; duplicate/overlap review completed.
  • Conventional commits; generic scope; no new settings, tools, plugin hooks, polling loops, or third-party policy.
  • Added behavior tests; regression RED/GREEN evidence retained.
  • Relevant expanded local tests compared with an isolated baseline.
  • Blocking local lint/code-health checks and Desktop source build pass.
  • Independent automated review completed and blocking findings fixed.
  • Public event contract and generated artifacts updated.
  • Upstream CI completed successfully after maintainer workflow action.
  • Complete Python and Desktop suites pass.
  • Native packaged-app deletion E2E verified.

Screenshots / Logs

Behavior tests use real temporary SQLite stores and renderer stores/lifecycle routing. No production data or private local paths are included. The local receipt preserves exact commands, exit codes, immutable-tree checks, baseline failure-ID comparison, JSON renderer reports, RED/GREEN logs and independent-review results.

Conflict refresh — 0d2d45a

Earlier test sections are historical pre-refresh evidence. This commit merges frozen upstream e06df9c2eae6901f4c88f464e0ae6e753c0428b8; published tree exactly matches the tested/reviewed candidate.

Retained both upstream onGatewayReady/onboarding handling and deletion dispatch/profile fences. Regenerated OpenRPC from Python contracts, preserving SessionsDeletedPayload and upstream SetupBeat; generated TypeScript freshness checked. Renderer scope: 284 files / 3291 passed. Broader prior Python path scope: 153 passed, no failures; focused 116-pass rerun overlaps, not additive. Typecheck, relevant eight-file ESLint, scripts/check 12 checks, regeneration/freshness passed. Earlier 12 Python failures did not reproduce in this isolated scoped run; root-cause attribution or whole-suite success is not claimed. Native packaged deletion E2E remains unverified; existing non-Draft review status retained.

Independent static review found no security/logic blockers; this does not imply the reviewer executed suites. PR-delta whitespace is clean; unrelated upstream whitespace findings were retained. Latest main d97007c32d6340f7ba79ac1dfb5773cc00846448 was separately checked with merge-tree (exit 0); its newer delta is release-tool scripts/tests. This is a conflict check, not runtime validation of a synthetic combined tree. Exact-head GitHub readback reported mergeable=true, mergeable_state=blocked; content conflicts are cleared, but review/design/workflow gates remain. No upstream PR merge, production code/database change, app edit or restart performed.

Recurrent conflict refresh — 6f61fff

Merged frozen upstream 7bda2a3368024f7ae1c70cef16583e7c2c891674. Lifecycle imports now preserve both session-deletion dispatch and upstream owner-aware replay-gap recovery. Exact published tree 26b17c1f7fd70496569a68e5e6a6350f7549fe5b matches the runtime-tested/independently statically reviewed candidate.

Validation: renderer 301 files / 3376 passed; related Python 48 passed; Desktop typecheck and lifecycle ESLint pass. Contract generator --check passes. scripts/check --staged --base 7bda2a3368024f7ae1c70cef16583e7c2c891674 completes in 23.33 seconds with 12 checks OK, 0 blocking/0 advisory. An earlier default-base scan timed out while examining thousands of inherited upstream files; its failed record remains preserved. No checks were disabled; the successful rerun uses the explicit frozen upstream to select the actual PR delta.

No new cleanup plugin/migration features or production changes. Full suite/native E2E remain unverified. This is conflict repair and review-readiness maintenance, not green remote CI or maintainer approval.

@ljhade24
ljhade24 force-pushed the fix/desktop-session-deletion-sync branch from 3dd9ac7 to 6ac7021 Compare October 9, 2026 06:18
@alt-glitch alt-glitch added type/bug Something isn't working P3 Low — cosmetic, nice to have comp/desktop Electron desktop app (apps/desktop/*) comp/tui Terminal UI (ui-tui/ + tui_gateway/) area/sessions Session lifecycle, resume, persistence, history sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state labels Oct 9, 2026
@ljhade24
ljhade24 marked this pull request as ready for review October 9, 2026 08:56
@ljhade24

ljhade24 commented Oct 9, 2026

Copy link
Copy Markdown
Author

Verification follow-up on 9097781471a47ae71513c9bd0e445249aeee02ed: fixed independent-review findings around profile-blind stale-page fencing, foreign live writers, dedicated-list lineage aliases, and stale project-tree previews. The separate serial baseline run reproduced the same 12 Python failure IDs (head 141 passed / base 135 passed); the complete renderer store + session-hook scope passed 3,240 tests on head and 3,228 on base. Typecheck, changed-file lint, all 11 scripts/check guards, source build, and fresh-context automated review pass. The body now records the exact bounds; neither whole-suite success nor native packaged deletion E2E is claimed. Marked ready for maintainer review, not merge readiness. Upstream CI/Docker/Nix are still action_required with no reported checks—could a maintainer approve/inspect those workflow runs? No merge requested or performed.

Preserve deletion dispatch and upstream onGatewayReady together. Regenerate OpenRPC from Python contracts, retaining SessionsDeletedPayload and upstream SetupBeat. Independent static review passed; renderer 3291, scoped Python 153, typecheck, lint, contract freshness and 12 checks pass. Python focused rerun overlaps broader scope; no full-suite/native E2E claim.
@ljhade24

Copy link
Copy Markdown
Author

Conflict refresh pushed as 0d2d45a, merging frozen upstream e06df9c2eae6901f4c88f464e0ae6e753c0428b8.

Retained both upstream onGatewayReady/onboarding handling and deletion dispatch/profile fences. Regenerated OpenRPC from Python contracts, preserving SessionsDeletedPayload and upstream SetupBeat; generated TypeScript freshness checked. Renderer scope: 284 files / 3291 passed. Broader prior Python path scope: 153 passed, no failures; focused 116-pass rerun overlaps, not additive. Typecheck, relevant eight-file ESLint, scripts/check 12 checks, regeneration/freshness passed. Earlier 12 Python failures did not reproduce in this isolated scoped run; root-cause attribution or whole-suite success is not claimed. Native packaged deletion E2E remains unverified; existing non-Draft review status retained.

Independent static review found no security/logic blockers, and the committed tree equals the tested/reviewed candidate. Separate merge-tree check against latest main d97007c32d6340f7ba79ac1dfb5773cc00846448 returned exit 0; exact-head GitHub readback reports mergeable=true (blocked, not dirty). Content conflicts are cleared, not maintainer approval or green remote CI. Existing Draft/readiness states are unchanged. No merge, production change or restart.

Retain deletion dispatch with upstream owner-aware replay-gap recovery. Tested/reviewed tree 26b17c1; renderer 3376, related Python 48, typecheck/lint/contract freshness and all 12 upstream-relative checks pass. No full-suite/native E2E claim.
@ljhade24

Copy link
Copy Markdown
Author

Recurrent content conflict resolved and pushed as 6f61fff, merging upstream 7bda2a3368024f7ae1c70cef16583e7c2c891674. Both deletion dispatch and upstream owner-aware replay-gap recovery are preserved.

Exact published tree matches the tested/independently statically reviewed candidate. Renderer 3376 passed, related Python 48 passed, typecheck/lifecycle lint and generated-contract freshness pass. All 12 scripts/check checks pass with explicit frozen upstream base; the earlier broad-default-base timeout is retained as historical failure, not relabeled success. No checks skipped.

No new plugin features, production changes, merge or native/full-suite success claim. Existing review-readiness state unchanged; remote CI/maintainer gates still apply.

Merge frozen upstream 73b9f2a. Retain settled-mtime cache semantics and ID deletion broadcasts, with a same-tick exactly-once watcher regression. Tested and independently reviewed tree 9ed0179. Renderer 3376 and related Python 43 pass; typecheck, contracts freshness and 12 blocking checks pass. No full-suite or native E2E claim.
@ljhade24

ljhade24 commented Oct 15, 2026 •

Copy link
Copy Markdown
Author

New upstream conflict resolved and pushed as d928339, merging frozen main 73b9f2af7b87a619f42396710c332be04353aef2.

The watcher keeps upstream's _settled_mtime cache sentinel and per-file project/pairing signatures alongside the existing committed-ID deletion broadcast. Added a real SQLite regression for a committed deletion inside the same mtime tick: one deletion event across successive watcher passes. This is watcher emission behavior, not an exactly-once transport-delivery guarantee.

Published tree 9ed017909de971d34b34bf7bf257c6821cb0bf6e equals the tested and independently statically reviewed candidate. Canonical Python runner: 43 passed across 4 files, including watcher, same-tick metadata recovery, profile skin scope and generated-contract coverage. Renderer stores/session hooks: 301 files / 3376 passed. Desktop typecheck, generated-contract freshness and all 12 blocking scripts/check checks against frozen main pass. No full-suite/native packaged E2E claim.

Existing non-Draft status retained; maintainer review and workflow approval remain separate gates. No new plugin features, production changes or upstream merge.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/sessions Session lifecycle, resume, persistence, history comp/desktop Electron desktop app (apps/desktop/*) comp/tui Terminal UI (ui-tui/ + tui_gateway/) P3 Low — cosmetic, nice to have sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants