Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -349,7 +349,10 @@ export function readCachedGitHubCommitAuthor(
}

export function clearGitHubCachesForWorktree(worktreePath: string): void {
githubStatusResource.invalidatePrefix(worktreePath);
// githubStatusResource is keyed by the exact worktreePath (no suffix), so
// invalidate() rather than invalidatePrefix() avoids matching sibling
// worktrees whose paths share this prefix (e.g. /foo/bar vs /foo/bar-2).
githubStatusResource.invalidate(worktreePath);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Use prefix invalidation for GitHub status cache

githubStatusResource keys are not always the exact worktree path: fetchGitHubPRStatus stores branch workspace entries as "${worktreePath}::${branchName}". Switching clearGitHubCachesForWorktree to invalidate(worktreePath) only clears the exact key, so force-refresh and post-mutation clears can leave branch-scoped status entries untouched, causing stale GitHub status to persist until TTL expiry for branch workspaces. Please invalidate by worktree prefix (or explicitly clear both exact and branch-suffixed keys).

Useful? React with 👍 / 👎.

repoContextResource.invalidate(worktreePath);
recordGitHubCacheMetric({
kind: "status",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -195,9 +195,12 @@ export async function applyAiWorkspaceRename(
throw err;
}

// No-op detection: if the cloud row's name still equals oldWorkspaceName,
// the expected-name guard fired (user renamed in the meantime). Revert
// the git rename and skip local DB update so all three stay in lockstep.
// No-op detection: when the cloud's atomic WHERE guard fires (user raced
// a rename ahead of us), updateNameFromHost returns the pre-update row
// unchanged, so `cloudResult.branch` still equals the old branch and not
// the `deduped` value we asked for. That mismatch is the signal to roll
// back the git rename and skip the local DB update, keeping git, cloud,
// and host-local DB in lockstep.
if (gitRenamed && cloudResult.branch !== deduped) {
await ctx
.git(worktreePath)
Expand Down
9 changes: 6 additions & 3 deletions packages/trpc/src/router/v2-workspace/v2-workspace.ts
Original file line number Diff line number Diff line change
Expand Up @@ -250,9 +250,12 @@ export const v2WorkspaceRouter = {
.where(and(...conditions))
.returning();
if (!updated) {
// Row still exists but name raced — return the current row so
// `applyAiWorkspaceRename` sees `branch !== deduped` and rolls
// back the git rename.
// WHERE guard matched zero rows: the row still exists (we just
// fetched it above) but the user's rename raced ahead of ours.
// Return the pre-update `workspace` row (NOT a fresh read) —
// the caller `applyAiWorkspaceRename` checks `cloudResult.branch
// !== deduped` and uses that mismatch as the signal to roll back
// the git rename.
return workspace;
}
return updated;
Expand Down
Loading
Loading