SIGN IN SIGN UP

Let a conversation be deleted, and its thread with it (#197)

* feat: let a conversation be deleted, and its Intelligence thread with it

Nothing removed a channel, so the roster only ever grew (#196). Adds
DELETE /api/channels/:channelId: the channel row, its memberships,
agent links, and thread mapping go through the FK cascades that
already existed for them, and the deployment asks Intelligence to
permanently delete the thread itself.

The local delete commits first. A rejected or unreachable upstream
thread delete is non-fatal and leaves the channel gone from the
roster either way, with an audit row (channel.deleted) naming the
thread and whether Intelligence actually forgot it - a channel gone
locally with an orphaned thread is a smaller, more honest failure
than one still sitting in the roster with its history silently
wiped.

Removal fans out over the existing channel_activity NOTIFY topic so
other open tabs see it live. The sidebar row gets an options menu
with a two-button confirm dialog (new alert-dialog.tsx, wrapping
@base-ui/react/alert-dialog).

* Keep the roster memoized, attribute the row, and stop a failed delete vanishing

The confirm button was AlertDialogAction, which renders the primitive's
Close: it shut the dialog the instant it was pressed, before the request
it started was answered. A delete that failed then reported nothing at
all, leaving the conversation in the roster with no explanation, and
"Deleting…" could never appear. A plain button waits for the answer,
closes on success, and shows the server's message otherwise.

DELETE answers 200 with historyLeftBehind rather than a bare 204. The
thread deletion is the half that can fail on its own, and 204 said the
whole act succeeded whichever way it went, so a screen had no way to
avoid claiming a message history was gone while it was still on the
platform.

The roster row lost its memo for a stated reason that does not hold:
memo compares props and says nothing about a hook, and use-channel-events
preserves row identity precisely so rows do not re-render.

The audit row dropped the actor in single-user mode, believing
audit_events.actor_user_id has a foreign key into users. It has none, and
initializeDevActorUser writes that row at start-up anyway. Single-user is
the mode .env.example ships switched on, so that was the default row, and
it recorded that a conversation was deleted but not by whom.

Also move a tab that is looking at the channel another tab just deleted,
which was left on a route that no longer resolves, and cover the audit
row, which nothing tested.

---------

Co-authored-by: David McKay <davidmckayv@users.noreply.github.com>
C
Cristian Peralta Segura committed
983c9092c874f15f0d62dfe9cd24162e32bcdec4
Parent: 574b4cd
Committed by GitHub <noreply@github.com> on 8/24/2026, 5:04:12 PM