Repository navigation
feat(staged): show short repo names on the project grid and kanban board - #1071
Merged
Merged
Conversation
The project grid's cards, the board's cards and the filter chips above both named repos by their full path (`block/builderbot`, `block/builderbot/apps/staged`), while the sidebar already showed each repo's badge short name. They now show the same short name. Where the short-name logic lives: - New `src/lib/shared/repoShortLabel.ts`. `repoShortLabel()` returns the short name and hue of the repo's badge, the rule the sidebar already drew, and `repoFullName()` returns the full path for tooltips. No new formatting rule is introduced. Components changed: - `ProjectCardLabels.svelte` (grid cards and board cards): a badged repo shows its short name in its hue. - `ProjectFilterChips.svelte` (chip bar above the grid and the board): a repo chip shows its short name, as the sidebar's Filter menu does. - `ProjectRowContent.svelte` and `SidebarFilterMenu.svelte` (sidebar rows and Filter menu): no visible change, they now call the shared helper instead of looking the badge up inline. How subpaths and collisions render: - A badge belongs to one (repo, subpath) pair, so a monorepo subpath has its own short name: `block/builderbot` + `apps/staged` reads "staged", beside "bb" for the repo itself. - The backend keeps short names unique across all badges (a second "goose" is stored as "goose2", and a rename onto a taken name is refused), so two repos never share a label and the frontend adds no disambiguation. - A repo with no badge has no short name and keeps its full path. That covers a badge that has not loaded yet and a fork-backed repo in the filter chips, whose filter key is the head repo. This matches the sidebar Filter menu's existing fallback. Display label only: - The full path stays as the tooltip (`title`) on every shortened label; a fork-backed card label keeps the fork's path there, as before. - Card repo order (by full path), filter keys, filter chip order, search matching and badge lookups are unchanged. Tests: unit tests for the helper, and the card-label and filter-chip component tests now cover short names, subpaths, tooltips, the no-badge fallback and that a chip still filters on the full repo and subpath. Left alone, still showing full repo paths (via `RepoLabel`), to handle separately if wanted: `RepoCard` (the home "Repos" row, the sidebar's pinned repos and the all-repos view), `BranchCardHeaderInfo`, `DiffModal`, `ProjectTransferRepoRow`, `RepoConfigForm`, `RepoSearchInput`, `SuggestedRepos`, `HashtagInput`, `NewSessionModal` and `ActionsSettingsPanel`. `repoBadgeShortName` in `projectSearchContext.ts` (search matching and the command palette) reads the same badge field but was not rerouted through the new helper. Signed-off-by: Matt Toohey <contact@matttoohey.com> Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com> Signed-off-by: Matt Toohey <contact@matttoohey.com>
Codex Review Summary: ✅ CompletedThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
matt2e
added a commit
that referenced
this pull request
Nov 14, 2026
CI on PR #1069 failed one test at e872926, deterministically: `web_server::blocking_guard_tests::synchronous_commands_touch_only_in_process_state` rejected `rename_project`. Main added that command in 6a8fe91 (#1044) as a plain `fn`, after this branch's Fix A commits, so it ran on the main thread and froze the window for its store write, its read and any wait on the store lock. The guard is right, so the command is converted, not allowlisted. The branch was first rebased onto origin/main 485b576 (#1071), with no conflicts; the nine commits are unchanged by range-diff. On that tree the four guard tests were run before the fix: the one above fails, for `rename_project` only, and the other three pass. Main brought no other plain `fn` command or dispatch arm the guards object to. Converted - `rename_project` is now an `async fn` that only looks up the store handle and hands its whole body, `rename_project_impl`, to `tauri::async_runtime::spawn_blocking`, the pattern of d99d9f7. The body is unchanged. - It is added to `OFFLOADED_COMMANDS`, so the command guard and the arm guard hold it to the handoff-only rule. - Its web `dispatch` arm called `rename_project_impl` inline on a runtime worker. It now goes through `blocking_json`, with the same result and the same error text. Ordering: the audit The command has one frontend call site: the `renameProject` wrapper, called by `handleRenameProject` in ProjectHome, which ProjectSection's inline title editor awaits and which then paints the name into projectsData with `projectRenamed`. - Successive renames of one project. The editor holds `renameProjectSaving` and disables its input, so one mounted editor sends one rename at a time. The flag is component state, though. Opening another project and coming back while a rename is still out mounts a fresh editor, which can send a second. As a synchronous command the first froze the window, so that could not happen. On the pool the two wait on the store lock together and the older name can land last and stay. Exposed, and only when a rename is stuck for seconds. - A read over the local paint. `projectRenamed` writes the name into the project list. A `list_projects` read sent before the rename landed may have read the store before it, and nothing refused it, so it put the old name back until the next list arrived. The usual case is a reload already in flight: the rename's own project-changed reload queues behind it and bumps no generation until it has been applied. Exposed, as a flicker. `list_projects` left the main thread in 8f6db6e, so this came with the rebase onto #1044 and not with this conversion. - Another ordered write for the same project. There is none: no `writeOrder` key is per project. The tag writes for a project go to other tables and commute with the name. Ordering: the fixes - `renameProject` sends through `writeOrder` under `project:<id>`, so a project's rename waits for the one sent before it and for nothing else. Only the command is ordered; the cache invalidation after it is not. - `projectRenamed` records a rename that really changes a name as a local write (`project-writes`). The two places that apply a project list, the reload and `refreshProject`, go through a new `admitProjectList`. A list sent before the rename is set aside, not applied, and the list is read again in the background, since it may carry some other change. The checks run in the order 8c519c1 settled on: refused, then set aside, then recorded, so a list that is set aside leaves no mark. Residual risk - A rename still in flight when its project is deleted. The delete is not sent behind the rename's key, so that a stuck rename cannot hold a delete back. If the delete's row removal won the store lock from a rename that was already waiting, the rename would fail on the missing project and leave its pinned error toast. The delete confirms in a dialog and takes the lock at least twice before it removes the row, so the rename lands first in practice. - A rename that never settles holds back later renames of that project. As a synchronous command it held back every command. - The order is per window, as for every other key. - `projectCreated` paints a new project into the same list without a mark. A list read sent before the create can drop the project until the next reload. Not new and not changed here. Verified - Pre-fix, on the rebased tree, with the pinned 1.99.1 toolchain: `cargo test --lib blocking_guard_tests` gives 3 passed, 1 failed, with the CI message. - `cargo fmt --check` passes. - Prettier passes on the four frontend files changed. - The five new frontend tests pass: one for the wrapper order, three for the list guard, and one pinning that a rename that changes no name marks nothing. With the list guard disabled two of them fail; with the rename key made unique per call the wrapper test fails. Not run, left to CI as asked - The guard tests after the fix, and the rest of the Rust test suite. That the converted command and arm satisfy the guards was checked by reading the guards' rules only. - The full vitest suite. - `cargo clippy --workspace -- -D warnings` and `pnpm check` had not finished when this commit was pushed. - The app itself. Neither race was reproduced live. Signed-off-by: Matt Toohey <contact@matttoohey.com> Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
matt2e
added a commit
that referenced
this pull request
Nov 14, 2026
…commands CI on PR #1069 failed one test at e872926, deterministically: `web_server::blocking_guard_tests::synchronous_commands_touch_only_in_process_state` rejected `rename_project`. Main added that command in 6a8fe91 (#1044) as a plain `fn`, after this branch's Fix A commits, so it ran on the main thread and froze the window for its store write, its read and any wait on the store lock. The guard is right, so the command is converted, not allowlisted. The branch was first rebased onto origin/main 485b576 (#1071), with no conflicts; the nine commits are unchanged by range-diff. On that tree the four guard tests were run before the fix: the one above fails, for `rename_project` only, and the other three pass. Main then moved again while CI ran the first push of this fix (c036996). 22c8335 (#1072, `stagedctl project open`) added three plain `fn` commands, and the same guard rejected them as unlisted. The branch is rebased onto 22c8335, again with no conflicts, and they are handled below. Converted - `rename_project` is now an `async fn` that only looks up the store handle and hands its whole body, `rename_project_impl`, to `tauri::async_runtime::spawn_blocking`, the pattern of d99d9f7. The body is unchanged. - It is added to `OFFLOADED_COMMANDS`, so the command guard and the arm guard hold it to the handoff-only rule. - Its web `dispatch` arm called `rename_project_impl` inline on a runtime worker. It now goes through `blocking_json`, with the same result and the same error text. Listed as synchronous The guard's scan found nothing that blocks in #1072's three commands, but it does not follow method calls, so each was read before it went into `SYNCHRONOUS_COMMANDS`: - `list_pending_project_opens` and `answer_project_open` read and answer a window's parked request: a map behind its own mutex, held for one lookup, and a oneshot send. - `note_window_revealed` records the window in a set behind its own mutex and, if a raise is owed, shows and focuses the native window, sets the PR poll focus hint and records the window as used. The window calls belong on the main thread and the rest is in memory. None reaches the store, a child process or the filesystem. They are not converted, so their ordering is unchanged and they needed no frontend audit. Ordering: the audit The command has one frontend call site: the `renameProject` wrapper, called by `handleRenameProject` in ProjectHome, which ProjectSection's inline title editor awaits and which then paints the name into projectsData with `projectRenamed`. - Successive renames of one project. The editor holds `renameProjectSaving` and disables its input, so one mounted editor sends one rename at a time. The flag is component state, though. Opening another project and coming back while a rename is still out mounts a fresh editor, which can send a second. As a synchronous command the first froze the window, so that could not happen. On the pool the two wait on the store lock together and the older name can land last and stay. Exposed, and only when a rename is stuck for seconds. - A read over the local paint. `projectRenamed` writes the name into the project list. A `list_projects` read sent before the rename landed may have read the store before it, and nothing refused it, so it put the old name back until the next list arrived. The usual case is a reload already in flight: the rename's own project-changed reload queues behind it and bumps no generation until it has been applied. Exposed, as a flicker. `list_projects` left the main thread in 8f6db6e, so this came with the rebase onto #1044 and not with this conversion. - Another ordered write for the same project. There is none: no `writeOrder` key is per project. The tag writes for a project go to other tables and commute with the name. Ordering: the fixes - `renameProject` sends through `writeOrder` under `project:<id>`, so a project's rename waits for the one sent before it and for nothing else. Only the command is ordered; the cache invalidation after it is not. - `projectRenamed` records a rename that really changes a name as a local write (`project-writes`). The two places that apply a project list, the reload and `refreshProject`, go through a new `admitProjectList`. A list sent before the rename is set aside, not applied, and the list is read again in the background, since it may carry some other change. The checks run in the order 8c519c1 settled on: refused, then set aside, then recorded, so a list that is set aside leaves no mark. Residual risk - A rename still in flight when its project is deleted. The delete is not sent behind the rename's key, so that a stuck rename cannot hold a delete back. If the delete's row removal won the store lock from a rename that was already waiting, the rename would fail on the missing project and leave its pinned error toast. The delete confirms in a dialog and takes the lock at least twice before it removes the row, so the rename lands first in practice. - A rename that never settles holds back later renames of that project. As a synchronous command it held back every command. - The order is per window, as for every other key. - `projectCreated` paints a new project into the same list without a mark. A list read sent before the create can drop the project until the next reload. Not new and not changed here. Verified - Pre-fix, on the tree rebased onto 485b576, with the pinned 1.99.1 toolchain: `cargo test --lib blocking_guard_tests` gives 3 passed, 1 failed, with the CI message. - CI run 43540379914 on c036996, which is this commit without the three list entries, merged with 22c8335. Rust lint and Frontend (prettier, svelte-check, tsc, vitest) pass. Rust test: 2271 passed, 1 failed. The failure is the same guard, now naming only #1072's three commands, so `rename_project` passes all four guards there. - Locally on c036996: `cargo fmt --check`, `cargo clippy --workspace -- -D warnings`, `pnpm check` (0 errors) and prettier on the changed frontend files pass. - The five new frontend tests pass: one for the wrapper order, three for the list guard, and one pinning that a rename that changes no name marks nothing. With the list guard disabled two of them fail; with the rename key made unique per call the wrapper test fails. The two test files they are in pass in full (151 tests). - After the second rebase and the list entries: `cargo fmt --check`. Not run, left to CI as asked - Any test after the second rebase and the three list entries. The guard has not been seen to pass with them; they were added from its failure message and from reading the commands. - The Rust test suite and the full vitest suite locally. - The app itself. Neither race was reproduced live. Signed-off-by: Matt Toohey <contact@matttoohey.com> Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
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.
Summary
The project grid's cards, the board's cards and the filter chips above both named repos by their full path (
block/builderbot,block/builderbot/apps/staged), while the sidebar already showed each repo's badge short name. They now show the same short name, with the full path kept as the tooltip.src/lib/shared/repoShortLabel.ts:repoShortLabel()returns the short name and hue of a repo's badge (the rule the sidebar already drew) andrepoFullName()returns the full path for tooltips. No new formatting rule is introduced.ProjectCardLabels.svelte(grid and board cards): a badged repo shows its short name in its hue.ProjectFilterChips.svelte(chip bar above the grid and board): a repo chip shows its short name, as the sidebar's Filter menu does.ProjectRowContent.svelteandSidebarFilterMenu.svelte: no visible change, they now call the shared helper instead of looking the badge up inline.Behaviour notes
Not changed
Other surfaces still show full paths via
RepoLabel(RepoCard,BranchCardHeaderInfo,DiffModal,ProjectTransferRepoRow,RepoConfigForm,RepoSearchInput,SuggestedRepos,HashtagInput,NewSessionModal,ActionsSettingsPanel); these can be handled separately if wanted.Tests
Unit tests for the helper, plus card-label and filter-chip component tests covering short names, subpaths, tooltips, the no-badge fallback and that a chip still filters on the full repo and subpath.
🤖 Generated with Claude Code