feat(chat): new chats start in the most recent chat's project#3712
Merged
Conversation
A brand-new chat (chat composer or Home) now defaults to the project the most recent visible chat ran in, instead of snapping to the alphabetically first project. Explicit picks, a linked task's project, and an opener surface's root all still outrank the recency default; existing sessions in unregistered cwds stay No project (regression-pinned). - chat-projects: recentChatProjectRoot(sessions, projects) walks visible chats newest-first and returns the first registered project root; resolveChatProjectSelection takes recentProjectRoot in its brand-new-chat fallback, before firstProject. - chat-view: resolves the recency default LIVE at render (sessions load async after boot), keeps a null project draft for brand-new chats so a late recency signal still applies and background refreshes can never clobber an explicit pick; only switching compose targets clears a pick, and only while sessionId is still null (a just-minted session keeps the pick its first send used). Empty-state picker displays the resolved id. - home-composer: resolveHomeComposerProject takes the recent root before projects[0]; the composer no longer seeds state to the first project (clear-only effect for stale picks) and pickers display the resolved live default the send would use. - tests: updated pinned source shapes, added resolver/helper unit coverage. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Contributor
There was a problem hiding this comment.
Pull request overview
This PR updates chat and Home composer project-defaulting so brand-new chats inherit the registered project of the most recent visible chat (when the user hasn’t explicitly picked a project), instead of defaulting to the alphabetically-first project—while preserving existing precedence rules for explicit picks, linked tasks, and existing sessions in unregistered directories.
Changes:
- Added a recency-based default (
recentChatProjectRoot) and threaded it intoresolveChatProjectSelectionfor brand-new chats. - Updated ChatView and HomeComposer to keep “unset” project drafts null so the default resolves live as sessions load, and to display the resolved default in pickers.
- Expanded/added unit + source-shape tests covering recency inheritance and the “don’t freeze default” regression.
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| src/lib/home-composer-context.ts | Adds recency-root fallback to home composer project resolution. |
| src/lib/home-composer-context.test.ts | Adds tests for recency-based defaulting behavior. |
| src/lib/chat-projects.ts | Adds recentChatProjectRoot and integrates recency-root into selection resolution. |
| src/lib/chat-projects.test.ts | Adds coverage for recency precedence and recentChatProjectRoot behavior. |
| src/components/task-chat-cwd.test.ts | Updates pinned-source assertions for new recentProjectRoot plumbing and null-draft behavior. |
| src/components/project-picker.test.ts | Updates pinned-source expectation to use resolved display project id. |
| src/components/home-composer.tsx | Computes recent project root, uses live-resolved selection for display, and stops seeding projects[0] into state. |
| src/components/home-composer.test.ts | Updates pinned-source assertions + regression check against seeding first project. |
| src/components/chat-view.tsx | Computes recent project root; keeps null draft for new chats; uses resolved project id for composer/empty-state. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+5187
to
5191
| taskCwd: linkedContext?.task?.cwd, | ||
| projects, | ||
| }).projectId; | ||
| // Initialise when unset, or always resync on session switch. | ||
| return prev === null ? resolved : resolved ?? prev; |
Comment on lines
+31
to
+34
| return ( | ||
| projects.find((project) => project.id === selectedProjectId) ?? | ||
| projectForRoot(recentProjectRoot, projects.slice()) ?? | ||
| projects[0] ?? |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
New chats (chat composer and Home) now default to the project the most recent chat ran in, instead of the alphabetically-first project. Zero-config recency inheritance — requested as "set a default project OR let new chats start with the most recent chat's project".
Precedence (unchanged where it matters)
user pick → linked task's project → opener/session root → most recent chat's project (new) → first project
projectsis familiar-grant-scoped, so a non-granted project can never be inherited.How
chat-projects.ts: newrecentChatProjectRoot(sessions, projects)— newest visible chat whose recorded cwd maps to a registered project;resolveChatProjectSelectiongainsrecentProjectRootused only in the brand-new-chat fallback.chat-view.tsx: recency resolves live at render (sessions land seconds after boot; an eager seed would freeze it out). Brand-new chats keep a null draft; only switching compose targets clears a prior pick — and only whilesessionIdis null, so a just-minted session keeps the pick its first send used. Empty-state picker displays the resolved id.home-composer.tsx/home-composer-context.ts: same fallback beforeprojects[0]; the seeding effect no longer writesprojects[0]into state (clear-only for stale picks); pickers display the resolved live default.Verification
pnpm test:app— 888 test files passedpnpm typecheck— cleanrecentChatProjectRoot, andresolveHomeComposerProjectrecency cases.