From 48a88010c2ed0d11df7cd55a9fbdaeecbc2c410b Mon Sep 17 00:00:00 2001 From: Jinjing <6427696+AmethystLiang@users.noreply.github.com> Date: Fri, 13 Nov 2026 19:52:32 -0800 Subject: [PATCH] Revert "fix: explain merged workspace cleanup and keep visible reviews fresh (#41200)" This reverts commit 30fa6b43980ab33ddb119fc56aa1d9576445112e. (cherry picked from commit 0b93e1bde5651fd676803370544d4bbf40a4eb60) --- .../refresh/pr-refresh-candidate-policy.ts | 13 -- .../refresh/pr-refresh-pacing.ts | 26 +--- .../refresh/pr-refresh-queue-drainer.ts | 2 +- .../refresh/pr-refresh-queue-scheduler.ts | 9 +- .../pull-requests/refresh/pr-refresh-queue.ts | 8 +- .../refresh/pr-refresh-read-admission.ts | 2 +- .../refresh/pr-refresh-watch-jobs.ts | 6 +- ...merged-review-workspace-notice.fixture.tsx | 66 ---------- .../panel/merged-review-workspace-notice.tsx | 119 ------------------ .../source-control/panel/panel-content.tsx | 14 --- .../deletion/delete-worktree-flow.test.ts | 14 --- .../deletion/delete-worktree-flow.ts | 3 +- .../deletion/worktree-delete-request.ts | 1 - .../surface/WorktreeCardStatusSlot.tsx | 22 +--- .../src/i18n/en-runtime-required.json | 2 - src/renderer/src/i18n/locales/en.json | 5 - .../merged-review-workspace-readiness.ts | 33 ----- 17 files changed, 19 insertions(+), 326 deletions(-) delete mode 100644 src/renderer/src/components/right-sidebar/source-control/panel/merged-review-workspace-notice.fixture.tsx delete mode 100644 src/renderer/src/components/right-sidebar/source-control/panel/merged-review-workspace-notice.tsx delete mode 100644 src/shared/source-control/decisions/merged-review-workspace-readiness.ts diff --git a/src/main/github/pull-requests/refresh/pr-refresh-candidate-policy.ts b/src/main/github/pull-requests/refresh/pr-refresh-candidate-policy.ts index e3a3e12355f..7bed1ef64b1 100644 --- a/src/main/github/pull-requests/refresh/pr-refresh-candidate-policy.ts +++ b/src/main/github/pull-requests/refresh/pr-refresh-candidate-policy.ts @@ -217,19 +217,6 @@ export function visibleCandidateAfterOutcome( } } -const SELECTED_VISIBLE_PRIORITY = 80 -const LIST_VISIBLE_PRIORITY = 40 - -/** The selected workspace's follow-up wins the next list-budget slot; a deselected one rejoins list order. */ -export function visibleFollowUpPriority( - candidate: GitHubPRRefreshCandidate, - priority: number -): number { - return candidate.isSelected === true - ? Math.max(priority, SELECTED_VISIBLE_PRIORITY) - : Math.min(priority, LIST_VISIBLE_PRIORITY) -} - export function hasStaleHead(candidate: GitHubPRRefreshCandidate): boolean { return ( candidate.currentHeadOid != null && diff --git a/src/main/github/pull-requests/refresh/pr-refresh-pacing.ts b/src/main/github/pull-requests/refresh/pr-refresh-pacing.ts index 0353b30c4b4..1644d9c7f16 100644 --- a/src/main/github/pull-requests/refresh/pr-refresh-pacing.ts +++ b/src/main/github/pull-requests/refresh/pr-refresh-pacing.ts @@ -7,13 +7,6 @@ const BACKGROUND_BUDGET_MAX = 20 const ACTIVE_BURST_WINDOW_MS = 30_000 const ACTIVE_BURST_MAX = 3 -export function isSelectedBudgetedPRRefresh(entry: PRRefreshQueueEntry): boolean { - return ( - entry.candidate.isSelected === true && - (entry.reason === 'visible' || (entry.reason === 'swr' && entry.hosted !== undefined)) - ) -} - export function usesActiveRefreshPacing(entry: PRRefreshQueueEntry): boolean { // Selected admission survives deselection while queued; periodic follow-ups clear the flag. return ( @@ -26,7 +19,6 @@ export class PRRefreshPacing { private readonly backgroundStarts: number[] = [] private readonly activeStartsByScope = new Map() private lastBackgroundStartAt = 0 - private lastBudgetedStartWasSelected = false clearActiveBurstWindow(windowId: number): void { const windowPrefix = `${windowId}::` @@ -37,8 +29,7 @@ export class PRRefreshPacing { } } - noteBackgroundStart(entry: PRRefreshQueueEntry): void { - this.lastBudgetedStartWasSelected = isSelectedBudgetedPRRefresh(entry) + noteBackgroundStart(): void { const now = Date.now() this.lastBackgroundStartAt = now this.backgroundStarts.push(now) @@ -59,11 +50,7 @@ export class PRRefreshPacing { return Math.max(spacingDelay, windowDelay) } - order(a: PRRefreshQueueEntry, b: PRRefreshQueueEntry): number { - const priority = this.priority(b) - this.priority(a) - if (priority !== 0) { - return priority - } + activeOrder(a: PRRefreshQueueEntry, b: PRRefreshQueueEntry): number { if (!usesActiveRefreshPacing(a) || !usesActiveRefreshPacing(b)) { return 0 } @@ -73,15 +60,6 @@ export class PRRefreshPacing { return b.queuedAt - a.queuedAt } - private priority(entry: PRRefreshQueueEntry): number { - // Reserve the next budgeted slot for other ready reviews after a selected list read. - return this.lastBudgetedStartWasSelected && - isSelectedBudgetedPRRefresh(entry) && - entry.bypassBackgroundBudget !== true - ? 0 - : entry.priority - } - entryDelay(entry: PRRefreshQueueEntry): number { const activeDelay = usesActiveRefreshPacing(entry) ? this.nextActiveBurstDelay(entry) : 0 if (activeDelay > 0) { diff --git a/src/main/github/pull-requests/refresh/pr-refresh-queue-drainer.ts b/src/main/github/pull-requests/refresh/pr-refresh-queue-drainer.ts index 54c39d72422..ad78c8d7c8d 100644 --- a/src/main/github/pull-requests/refresh/pr-refresh-queue-drainer.ts +++ b/src/main/github/pull-requests/refresh/pr-refresh-queue-drainer.ts @@ -196,7 +196,7 @@ export class PRRefreshQueueDrainer { next.bypassBackgroundBudget !== true && isBudgetedBackground(next.reason) ) { - this.pacing.noteBackgroundStart(next) + this.pacing.noteBackgroundStart() this.watchJobs.noteBudgetedStart(next) } if (next.watch && usesActiveRefreshPacing(next)) { diff --git a/src/main/github/pull-requests/refresh/pr-refresh-queue-scheduler.ts b/src/main/github/pull-requests/refresh/pr-refresh-queue-scheduler.ts index 2a2459cd804..973c3d93800 100644 --- a/src/main/github/pull-requests/refresh/pr-refresh-queue-scheduler.ts +++ b/src/main/github/pull-requests/refresh/pr-refresh-queue-scheduler.ts @@ -9,8 +9,7 @@ import { isBudgetedBackground, isMergeabilityPendingOutcome, sameAliasRequestIdentity, - visibleCandidateAfterOutcome, - visibleFollowUpPriority + visibleCandidateAfterOutcome } from './pr-refresh-candidate-policy' import type { PreparedPRBranchRead, @@ -110,7 +109,7 @@ export class PRRefreshQueueScheduler { candidate: current, aliases: new Map(aliases.map((alias) => [alias.cacheKey, alias])), reason: 'visible', - priority: visibleFollowUpPriority(current, priority), + priority, dueAt, queuedAt: this.queue.nextOrder(), followUp: !foreground, @@ -250,7 +249,7 @@ export class PRRefreshQueueScheduler { candidate: followUpCandidate, aliases: new Map(aliases.map((alias) => [alias.cacheKey, alias])), reason: 'visible', - priority: visibleFollowUpPriority(followUpCandidate, priority), + priority, dueAt, queuedAt: this.queue.nextOrder(), bypassBackgroundBudget: pendingDueAt !== null, @@ -262,7 +261,7 @@ export class PRRefreshQueueScheduler { ordered(): PRRefreshQueueEntry[] { this.watchJobs.age(this.queue.values(), Date.now()) - return this.queue.ordered((a, b) => this.pacing.order(a, b)) + return this.queue.ordered((a, b) => this.pacing.activeOrder(a, b)) } nextQueuedWakeDelay(excludedKey: string): number | null { diff --git a/src/main/github/pull-requests/refresh/pr-refresh-queue.ts b/src/main/github/pull-requests/refresh/pr-refresh-queue.ts index 77db82b084b..835be7ef883 100644 --- a/src/main/github/pull-requests/refresh/pr-refresh-queue.ts +++ b/src/main/github/pull-requests/refresh/pr-refresh-queue.ts @@ -13,8 +13,7 @@ import { POST_PUSH_DELAY_MS, refreshKey, sameAliasRequestIdentity, - shouldSkipFresh, - visibleFollowUpPriority + shouldSkipFresh } from './pr-refresh-candidate-policy' export type PRRefreshQueueEntry = { @@ -123,7 +122,6 @@ export class PRRefreshQueue { continue } entry.candidate = candidateFor(entry.key, entry.candidate) - entry.priority = visibleFollowUpPriority(entry.candidate, entry.priority) entry.dueAt = Math.max( freshRetryAt(entry.candidate) ?? Date.now(), this.backgroundNotBefore.get(entry.key)?.until ?? 0 @@ -277,14 +275,14 @@ export class PRRefreshQueue { } ordered( - pacingOrder: (a: PRRefreshQueueEntry, b: PRRefreshQueueEntry) => number + activeOrder: (a: PRRefreshQueueEntry, b: PRRefreshQueueEntry) => number ): PRRefreshQueueEntry[] { const now = Date.now() return Array.from(this.entries.values()).sort((a, b) => { const aReady = a.dueAt <= now const bReady = b.dueAt <= now if (aReady && bReady) { - return pacingOrder(a, b) || b.priority - a.priority || a.dueAt - b.dueAt + return b.priority - a.priority || activeOrder(a, b) || a.dueAt - b.dueAt } if (aReady !== bReady) { return aReady ? -1 : 1 diff --git a/src/main/github/pull-requests/refresh/pr-refresh-read-admission.ts b/src/main/github/pull-requests/refresh/pr-refresh-read-admission.ts index d6a0f23af2a..4aa01a7a3ab 100644 --- a/src/main/github/pull-requests/refresh/pr-refresh-read-admission.ts +++ b/src/main/github/pull-requests/refresh/pr-refresh-read-admission.ts @@ -53,7 +53,7 @@ export function admitPreparedPRRead( } if (!next.watch && isBackground(next.reason) && !joinedRead) { if (next.bypassBackgroundBudget !== true && isBudgetedBackground(next.reason)) { - pacing.noteBackgroundStart(next) + pacing.noteBackgroundStart() watchJobs.noteBudgetedStart(next) } if (usesActiveRefreshPacing(next)) { diff --git a/src/main/github/pull-requests/refresh/pr-refresh-watch-jobs.ts b/src/main/github/pull-requests/refresh/pr-refresh-watch-jobs.ts index 66d6737f246..a58f7cd6db8 100644 --- a/src/main/github/pull-requests/refresh/pr-refresh-watch-jobs.ts +++ b/src/main/github/pull-requests/refresh/pr-refresh-watch-jobs.ts @@ -6,7 +6,6 @@ import { prChecksDone } from '../../../../shared/github/pull-requests/refresh/pr export { prChecksDone } from '../../../../shared/github/pull-requests/refresh/pr-quiet-refresh' import { reviewRefreshIntervalMs } from '../../../../shared/review-refresh-policy' import type { PRRefreshQueue, PRRefreshQueueEntry } from './pr-refresh-queue' -import { isSelectedBudgetedPRRefresh } from './pr-refresh-pacing' import type { PRRefreshRetryState } from './pr-refresh-retry-state' export type PRWatchJob = NonNullable @@ -97,10 +96,7 @@ export class PRWatchJobRunner { } noteBudgetedStart(entry: PRRefreshQueueEntry): void { - // Selected list reads have their own reserved turn; preserve the watch/list alternation. - if (!isSelectedBudgetedPRRefresh(entry)) { - this.lastBudgetedStartWasWatch = entry.watch !== undefined - } + this.lastBudgetedStartWasWatch = entry.watch !== undefined } /** Re-ranks watch entries before each pick, so aging needs no change to the queue's order. */ diff --git a/src/renderer/src/components/right-sidebar/source-control/panel/merged-review-workspace-notice.fixture.tsx b/src/renderer/src/components/right-sidebar/source-control/panel/merged-review-workspace-notice.fixture.tsx deleted file mode 100644 index d401170403e..00000000000 --- a/src/renderer/src/components/right-sidebar/source-control/panel/merged-review-workspace-notice.fixture.tsx +++ /dev/null @@ -1,66 +0,0 @@ -import { defineFixture } from '../../../../component-fixtures/component-fixture' -import type { HostedReviewInfo } from '../../../../../../shared/source-control/hosted-review/hosted-review-contracts' -import { makeWorktree } from '../../../worktree-jump/worktree-jump-palette-test-fixtures' -import { SourceControlMergedReviewNotice } from './merged-review-workspace-notice' - -const review: HostedReviewInfo = { - provider: 'github', - number: 412, - title: 'Add retry to sync', - state: 'merged', - url: 'https://github.com/acme/app/pull/412', - status: 'success', - updatedAt: '2026-11-10T00:00:00Z', - mergeable: 'UNKNOWN', - headSha: 'a1b2c3d' -} - -const worktree = makeWorktree('repo-1::/work/app-sync', 'app-sync', { - branch: 'refs/heads/feature/sync-retry' -}) - -function Frame({ children }: { children: React.ReactNode }) { - return
{children}
-} - -export const safeToDelete = defineFixture({ - width: 300, - render: () => ( - - - - ) -}) - -export const uncommittedChanges = defineFixture({ - width: 300, - render: () => ( - - - - ) -}) - -export const headMovedPastReview = defineFixture({ - width: 300, - render: () => ( - - - - ) -}) diff --git a/src/renderer/src/components/right-sidebar/source-control/panel/merged-review-workspace-notice.tsx b/src/renderer/src/components/right-sidebar/source-control/panel/merged-review-workspace-notice.tsx deleted file mode 100644 index 938a8dbdccf..00000000000 --- a/src/renderer/src/components/right-sidebar/source-control/panel/merged-review-workspace-notice.tsx +++ /dev/null @@ -1,119 +0,0 @@ -import React from 'react' -import { GitMerge, LoaderCircle, Trash2 } from 'lucide-react' -import { Button } from '@/components/ui/button' -import { useAppStore } from '@/store/index' -import { useTranslate } from '@/i18n/use-translate' -import { - localizedHostedReviewCopy, - resolveSupportedHostedReviewCopyProvider -} from '@/i18n/hosted-review-localized-copy' -import type { HostedReviewInfo } from '../../../../../../shared/source-control/hosted-review/hosted-review-contracts' -import type { Worktree } from '../../../../../../shared/worktree/types' -import { resolveMergedReviewWorkspaceReadiness } from '../../../../../../shared/source-control/decisions/merged-review-workspace-readiness' -import { runWorktreeDelete } from '../../../sidebar/workspace-actions/deletion/delete-worktree-flow' -import { getDeleteStateForWorktreeHost } from '../../../sidebar/workspace-actions/deletion/worktree-delete-state-host-match' - -/** Says whether a merged workspace still holds work; the branch compare alone keeps squash-merged commits listed. */ -export function SourceControlMergedReviewNotice({ - review, - worktree, - statusHeadOid, - uncommittedCount -}: { - review: HostedReviewInfo | null - worktree: Worktree - /** HEAD from the latest successful git status read; null until one completes. */ - statusHeadOid: string | null - uncommittedCount: number -}): React.JSX.Element | null { - const t = useTranslate() - const isDeleting = useAppStore( - (s) => getDeleteStateForWorktreeHost(worktree, s.deleteStateByWorktreeId)?.isDeleting ?? false - ) - const readiness = resolveMergedReviewWorkspaceReadiness({ - review, - headOid: statusHeadOid, - uncommittedCount - }) - // Without a status read the change count is unknown, so say nothing rather than guess. - if (!review || !readiness || !statusHeadOid) { - return null - } - const label = `${ - localizedHostedReviewCopy(resolveSupportedHostedReviewCopyProvider(review.provider), t) - .shortLabel - } #${review.number}` - const canDelete = readiness.kind === 'safe-to-delete' && !worktree.isMainWorktree - - let message: string - if (readiness.kind === 'uncommitted-changes') { - message = t( - 'auto.components.right.sidebar.SourceControl.mergedReviewUncommitted', - '{{review}} is merged, but this workspace has {{count}} uncommitted changes that are not in it.', - { - review: label, - count: readiness.count, - defaultValue_one: - '{{review}} is merged, but this workspace has {{count}} uncommitted change that is not in it.' - } - ) - } else if (readiness.kind === 'head-unverified') { - message = t( - 'auto.components.right.sidebar.SourceControl.mergedReviewHeadMoved', - '{{review}} is merged, but Orca could not confirm that it includes this branch’s current commits.', - { review: label } - ) - } else if (worktree.isMainWorktree) { - message = t( - 'auto.components.right.sidebar.SourceControl.mergedReviewNothingLeft', - '{{review}} is merged and includes this branch’s committed changes.', - { review: label } - ) - } else { - message = t( - 'auto.components.right.sidebar.SourceControl.mergedReviewSafeToDelete', - '{{review}} is merged and includes this branch’s committed changes. Ignored files may still be lost when deleting this workspace.', - { review: label } - ) - } - - return ( -
-
- ) -} diff --git a/src/renderer/src/components/right-sidebar/source-control/panel/panel-content.tsx b/src/renderer/src/components/right-sidebar/source-control/panel/panel-content.tsx index aef82b2e029..122388ceffe 100644 --- a/src/renderer/src/components/right-sidebar/source-control/panel/panel-content.tsx +++ b/src/renderer/src/components/right-sidebar/source-control/panel/panel-content.tsx @@ -8,7 +8,6 @@ import { SourceControlUncommittedSections } from '../listing/uncommitted-section import { CompareUnavailable } from '../sync/compare-summary' import { SourceControlCommitSurface } from './commit-surface' import { SourceControlForkPushNotice } from './fork-push-notice' -import { SourceControlMergedReviewNotice } from './merged-review-workspace-notice' import type { SourceControlPanelReadyProps } from './panel-props' import { useSourceControlScope } from './worktree-scope' import { openGitGraph } from '@/lib/source-control/graph/git-graph-tab' @@ -136,19 +135,6 @@ export function SourceControlPanelContent(props: SourceControlPanelReadyProps) { {/* Keep CommitArea through clean/mid-commit states (#1448); only active conflict operations replace the action surface. */} - {model.isFolder ? null : ( - - )} - {shouldRenderCommitArea( unresolvedConflicts.length, conflictOperation, diff --git a/src/renderer/src/components/sidebar/workspace-actions/deletion/delete-worktree-flow.test.ts b/src/renderer/src/components/sidebar/workspace-actions/deletion/delete-worktree-flow.test.ts index dc4e51ebbbb..179ab483b53 100644 --- a/src/renderer/src/components/sidebar/workspace-actions/deletion/delete-worktree-flow.test.ts +++ b/src/renderer/src/components/sidebar/workspace-actions/deletion/delete-worktree-flow.test.ts @@ -108,8 +108,6 @@ vi.mock('./delete-worktree-failure-toast', () => ({ import { toast } from 'sonner' import { showDeleteWorktreeFailureToast } from './delete-worktree-failure-toast' -import { runWorktreeDelete } from './delete-worktree-flow' - import { runWorktreeDeletesInParallel } from './worktree-delete-execution' function setWorktrees( @@ -161,18 +159,6 @@ describe('delete worktree flow', () => { setWorktrees([]) }) - it('keeps the ignored-file confirmation available when a caller requires it despite skip-confirm', () => { - setWorktrees([{ id: 'ignored-only' }]) - mocks.state.settings.skipDeleteWorktreeConfirm = true - runWorktreeDelete('ignored-only', { forceConfirm: true }) - expect(mocks.state.openModal).toHaveBeenCalledWith('delete-worktree', { - worktreeId: 'ignored-only', - worktreeDeleteIdentities: [{ id: 'ignored-only', instanceId: 'ignored-only-instance' }], - allowSkipConfirm: false - }) - expect(mocks.state.removeWorktree).not.toHaveBeenCalled() - }) - it('revalidates each queued instance immediately before execution', async () => { // Model a host relying on renderer serialization for same-repo branch cleanup. setWorktrees([ diff --git a/src/renderer/src/components/sidebar/workspace-actions/deletion/delete-worktree-flow.ts b/src/renderer/src/components/sidebar/workspace-actions/deletion/delete-worktree-flow.ts index 8a92aad8ce9..743d42febab 100644 --- a/src/renderer/src/components/sidebar/workspace-actions/deletion/delete-worktree-flow.ts +++ b/src/renderer/src/components/sidebar/workspace-actions/deletion/delete-worktree-flow.ts @@ -96,7 +96,6 @@ export function runWorktreeDelete(worktreeId: string, options: WorktreeDeleteOpt const hasLineageChildren = deleteLineage.descendants.length > 0 // Skip-confirm preferences never authorize discarding unsaved editor text. const skipConfirm = - options.forceConfirm !== true && (state.settings?.skipDeleteWorktreeConfirm ?? false) && collectRemovalUnsavedFiles(useAppStore.getState, [target]).length === 0 if (skipConfirm && !hasLineageChildren) { @@ -115,7 +114,7 @@ export function runWorktreeDelete(worktreeId: string, options: WorktreeDeleteOpt ]) } : {}), - ...(hasLineageChildren || options.forceConfirm ? { allowSkipConfirm: false } : {}) + ...(hasLineageChildren ? { allowSkipConfirm: false } : {}) }) } diff --git a/src/renderer/src/components/sidebar/workspace-actions/deletion/worktree-delete-request.ts b/src/renderer/src/components/sidebar/workspace-actions/deletion/worktree-delete-request.ts index 2d1444efcfe..f3d55449f6c 100644 --- a/src/renderer/src/components/sidebar/workspace-actions/deletion/worktree-delete-request.ts +++ b/src/renderer/src/components/sidebar/workspace-actions/deletion/worktree-delete-request.ts @@ -15,7 +15,6 @@ export type WorktreeBatchDeleteOptions = { export type WorktreeDeleteIdentity = Pick export type WorktreeDeleteOptions = { - forceConfirm?: boolean expectedInstanceId?: string /** Pin deletion to the confirmed host because workspace IDs repeat across hosts. */ expectedHostId?: ExecutionHostId diff --git a/src/renderer/src/components/sidebar/workspace-card/surface/WorktreeCardStatusSlot.tsx b/src/renderer/src/components/sidebar/workspace-card/surface/WorktreeCardStatusSlot.tsx index a32b5015990..5d2f1b671ca 100644 --- a/src/renderer/src/components/sidebar/workspace-card/surface/WorktreeCardStatusSlot.tsx +++ b/src/renderer/src/components/sidebar/workspace-card/surface/WorktreeCardStatusSlot.tsx @@ -114,9 +114,6 @@ function getReviewStatusLabel(review: WorktreeCardPrDisplay, t: Translate = tran if (outcome === 'success') { return t('workspace.reviewStatus.checksPassing', '{{review}} checks: Passing', values) } - if (!review.state && !outcome) { - return getReviewLabel(review) - } return t('workspace.reviewStatus.open', '{{review}}: Open', values) } @@ -151,17 +148,14 @@ export function WorktreeCardStatusSlot({ const vmState = vmRuntime ? getEphemeralVmHostState(vmRuntime.status, vmWaking) : null const vmFailure = vmRuntime?.failure ?? null const statusLabel = getWorktreeStatusLabel(status) || status + // Sleeping remains distinct from quiet done/branch/review states; attention keeps its own glyph. + const canShowSleepingStatus = + newCardStyle && showStatus && isSleeping && QUIET_REVIEW_REPLACEABLE_STATUSES.has(status) const canShowReviewStatus = newCardStyle && showStatus && (prDisplay !== null || Boolean(collectionChecks)) && - QUIET_REVIEW_REPLACEABLE_STATUSES.has(status) - // Review outranks sleep: the dimmed row already shows sleep, only the glyph can show the PR. - const canShowSleepingStatus = - newCardStyle && - showStatus && - isSleeping && - !canShowReviewStatus && + !canShowSleepingStatus && QUIET_REVIEW_REPLACEABLE_STATUSES.has(status) const canShowBranchStatus = newCardStyle && @@ -184,14 +178,10 @@ export function WorktreeCardStatusSlot({ isReviewStale && canShowReviewStatus && (collectionChecks || prDisplay) ? `${passiveStatusLabel} · ${t('workspace.links.lastKnownStatus', 'Last known status')}` : passiveStatusLabel - const laneLabel = - canShowReviewStatus && isSleeping - ? `${reviewLabel} · ${getSleepingStatusLabel(t)}` - : reviewLabel const passiveStatusAnnouncement = newCardStyle && isUnread - ? t('workspace.activity.unreadLabel', '{{status}} · Unread', { status: laneLabel }) - : laneLabel + ? t('workspace.activity.unreadLabel', '{{status}} · Unread', { status: reviewLabel }) + : reviewLabel // Working/permission own the lane; retain unread in accessible copy and restore its dot afterward. const showNewCardUnreadAlert = newCardStyle && isUnread && showStatus && status !== 'working' && status !== 'permission' diff --git a/src/renderer/src/i18n/en-runtime-required.json b/src/renderer/src/i18n/en-runtime-required.json index aed48de2de2..cd8ce15c0e7 100644 --- a/src/renderer/src/i18n/en-runtime-required.json +++ b/src/renderer/src/i18n/en-runtime-required.json @@ -429,8 +429,6 @@ "discardFailedSampleMore_other": "{{paths}}, +{{count}} more", "discardFailed_one": "Failed to discard {{count}} file", "discardFailed_other": "Failed to discard {{count}} files", - "mergedReviewUncommitted_one": "{{review}} is merged, but this workspace has {{count}} uncommitted change that is not in it.", - "mergedReviewUncommitted_other": "{{review}} is merged, but this workspace has {{count}} uncommitted changes that are not in it.", "noteCount_one": "{{count}} note", "noteCount_other": "{{count}} notes" }, diff --git a/src/renderer/src/i18n/locales/en.json b/src/renderer/src/i18n/locales/en.json index 3f328ae6c92..a60cee601d7 100644 --- a/src/renderer/src/i18n/locales/en.json +++ b/src/renderer/src/i18n/locales/en.json @@ -14789,11 +14789,6 @@ "locallyResolvedConflictOfKind": "Conflict resolved locally ({{kind}})", "locallyResolvedConflict": "Conflict resolved locally", "pushesToForkTarget": "Pushes to fork {{target}}", - "mergedReviewUncommitted_one": "{{review}} is merged, but this workspace has {{count}} uncommitted change that is not in it.", - "mergedReviewUncommitted_other": "{{review}} is merged, but this workspace has {{count}} uncommitted changes that are not in it.", - "mergedReviewHeadMoved": "{{review}} is merged, but Orca could not confirm that it includes this branch’s current commits.", - "mergedReviewNothingLeft": "{{review}} is merged and includes this branch’s committed changes.", - "mergedReviewSafeToDelete": "{{review}} is merged and includes this branch’s committed changes. Ignored files may still be lost when deleting this workspace.", "abortRebaseConfirmTitle": "Abort rebase?", "abortMergeConfirmTitle": "Abort merge?", "abortRebaseConfirmDescription": "This cancels the rebase in progress and can discard conflict resolutions made during this rebase.", diff --git a/src/shared/source-control/decisions/merged-review-workspace-readiness.ts b/src/shared/source-control/decisions/merged-review-workspace-readiness.ts deleted file mode 100644 index 41f93f7dda0..00000000000 --- a/src/shared/source-control/decisions/merged-review-workspace-readiness.ts +++ /dev/null @@ -1,33 +0,0 @@ -import type { HostedReviewInfo } from '../hosted-review/hosted-review-contracts' - -export type MergedReviewWorkspaceReadiness = - /** HEAD is the merged review's head (or GitHub confirmed it is in the review) and nothing is uncommitted. */ - | { kind: 'safe-to-delete' } - | { kind: 'uncommitted-changes'; count: number } - /** Merged, but this checkout's HEAD cannot be matched to the review, so its commits may be elsewhere. */ - | { kind: 'head-unverified' } - -/** - * Whether a workspace whose review merged still holds work. A squash or rebase merge copies commits onto - * the base with new IDs, so the branch compare keeps listing them; only HEAD identity proves they landed. - */ -export function resolveMergedReviewWorkspaceReadiness(input: { - review: Pick | null - headOid: string | null | undefined - uncommittedCount: number -}): MergedReviewWorkspaceReadiness | null { - const { review, uncommittedCount } = input - if (review?.state !== 'merged') { - return null - } - if (uncommittedCount > 0) { - return { kind: 'uncommitted-changes', count: uncommittedCount } - } - const head = input.headOid?.trim().toLowerCase() ?? '' - const reviewHeads = [review.headSha, review.confirmedContainedHeadOid].map( - (oid) => oid?.trim().toLowerCase() ?? '' - ) - return head.length > 0 && reviewHeads.includes(head) - ? { kind: 'safe-to-delete' } - : { kind: 'head-unverified' } -}