fix(sessions): report bulk-delete rows skipped for a live turn

delete_sessions(exclude_active_write_guards=True) dropped guarded rows
silently: the web bulk-delete endpoint returned only a count and the
dashboard removed every selected row optimistically, so refused rows
reappeared on the next reload with no explanation.

The store now appends refused ids to an optional skipped_ids list inside
the same write transaction, the endpoint returns them as skipped_active,
and SessionsPage keeps those rows listed. Also hoists the
SessionActiveWriteGuardError imports to module top (hermes_state_errors
is stdlib-only) and drops the assertion-less lineage comment in the test.
This commit is contained in:
kshitijk4poor
2026-09-27 15:18:51 +05:30
committed by kshitij
parent 40523600b0
commit 907e3188da
8 changed files with 21 additions and 14 deletions

View File

@@ -502,7 +502,7 @@ export const api = {
},
),
bulkDeleteSessions: (ids: string[], profile = getManagementProfile()) =>
fetchJSON<{ ok: boolean; deleted: number }>("/api/sessions/bulk-delete", {
fetchJSON<{ ok: boolean; deleted: number; skipped_active?: string[] }>("/api/sessions/bulk-delete", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ ids, profile: profile || undefined }),

View File

@@ -1408,7 +1408,9 @@ export default function SessionsPage() {
// than waiting for the reload. The reload still runs so total /
// pagination stays correct, and so any rows the reload pulls in
// from later pages render in place.
const deletedSet = new Set(ids);
// Rows a live turn still owns were refused server-side; keep them listed.
const skipped = new Set(resp.skipped_active ?? []);
const deletedSet = new Set(ids.filter((id) => !skipped.has(id)));
setSessions((prev) => prev.filter((s) => !deletedSet.has(s.id)));
setTotal((prev) => Math.max(0, prev - resp.deleted));
if (expandedId && deletedSet.has(expandedId)) setExpandedId(null);