Refactor chat thread lifecycle and listing into dedicated Convex modules - #159
Conversation
- Move thread creation, repository transfer, single-turn reset, archive, and delete logic from `convex/chat/threads.ts` into new `convex/chat/threadLifecycle.ts` - Update `startChatTurnInExistingThread` to use `resetSingleTurnThreadForNextTurn` before new turns in single-turn repoless threads - Route agent/thread profile resolution and context imports to the new lifecycle module to reduce cross-file duplication
- Centralize thread listing into a shared scope-aware helper with pinned-first ordering and regression tests - Introduce adapter-driven repository deletion lifecycle with ordered groups and retry policies - Add explicit sandbox list pagination and propagate hardened cleanup result handling - Move chat composer grounding state to reducer-based session state for stable updates
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Warning Review limit reached
More reviews will be available in 37 minutes. Learn how PR review limits work. Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file). ⌛ How to resolve this issue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based credits. 🚦 How do rate limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please see our Fair Usage Limits Policy for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (5)
📝 WalkthroughWalkthroughThis PR extracts ~600 lines of inline thread mutation logic from ChangesBackend: Thread Lifecycle and Repository Deletion Refactors
Frontend: Composer Session Reducer and Workspace State Extraction
Sequence Diagram(s)sequenceDiagram
participant RepositoryShell
participant useRepositoryWorkspaceState
participant useChatComposerSession
participant reduceComposerSession
participant threadLifecycle
RepositoryShell->>useRepositoryWorkspaceState: urlRepositoryId, urlThreadId
useRepositoryWorkspaceState->>useChatComposerSession: thread, repository, capabilities
useChatComposerSession->>reduceComposerSession: dispatch sync(composerSessionInputs)
reduceComposerSession-->>useChatComposerSession: ComposerSessionState
useChatComposerSession-->>useRepositoryWorkspaceState: composerSession snapshot
useRepositoryWorkspaceState-->>RepositoryShell: RepositoryWorkspaceState
RepositoryShell->>useRepositoryWorkspaceState: handlers.archiveThread(thread)
useRepositoryWorkspaceState->>threadLifecycle: archiveThreadLifecycle(ctx, { thread })
threadLifecycle-->>useRepositoryWorkspaceState: void
useRepositoryWorkspaceState-->>RepositoryShell: dialogs.archiveThread updated
Estimated code review effort🎯 5 (Critical) | ⏱️ ~120 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 5
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@convex/chat/threadLifecycle.ts`:
- Around line 439-446: Avoid removing archive-scope membership twice in
deleteArchivedThreadLifecycle and deleteThreadLifecycle. The archived thread is
already removed from archive scope before the permanent delete, so update
deleteThreadLifecycle to skip recordThreadRemovedFromArchiveScope when the
thread is being permanently deleted from an archived state, or centralize the
removal so it happens only once. Use the deleteArchivedThreadLifecycle and
deleteThreadLifecycle flow to ensure the archive decrement is performed a single
time for archived-thread deletes.
- Around line 290-302: The single-turn reset flow in threadLifecycle should also
clear lastAssistantMessageAt whenever the reset completes, not only in the
synchronous path. Update the reset handling around enablingSingleTurn and the
pending-reset continuation so that once drainThreadMessageArtifacts finishes
removing all remaining messages/streams, the thread patch removes or nulls
lastAssistantMessageAt alongside singleTurnResetPending. Use the existing reset
logic in threadLifecycle as the entry point and ensure both the immediate
completion path and the deferred completion path apply the same metadata
cleanup.
In `@src/components/chat-shell-shared/use-chat-composer-session.ts`:
- Around line 106-118: The composer session currently renders with stale reducer
state before the sync effect corrects it, which can drive route and send payload
from the previous thread/repo/access context. Update the logic in
use-chat-composer-session, especially around useReducer,
getComposerSessionSnapshot, and the sync effect, so the first render after
threadId/repositoryId/access input changes derives the new composer session
state immediately instead of waiting for the effect to dispatch a fix-up. Ensure
the grounding selection and any derived route/payload values always reflect the
latest baseComposerSessionInputs on that same render.
In `@src/components/chat-shell-shared/use-repository-workspace-state.ts`:
- Around line 159-160: The artifact panel can be enabled via an attached
repository, but `handleSelectArtifact` and related artifact navigation still
rely only on `currentRepositoryId`, causing clicks to fail for repoless or moved
threads. Update the artifact-selection flow in `useRepositoryWorkspaceState` to
derive an effective repository id from `capabilities.attachedRepository?.id ??
currentRepositoryId`, and use that wherever artifact navigation or downstream
panel state is computed so the panel never receives a null repo id when an
attached repository is available.
- Around line 274-291: The repoless thread selection handler in
handleSelectThread drops the selected thread when currentRepositoryId is null by
navigating to DEFAULT_AUTHENTICATED_PATH instead of a thread-specific route.
Update the null-repository branch to route through the repoless thread/new-chat
path so AppSidebarLeft actions remain routable, and keep the selected
threadId/new-thread flow intact for both thread selection and thread creation
paths.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: edec2837-b17d-45a2-9721-41c48da5caec
📒 Files selected for processing (15)
convex/chat/chatTurnIntake.tsconvex/chat/context.tsconvex/chat/threadLifecycle.tsconvex/chat/threadListing.tsconvex/chat/threads.test.tsconvex/chat/threads.tsconvex/daytona.tsconvex/lib/repositoryOwnedDataAdapters.tsconvex/lib/repositoryOwnedDataLifecycle.tsconvex/repositories-delete.test.tssrc/components/chat-shell-shared/use-chat-composer-session.tssrc/components/chat-shell-shared/use-repository-workspace-state.tssrc/components/repository-shell.tsxsrc/lib/chat-composer-session.test.tssrc/lib/chat-composer-session.ts
- Clear assistant timestamp on single-turn re-enables and avoid duplicate archive-scope deletion - Use async-iterator-based Daytona sandbox pagination for cleaner listing - Resolve artifact repository and thread navigation for repo-less flows, and stabilize composer session snapshots
Summary
convex/chat/threads.tsinto a newconvex/chat/threadLifecycle.tsmodule (create/move/delete/archive/recover/reset, share updates, stream/message cleanup orchestration, and bounded continuation scheduling).convex/chat/threadListing.tsmodule to centralize active-thread list ordering (pinned-first, then recent, deduplicated) for both repository-scoped and repoless scopes.convex/chat/chatTurnIntake.tsto use shared lifecycle reset logic for single-turn thread prep before new turns, reducing inline cleanup orchestration.convex/chat/context.tsand related call sites to consume new lifecycle exports (including repoless agent detection).threads.tswhile delegating heavy orchestration to the new modules and preserving existing mutation/query behavior.convex/chat/threads.test.ts,convex/lib/chat-composer-session.test.ts, andconvex/repositories-delete.test.ts.Testing
bun run format.bun run lint.bun run typecheck.bun run test.Summary by CodeRabbit
New Features
Bug Fixes