fix(server,web): renaming a grouped project to its repository name sticks#23074

open1
main
GitHub fork: fix/project-rename-to-repo-name
opened8d agobyMmackinleysmith
View on GitHubTwo-way sync enabledPR last imported 2d ago

Problem

Renaming a grouped project (one with more than one checkout) to exactly its repository name doesn't stick. Settings and the sidebar snap back to owner/repo. Fixes #15049.

There are two causes. deriveProjectGroupLabel treats a shared title equal to the repository name as a folder default and shows the repository display path instead. And when every checkout's folder title already equals the new name, projectGroupTitleNeedsUpdate skips the save entirely, so nothing reaches the server.

Change

A folder default and a deliberate rename can have the same text, so the client can't tell them apart without help. The server now records whether a title was set by hand.

  • Server: projection_projects gains has_custom_title (migration 068, default 0). ProjectStore sets it whenever a project.meta-updated event carries a title. Only explicit renames do that: settings, mobile, and the t3_project_update MCP tool. project.created leaves it false. The flag is derived from existing events, so replay stays consistent and no new event field is needed.
  • Contracts: OrchestrationProjectShell.hasCustomTitle is optional, so older servers and cached shells decode as "not renamed", the current behavior. Project shells are sent whole on every update, so web, desktop, mobile, and relay clients get the flag with no other wiring.
  • Label (client-runtime, shared by web and mobile): a shared title wins whenever any member has hasCustomTitle. Otherwise the existing rule applies unchanged, so groups nobody renamed still show owner/repo.
  • Settings save check (web): an edited name is saved unless every member already has that title and the flag. Untouched blurs are still skipped. Mobile already saves whenever the typed name differs from the label shown, so it needed no change.

I didn't backfill the flag for projects renamed before this change. A previous rename to exactly the repository name never reached the server (it hit the skipped save), so a backfill would recover nothing. Other past renames already display correctly.

Scope and approval

Fixes #15049, which a maintainer triaged and confirmed: triage comment. It states the intended behavior: an explicit rename to the plain repository name should stick, while a folder-default title keeps showing owner/repo.

This replaces #15212, which was closed for its model. Julius's review there asked that the default for never-renamed groups be preserved rather than changed. That's why this uses a stored flag instead of dropping the repository-name check.

Verification

Focused tests, run on unfixed code first and failing for the expected reasons, then passing with the fix:

  • projectGrouping.test.ts: two checkouts titled t3code show T3 Code (the repository display name) by default and t3code once renamed (fails on main: expected 'T3 Code' to be 't3code').
  • ProjectStore.test.ts: hasCustomTitle stays false after create and a non-title update, and turns true after a title update (fails on main: expected undefined to equal false).
  • ProjectSettingsPanel.logic.test.ts: renaming to a title every member already has still saves when the flag is missing, and is skipped once every member has it.

vp run check:changed passes after rebasing onto main: format, lint, knip, typecheck of 29 packages, and 9 test files, including the migration manifest test.

Manual, in an isolated dev server (vp run dev --no-seed, worktree-local state). The fixture is two checkouts in folders named demo, both with origin = https://github.com/acme/demo.git, so each folder title equals the repository name. In Settings → Project, I set Name to demo and blurred the field, then reloaded the page.

Before, on unfixed code: the header stays acme/demo while the field shows demo, and after a reload the field snaps back to acme/demo.

After, on this branch, with the same database upgraded in place by migration 068: before the rename the untouched group still reads acme/demo, so the default is preserved. After the rename, the header and field both read demo, and they still do after a reload. The projection shows has_custom_title = 1 on both demo checkouts and 0 on an untouched project.

Not checked: a native mobile run (mobile uses the same label helper and is covered by the shared tests), and two separate environments (I used two checkouts in one environment, which takes the same grouping path).


Built with Claude Opus 5.5 via Claude Code, running in T3 Code.

Checks

23 successful · 6 skipped
All checks passed
2970a67

Added 1 commit

  • fix(server,web): renaming a grouped project to its repository name sticks2970a67
Synced from GitHub

Approvability

Verdict: Approved at 2970a67

Macroscope's review found this PR approvable — This is a focused fix for grouped project renames that adds a backward-compatible persisted marker and preserves the existing repository-name default for untouched projects. Runtime changes are localized to project persistence, shell transport, grouping-label derivation, and the web save check, with targeted tests added.

You can add or adjust custom eligibility rules. Learn more.

Synced from GitHub

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Path: .coderabbit.config.ts
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: f304f616-67f2-44f1-b30f-38f4723a31f4
📥 Commits

Reviewing files that changed from the base of the PR and between d13fc846700e5bb7d63e1bfaabb1e55733912b21 and 2970a672b898961becccb7ddbb72587a2897fa5a.

📒 Files selected for processing: 15
  • apps/server/src/orchestration-v2/ProjectCommands.test.ts
  • apps/server/src/orchestration-v2/ProjectStore.test.ts
  • apps/server/src/orchestration-v2/ProjectStore.ts
  • apps/server/src/orchestration-v2/RuntimePolicy.test.ts
  • apps/server/src/orchestration-v2/ThreadLaunchService.test.ts
  • apps/server/src/orchestration-v2/ThreadTitleRegenerationService.test.ts
  • apps/server/src/persistence/Migrations.ts
  • apps/server/src/persistence/Migrations/068_ProjectionProjectsCustomTitle.ts
  • apps/server/src/persistence/reconcileV2PreviewMigration.test.ts
  • apps/web/src/components/settings/ProjectSettingsPanel.logic.test.ts
  • apps/web/src/components/settings/ProjectSettingsPanel.logic.ts
  • apps/web/src/components/settings/ProjectSettingsPanel.tsx
  • packages/client-runtime/src/state/projectGrouping.test.ts
  • packages/client-runtime/src/state/projectGrouping.ts
  • packages/contracts/src/orchestrationProject.ts

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 9 remain after this review.

📝 Walkthrough

The change adds persisted custom-title state to project shells. Settings group renames and repository-group labels now use that state to distinguish explicitly selected titles from default titles.

Changes: Custom title tracking

Layer / File(s)Summary
Persist custom-title state
packages/contracts/src/orchestrationProject.ts, apps/server/src/persistence/Migrations/068_ProjectionProjectsCustomTitle.ts, apps/server/src/persistence/Migrations.ts, apps/server/src/orchestration-v2/ProjectStore.ts, apps/server/src/orchestration-v2/ProjectStore.test.ts, apps/server/src/orchestration-v2/ProjectCommands.test.ts, apps/server/src/orchestration-v2/RuntimePolicy.test.ts, apps/server/src/orchestration-v2/ThreadLaunchService.test.ts, apps/server/src/orchestration-v2/ThreadTitleRegenerationService.test.ts, apps/server/src/persistence/reconcileV2PreviewMigration.test.ts
Project shells and the database now track whether a title is custom. New projects start with the flag set to false, and title updates set it to true. Migration and tests cover the field and its persistence.
Use custom-title state when saving group renames
apps/web/src/components/settings/ProjectSettingsPanel.logic.ts, apps/web/src/components/settings/ProjectSettingsPanel.tsx, apps/web/src/components/settings/ProjectSettingsPanel.logic.test.ts
Group rename checks now use each member’s title and custom-title state. Tests cover renaming to a default title and avoiding updates when the custom title is already applied.
Derive labels from custom-title state
packages/client-runtime/src/state/projectGrouping.ts, packages/client-runtime/src/state/projectGrouping.test.ts
Repository-group labels now use a shared title when any member has a custom title, including when that title matches the repository name or display name.

Priority: ⬇️ Low

Estimated code review effort: 3 (Moderate) | ~20 minutes

Change: Bug fix · Severity of issue fixed: Low

Suggested reviewers: maria-rcks

Merge Risk: ⚪ Minimal · up to 2970a

The reviewed change preserves explicit project renames without an identified merge-blocking issue. It is ready to merge after normal checks.

Pre-merge checks: Passed 4
Check nameStatusExplanation
Title checkPassedThe title clearly describes the primary fix: grouped project renames to the repository name now persist.
Description checkPassedThe description covers the problem, implementation, scope approval, focused tests, broader checks, manual verification, screenshots, and known limitations.
Linked Issues checkPassedIssue #15049 requires a grouped project to retain a rename to its repository name. ProjectStore now persists hasCustomTitle from title updates and exposes it in project shells. `deriveProjectGroup…
Out of Scope Changes checkPassedThe migration, projection field, contract field, client label logic, settings save logic, and regression tests all support the linked rename fix. The server and contract changes deliver the state requ…
Full details: Linked Issues check

Explanation

Issue #15049 requires a grouped project to retain a rename to its repository name. ProjectStore now persists hasCustomTitle from title updates and exposes it in project shells. deriveProjectGroupLabel uses the shared title when a member has a custom title. projectGroupTitleNeedsUpdate saves an edited title when a member lacks the custom-title flag. The added grouping, store, and settings tests cover these behaviors. The existing default label rule remains for groups without a custom title.

Full details: Out of Scope Changes check

Explanation

The migration, projection field, contract field, client label logic, settings save logic, and regression tests all support the linked rename fix. The server and contract changes deliver the state required by web and shared client code. No unrelated change is demonstrated by the reviewed summary or focused diffs.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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