[Bug] Undeletable "(no messages)" rows: the saved-session catalog skips the draft lifecycle filter #1921
Replies: 3 comments
|
Second pass: I replayed the real view pipeline over a live catalog instead of trusting unit tests, and it caught a defect in my own patch. MethodSnapshot of a running 0.8.1 daemon: 39 session summaries and 15 saved files, with one ghost created on purpose ( What it caughtThe first shape of the guard ( Both extra rows are real sessions carrying an CorrectionThe guard now excludes drafts only: export function shouldShowAgentsViewSavedSession(saved: AgentConnectionSavedSessionInfo): boolean {
return inactiveLifecycleForSession(saved) !== "draft";
}Re-run over the same live snapshot: exactly one row disappears, the ghost. No row is added, and the order of the remaining 18 rows is identical. Tests now pin archived and crash rows as visible so this cannot regress; 68 pass in Branch updated: The wider point for whoever picks this up: today the on-disk |
|
#1920 landed while this was open (created 14:34Z, three minutes before this report, so independent work rather than a reply). It changes the severity here, and it is worth being precise about which half it covers. What it fixes for this bug, reading the merged diff rather than running it: an empty, unnamed, idle session's worker is now passivated when its last client disconnects, so the daemon stops owning the file. What it does not change: the row still exists. Branch rebased onto 9f5edc1: Two smaller notes that survive #1920: the draft file itself still stays on disk after eviction (passivation keeps it), and |
|
Rebased after the agents-view roster/direct-transport stack landed, and replayed the saved-only contract on its merged base ( The row is still present. On a pristine Branch updated to The roster-stack rebase had one conflict only: keep the new Validation:
No maintainer action is needed to resolve rebase drift; the branch is current and remains available for cherry-pick. |
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Summary
The agents view lists rows titled
(no messages)that cannot be removed. They are not stale files and not orphaned leases: the daemon is holding a real, healthy, message-less session, while the saved-session catalog draws a second row for the same file that no lifecycle filter ever sees.activeLifecycleForSessionclassifies a message-less resident session asdraft, andAgentsViewMode.reconcileCatalogsdrops it withshouldShowAgentsViewSession. The same method then passesthis.savedSessionsintoreconcileUnifiedSessionsunfiltered, and a saved-only record is never classified, so the file returns as aninactiverow with noactiveSessionId. Ctrl+X cannot resolve it, because the daemon still owns the file, and the draft has no visible row to stop.Steps to reproduce
Three daemon commands from a clean state, no TUI needed (0.8.1, isolated session dir):
Observed:
The file is 696 bytes:
session,model_change,thinking_level_change,service_tier_change,session_state. No messages.Through the UI: start a session, never send a message, leave it. The row appears under Inactive as
(no messages), and Ctrl+X twice answersSession became active; stop it before deletingforever. On my machine three of them accumulated, one of them 12 hours old, each still holding aworkerState: "ready"worker with a liveworkerPidandattachedClients: 0.Expected behavior
Either the row does not exist, which is what the daemon already decides for the resident half of the same session, or the row is deletable. Today it is neither.
Prime Agent version
0.8.1 (npm). Code paths re-read on
mainat c718bf3; the mechanism is unchanged there.Environment
macOS 15 (arm64), background daemon plus agents view.
Why the file is there at all
hasUserContent()countsmodel_change,thinking_level_change, andservice_tier_changeas user intent throughCONTENT_ENTRY_TYPES, and every session writes those three at startup. SoisEmptyDraftContentis false for every abandoned session, the detach-time discard never fires, the file stays, and the worker stays resident with it. One ghost per abandoned session.The row is not the only symptom
prime-agent --continuepicks a session withfindMostRecentSessionForCwd, which sorts*.jsonlby mtime and filters on the header cwd only. An abandoned draft is the newest file in that directory, so it wins. Checked against the real function with two fixture files, a conversation written at 10:00 and a draft at 11:00:So the ghost is not only an undeletable row: it also captures
-cuntil the user opens something else in that directory. The view patch below does not change this, and it should not - the honest fix is either to stop persisting an abandoned draft or to make-cskip message-less files. The second one is not free:findMostRecentSessionForCwddeliberately reads one header line per file and would need message counts, so it is a decision for whoever owns that path.Patch
Branch: artgas1/prime-agent@main...artgas1:prime-agent:fix/agents-view-hidden-draft-rows (
bf2085d). PR #1919 was auto-closed by the contribution gate, as CONTRIBUTING describes; the branch remains available for review or cherry-pick. The same change as a mailbox patch: append.patchto that compare URL. Checked by applying it withgit amto a fresh worktree of c718bf3, whereagents-view-state,agents-view-mode, and the #502 regression file pass (105 tests), and biome and tsgo are clean.Catalog rows without a daemon summary get the same live-only rule as daemon rows, reusing the existing classifier so on-disk and resident answers cannot drift apart:
applied to the saved-only branch of
reconcileUnifiedSessions; daemon records are still enriched by their saved twin. This is the same direction as #1852: one answer to "does this session have messages", not two.Validation, since CI does not run for unvouched authors:
test/agents-view-state.test.ts68 passed, two new tests, both fail without the guard (expected [ Array(4) ] to deeply equal [ 'live' ]). Also green:agents-view-mode,agents-view-inactive-reply,agents-view-missing-cwd,session-view-search,daemon-session-list,session-manager/session-state,test/suite/regressions/502-unified-session-view.test.ts.npm run checkclean.Blast radius
The saved catalog has one consumer in the TUI,
listDaemonSavedSessionsat agents-view-mode.ts:2202, so the guard changes one surface. Explicit resume is unaffected:--resume <id|path>goes throughresolveSessionPath, which reads the directory itself, so a hidden draft can still be opened on purpose. And the project already treats a message-less session as unresumable one layer up, informatResumeHint: "Omit ephemeral and unflushed empty sessions because neither can be resumed."What the patch does not do, and one open choice
delete_saved_sessiondiscard an idle, message-less draft instead of refusing. I did not take it becauseactiveLifecycleForSessionstates that a conversation-less session must not surface a row, so the visible row looks like the defect. If you prefer the delete-side behavior, say so and I will prepare that shape instead.archivedandcrashfiles, which replaying the view over a live catalog proved wrong: see the comment below. Hiding archived rows belongs to Complete queued, interrupted, and archived session lifecycle recovery #1382, not to a view filter.Related
messageCount; this is the same idea one layer over.All reactions