Skip to content

feat(staged): add a project rename flow - #1044

Open
matt2e wants to merge 1 commit into
mainfrom
rename-project
Open

matt2e wants to merge 1 commit into
mainfrom
rename-project

Conversation

@matt2e

@matt2e matt2e commented Nov 9, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds a project-only rename: a rename_project command and an inline title editor on the project page.

The write is metadata-only. It changes the projects.name column and nothing else. Branches, worktrees, Blox workspaces and repo branch names keep whatever was inferred from the old name when they were created, so nothing on disk moves and the rename is safe while sessions in the project are queued or running.

Backend

  • The store gets a name-only rename_project write instead of routing through update_project, which rewrites the primary-repo columns and location from a snapshot and could carry stale values back over a concurrent transfer or branch move.
  • The command body lives in project_commands::rename_project_impl, shared by the Tauri command and a new web-server dispatch arm (required by the web-dispatch parity test).

Frontend

  • ProjectHome paints the new name into the shared projectsDataStore via a new projectRenamed entry point, so the detail-page title, sidebar row and sticky top-bar echo update at once. The store change feed's project-changed refetch then confirms it in every window.
  • The editor sits inside the title row next to the status picker. The pencil button is a sibling of the <h2> rather than a child, so the heading's accessible name is just the project name. The heading stays mounted through editing because ProjectHome measures it for the sticky top-bar handoff.
  • Enter and blur are distinct. Enter validates and keeps the user in the input (blank draft shows an inline error, a failed save refocuses). Blur cancels on a blank draft, an unchanged name, or the same name whose save just failed, so losing focus after a failure cannot loop through refocus → blur → identical rename → toast.
  • Enter during an IME composition (isComposing) is ignored.
  • The failure toast has a stable per-project id so retries replace rather than stack, and it is dismissed on a successful retry or cancel.
  • A delete starting while the editor is open closes it, and the save path refuses to run while deleting.

Tests

  • Store test for the name-only write.
  • Unit tests for rename_project_impl (trimming, blank rejection, missing project).
  • Component tests for the inline editor (Enter and blur saves, pending state, blur cancels, IME guard, toast dismissal on success and cancel, editor closing when deleting flips, heading structure).
  • Store test for the immediate repaint.

🤖 Generated with Claude Code

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Nov 9, 2026 •

Copy link
Copy Markdown
Codex Review Summary: ✅ Completed

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-11-09T05:27:05.209659Z 624d9a8 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to 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" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 624d9a82fe

ℹ️ 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".

Comment on lines +126 to +128
let updated = conn.execute(
"UPDATE projects SET name = ?1, updated_at = ?2 WHERE id = ?3",
params![name, now_timestamp(), id],

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 Badge Keep concurrent metadata writes from undoing renames

When another window or MCP client is adding a first/primary repository concurrently, add_project_repo_impl snapshots project.name before its workspace await and later passes that snapshot to update_project, which rewrites the name along with the repository fields. If this rename completes during that interval, the later repository update silently restores the old name; the primary-repository and removal paths have the same read-then-full-row-write pattern. Updating only the name here protects one race ordering but not the reverse, so the other project metadata writers also need to become column-scoped or otherwise preserve a concurrently renamed value.

Useful? React with 👍 / 👎.

Comment on lines +229 to +234
toast.error('Unable to rename project', {
// One toast per project: a retry replaces the previous failure
// rather than stacking another infinite-duration toast.
id: renameProjectToastId(),
description: message,
duration: Infinity,

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 Badge Dismiss rename errors when the section unmounts

If a rename fails and the user navigates somewhere that unmounts this ProjectSection, this infinite-duration toast is never dismissed because the component's existing onDestroy only stops the poller and flushes drafts. A request that is still pending during unmount can also fail afterward and create the same permanent, now-contextless toast, so teardown should dismiss it and prevent a superseded request from publishing another one.

Useful? React with 👍 / 👎.

Add a project-only rename: a `rename_project` command and an inline title
editor on the project page. The write is metadata-only — it changes the
`projects.name` column and nothing else. Branches, worktrees, Blox
workspaces and repo branch names keep whatever was inferred from the old
name when they were created, so nothing on disk moves and the rename is
safe while sessions in the project are queued or running.

Fitted to what has landed since the original commit:

- The store gets a name-only `rename_project` write instead of routing
  through `update_project`, which rewrites the primary-repo columns and
  `location` from a snapshot and could carry stale values back over a
  concurrent transfer or branch move.
- The command body lives in `project_commands::rename_project_impl`, shared
  by the Tauri command and a new web-server dispatch arm, which the
  web-dispatch parity test requires.
- ProjectHome paints the new name into the shared `projectsDataStore` via a
  new `projectRenamed` entry point, so the detail-page title, sidebar row
  and sticky top-bar echo update at once; the store change feed's
  `project-changed` refetch then confirms it in every window. The window
  title is derived from the sidebar filters, not the project name, so it
  is unaffected.
- The editor sits inside the title row next to the status picker. The
  pencil button is a sibling of the `<h2>` rather than a child, so the
  heading's accessible name is just the project name and heading
  navigation does not land on a control; the heading itself stays mounted
  through editing because ProjectHome measures it for the sticky top-bar
  handoff.

The editor distinguishes Enter from blur. Enter validates and keeps the
user in the input (a blank draft shows an inline error, a failed save
refocuses so the name can be fixed). Blur means the user has moved on: a
blank draft, an unchanged name, or the same name whose save just failed
all cancel and restore the old name, so the input losing focus after a
failure cannot loop through refocus → blur → identical rename → toast.
An Enter that confirms an IME composition candidate (`isComposing`) is
ignored so a half-composed name is never submitted. The failure toast
carries a stable per-project id so retries replace the previous toast
rather than stacking infinite-duration ones, and it is dismissed when a
retry succeeds or the editor is cancelled, so the pinned toast cannot
outlive the error it describes. A delete starting while the editor is
open closes it, and the save path refuses to run while deleting, so the
blur from the disabled input never renames a project that is being torn
down.

Covered by a store test for the name-only write, unit tests for
`rename_project_impl` (trimming, blank rejection, missing project),
component tests for the inline editor (Enter and blur saves, the pending
state, the blur cancels, the IME guard, toast dismissal on success and
cancel, the editor closing when `deleting` flips, and the heading
structure), and a store test for the immediate repaint.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: Matt Toohey <contact@matttoohey.com>
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.

1 participant