Skip to content

feat(chat): session-scoped sidebar rename + archive-on-delete#1763

Merged
sweetmantech merged 1 commit into
testfrom
feat/sidebar-delete-rename-session-scoped
Jun 1, 2026
Merged

feat(chat): session-scoped sidebar rename + archive-on-delete#1763
sweetmantech merged 1 commit into
testfrom
feat/sidebar-delete-rename-session-scoped

Conversation

@sweetmantech
Copy link
Copy Markdown
Collaborator

@sweetmantech sweetmantech commented Jun 1, 2026

Replaces chat#1758, which was auto-closed when its base branch (feat/sidebar-canonical-urls) was deleted alongside the chat#1756 merge. Same code, retargeted at test.


Two related changes to the sidebar's chat actions, now that chat#1756 has shipped session-scoped chat listings:

Rename — session-scoped PATCH

The rename flow was still hitting the legacy PATCH /api/chats shape ({ chatId, topic }), which only touched the rooms table. Repoint at PATCH /api/sessions/{sid}/chats/{cid} with { title } so the canonical chat row is updated.

  • updateChat({ sessionId, chatId, title })PATCH /api/sessions/{sid}/chats/{cid}.
  • useRenameModal sends title (was topic).

Delete — archive the owning session

Instead of removing the chat row, the "Delete chat" action now archives the owning session via PATCH /api/sessions/{sid} { status: "archived" }. The api filters chats from archived sessions out of GET /api/chats (api#630), so the row disappears from the sidebar; archive also triggers stopSandboxOnArchive so we tear down running sandboxes for chats the user said they're done with. Reversible from the admin side.

This also sidesteps the 400 Cannot delete the only chat in a session guard that blocks chat-row deletes when a session has a single chat — archive has no such constraint.

  • New lib/sessions/archiveSession.tsPATCH /api/sessions/{sid} { status: "archived" }.
  • useDeleteChat calls it (drops the chatId arg).
  • DeleteConfirmationModal no longer passes chatId.

Caveat

If a session ever has >1 chat, archiving from one row hides the others too. Today every sidebar row maps 1:1 to its own session (POST /api/sessions mints a fresh session per chat), so this is a theoretical concern. Will need to revisit if/when sessions ever hold multiple chats.


Summary by cubic

Session-scope the sidebar: renaming now updates the canonical chat row, and “Delete chat” archives the owning session so chats disappear and sandboxes stop.

  • New Features

    • Rename chat via PATCH /api/sessions/{sid}/chats/{cid} with { title } (threads sessionId; switches from topictitle).
    • Delete chat now archives the session via PATCH /api/sessions/{sid} with { status: "archived" }; avoids the “Cannot delete the only chat in a session” 400, is reversible, and triggers sandbox teardown.
    • Delete modal supports bulk delete with progress and calls the archive path; useDeleteChat now takes { sessionId }.
    • Removed legacy lib/chats/deleteChat.ts; added lib/sessions/archiveSession.ts.
  • Migration

    • No action needed. Archiving hides all chats in a session and is reversible by admins; the API filters archived sessions from GET /api/chats.

Written for commit 8187b1a. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

  • Refactor
    • Chat deletion now archives conversations instead of permanently removing them, preserving the ability to restore if needed.
    • Chat renaming updated with improved session-scoped data handling.
    • Streamlined delete confirmation workflow and session management logic.

@vercel
Copy link
Copy Markdown
Contributor

vercel Bot commented Jun 1, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
chat Ready Ready Preview Jun 1, 2026 4:24pm

Request Review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jun 1, 2026

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: e871acf7-eb32-4560-bdbd-2a6dc5800cca

📥 Commits

Reviewing files that changed from the base of the PR and between 44550b5 and 8187b1a.

📒 Files selected for processing (6)
  • components/Sidebar/Modals/DeleteConfirmationModal.tsx
  • hooks/useDeleteChat.ts
  • hooks/useRenameModal.ts
  • lib/chats/deleteChat.ts
  • lib/chats/updateChat.ts
  • lib/sessions/archiveSession.ts
💤 Files with no reviewable changes (1)
  • lib/chats/deleteChat.ts

📝 Walkthrough

Walkthrough

This PR refactors chat deletion from direct row deletion to session archiving, migrates chat updates to session-scoped endpoints, and threads session IDs through modal workflows. The old /api/chats deletion endpoint is removed and replaced with a session archiving flow that soft-deletes and filters archived sessions from chat listings.

Changes

Session-scoped chat operations

Layer / File(s) Summary
Archive session infrastructure and delete hook
lib/sessions/archiveSession.ts, hooks/useDeleteChat.ts
New archiveSession helper PATCHes a session to status "archived" with error parsing fallback. useDeleteChat hook refactored to accept { sessionId } and call archiveSession instead of the prior deleteChat helper, with updated documentation describing reversible archival and GET endpoint filtering.
Delete confirmation modal UI and flow
components/Sidebar/Modals/DeleteConfirmationModal.tsx
Modal props refactored to separate onDelete, chatsToDelete logic clarified for single/bulk modes, deletion loop updated to pass { sessionId: chat.sessionId } to the hook, and error aggregation/UI strings adjusted with updated formatting.
Chat update endpoint migration to session scope
lib/chats/updateChat.ts
UpdateChatParams adds sessionId and replaces topic with title; UpdateChatResponse now includes session/chat model fields (sessionId, title, modelId, activeStreamId, timestamps). Request URL rewired to /api/sessions/{sessionId}/chats/{chatId}, body changed to send { title }, and error handling now parses JSON with fallback.
Rename modal integration with updated chat endpoint
hooks/useRenameModal.ts
updateChat call arguments updated to use sessionId: chatRoom.sessionId and title: name, removing the intermediate chatId variable and replacing the prior topic-based payload.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • recoupable/chat#1612: Prior refactor of the deletion stack that this PR builds upon and further evolves.
  • recoupable/chat#1752: Parallel session-scoped chat refactor updating chat UI/loading and endpoints to /api/sessions/{sessionId}/chats/{chatId}.
  • recoupable/chat#1737: Earlier changes to DeleteConfirmationModal props and deletion invocation patterns.

Suggested reviewers

  • cubic-dev-ai

Poem

🗂️ Sessions now soft-delete with grace,
Archived and filtered from their place,
Chats scoped to home, not floating free,
Rename and update in harmony,
Clean architecture, neat and tight—refactor done just right! ✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Solid & Clean Code ⚠️ Warning DRY violation: archiveSession and updateChat duplicate fetch/error patterns; naming inconsistency in error variables; hardcoded "Chat" repeated in component. Extract shared API fetch utility; standardize error handling; create getChatName() helper; fix modal closure on partial deletion failures.
✅ Passed checks (2 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/sidebar-delete-rename-session-scoped

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@chatgpt-codex-connector
Copy link
Copy Markdown

💡 Codex Review

push(`/sessions/${conversation.sessionId}/chats/${conversation.id}`);

P2 Badge Update active-chat detection for session routes

When a user clicks a sidebar row, this now navigates to /sessions/{sessionId}/chats/{id}, but components/Sidebar/RecentChats/useRecentChats.ts still derives activeChatId only from /chat/{id} or params.roomId. On any canonical session URL, activeChatId becomes null, so RecentChatList no longer marks the current row active and ChatItem loses its active styling/options state. Please update the active-id parsing to read params.chatId or the /sessions/.../chats/... pathname as part of this route change.

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Repoints sidebar rename and delete at the session-scoped api so they
operate on the same `chats` / `sessions` rows the workflow surface
persists into — the previous handlers hit the legacy `rooms`-shaped
endpoints and silently no-op'd on workflow chats.

- Rename: `PATCH /api/chats { chatId, topic }` → `PATCH /api/sessions/{sid}/chats/{cid} { title }`. `useRenameModal` and `updateChat` thread `sessionId` from the `Conversation` row and switch the wire field to `title`.
- Delete: switch from row-delete to session archive (`PATCH /api/sessions/{sid} { status: "archived" }`). The api filters archived sessions out of `GET /api/chats` (recoupable/api#630), so the chat disappears from the sidebar; archive also runs the existing `stopSandboxOnArchive` lifecycle, and the whole thing stays reversible from the admin side.
- Sidesteps the previous "Cannot delete the only chat in a session" 400, which was a real footgun once every workflow chat ended up in a 1-chat session.
- Removes the now-unused `lib/chats/deleteChat.ts`; adds `lib/sessions/archiveSession.ts` for the new wire call.

If a session ever has >1 chat, archiving from one row will hide the
others too. Every sidebar row maps 1:1 to its own session today
(each `POST /api/sessions` mints a session + single chat), so this is
a no-op in practice — worth knowing before any future flow creates
additional chats inside an existing session.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 14 files

Confidence score: 4/5

  • This PR looks safe to merge with minimal risk: the reported issue is low-to-moderate severity (4/10) and appears limited to a specific deletion flow rather than broad app behavior.
  • In components/Sidebar/Modals/DeleteConfirmationModal.tsx, deleting chats that share a session can trigger repeated archive attempts on the same session, which may surface misleading "Failed to delete" errors for some rows.
  • User impact is mainly confusing feedback during multi-chat deletion, not clear data corruption or a systemic regression from the summary provided.
  • Pay close attention to components/Sidebar/Modals/DeleteConfirmationModal.tsx - repeated session archiving in the loop can cause false failure messages.
Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="components/Sidebar/Modals/DeleteConfirmationModal.tsx">

<violation number="1" location="components/Sidebar/Modals/DeleteConfirmationModal.tsx:68">
P2: When multiple chats share a session, the loop archives the same session repeatedly. Subsequent calls for an already-archived session likely fail, producing confusing "Failed to delete" errors for rows that were effectively archived. Consider deduplicating by `sessionId` before iterating.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic


try {
await deleteChat(chat.id);
await deleteChat({ sessionId: chat.sessionId });
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: When multiple chats share a session, the loop archives the same session repeatedly. Subsequent calls for an already-archived session likely fail, producing confusing "Failed to delete" errors for rows that were effectively archived. Consider deduplicating by sessionId before iterating.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At components/Sidebar/Modals/DeleteConfirmationModal.tsx, line 68:

<comment>When multiple chats share a session, the loop archives the same session repeatedly. Subsequent calls for an already-archived session likely fail, producing confusing "Failed to delete" errors for rows that were effectively archived. Consider deduplicating by `sessionId` before iterating.</comment>

<file context>
@@ -52,15 +65,18 @@ const DeleteConfirmationModal = ({ isOpen, onClose, chatRoom, chatRooms, onDelet
 
         try {
-          await deleteChat(chat.id);
+          await deleteChat({ sessionId: chat.sessionId });
         } catch (chatError) {
-          console.error(`Error deleting chat ${chat.topic || "Chat"}:`, chatError);
</file context>

Comment thread lib/sessions/archiveSession.ts
@sweetmantech
Copy link
Copy Markdown
Collaborator Author

Preview verification — live test on chat-git-feat-sidebar-delete-rename-session-scoped-recoup.vercel.app

Tested both flows end-to-end with Privy Bearer auth. Two passes: one before api#630 shipped (delete archive succeeds but row stays), one after (full UX works).

Rename flow

Step Result
Click "Chat options" → "Rename" on existing chat Modal opens with current title pre-filled ✅
Edit title, click "Rename" PATCH /api/sessions/{sid}/chats/{cid} body {"title":"..."} → 200 ✅
Sidebar updates Row shows new title, bubbles to top by updatedAt

Wire shape: session-scoped PATCH path, body uses title (not legacy topic).

Delete flow (now fully working with api#630 merged)

Step Result
Click "Chat options" → "Delete" Confirmation modal opens with chat title quoted ✅
Click "Delete" PATCH /api/sessions/{sid} body {"status":"archived"} → 200, response shows status: "archived", lifecycleState: "archived"
Sandbox teardown lifecycleState: "archived" in response confirms stopSandboxOnArchive fired ✅
Auto-refetch GET /api/chats?artist_account_id=... re-fires immediately after the PATCH ✅
Row removal api#630 filters the archived session out; row vanishes from sidebar in ~1s (14 → 13 chats) ✅

Reversibility check

Tested via curl: PATCH /api/sessions/{sid} {"status":"running"} → next GET /api/chats returns the chat again. So "delete" is a soft-delete from the user's perspective but recoverable from the admin side, matching the PR body's design.

Recommendation

All four substantive claims of #1763 verified end-to-end. Recommend merge.

@sweetmantech sweetmantech merged commit 3b93c19 into test Jun 1, 2026
3 checks passed
@sweetmantech sweetmantech deleted the feat/sidebar-delete-rename-session-scoped branch June 1, 2026 17:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants