diff --git a/src/renderer/src/components/AgentStateDot.tsx b/src/renderer/src/components/AgentStateDot.tsx
index 9cbf203351f..e4068db1749 100644
--- a/src/renderer/src/components/AgentStateDot.tsx
+++ b/src/renderer/src/components/AgentStateDot.tsx
@@ -58,7 +58,7 @@ export const AgentStateDot = React.memo(function AgentStateDot({
>
@@ -76,9 +76,11 @@ export const AgentStateDot = React.memo(function AgentStateDot({
'block rounded-full',
inner,
state === 'blocked' || state === 'waiting' || state === 'permission'
- ? 'bg-red-500 animate-pulse'
+ ? 'bg-red-500'
: state === 'done'
- ? 'bg-sky-500/80'
+ ? // Why: emerald-500 matches StatusIndicator's done dot so the
+ // dashboard and sidebar read as the same state.
+ 'bg-emerald-500'
: 'bg-neutral-500/40'
)}
/>
diff --git a/src/renderer/src/components/dashboard/AgentDashboard.tsx b/src/renderer/src/components/dashboard/AgentDashboard.tsx
index 2f35a29db43..32d9f548412 100644
--- a/src/renderer/src/components/dashboard/AgentDashboard.tsx
+++ b/src/renderer/src/components/dashboard/AgentDashboard.tsx
@@ -255,19 +255,19 @@ const AgentDashboard = React.memo(function AgentDashboard() {
{groupRunning > 0 && (
- {groupRunning}{' '}
+ {groupRunning}{' '}
active
)}
{groupBlocked > 0 && (
- {groupBlocked}{' '}
+ {groupBlocked}{' '}
blocked
)}
{groupDone > 0 && (
- {groupDone}{' '}
+ {groupDone}{' '}
done
)}
diff --git a/src/renderer/src/components/sidebar/StatusIndicator.tsx b/src/renderer/src/components/sidebar/StatusIndicator.tsx
index c30abe71b50..b9de553010d 100644
--- a/src/renderer/src/components/sidebar/StatusIndicator.tsx
+++ b/src/renderer/src/components/sidebar/StatusIndicator.tsx
@@ -1,6 +1,6 @@
import React from 'react'
import { cn } from '@/lib/utils'
-import type { WorktreeStatus } from '@/lib/worktree-status'
+import { getWorktreeStatusLabel, type WorktreeStatus } from '@/lib/worktree-status'
// Why: re-export WorktreeStatus under the existing `Status` alias so the
// sidebar component and the canonical lib share one source of truth — the
@@ -15,15 +15,25 @@ type StatusIndicatorProps = React.ComponentProps<'span'> & {
const StatusIndicator = React.memo(function StatusIndicator({
status,
className,
+ title,
...rest
}: StatusIndicatorProps) {
+ // Why: surface the status label as a native tooltip so hovering the dot
+ // reveals the state — matters especially for 'active' vs 'inactive', which
+ // share the same grey dot (see color-branch comment below). Callers pass
+ // aria-hidden="true" alongside an sr-only label, so the `title` attribute
+ // is ignored by AT and only serves sighted users on hover. Callers can
+ // override by passing their own `title`.
+ const resolvedTitle = title ?? getWorktreeStatusLabel(status)
+
if (status === 'working') {
return (
-
+
)
}
@@ -31,20 +41,22 @@ const StatusIndicator = React.memo(function StatusIndicator({
return (
diff --git a/src/renderer/src/components/sidebar/WorktreeCard.tsx b/src/renderer/src/components/sidebar/WorktreeCard.tsx
index 96115c43684..03dbb82de72 100644
--- a/src/renderer/src/components/sidebar/WorktreeCard.tsx
+++ b/src/renderer/src/components/sidebar/WorktreeCard.tsx
@@ -157,7 +157,7 @@ const WorktreeCard = React.memo(function WorktreeCard({
// that the spinner flickered; the blocked/waiting/done states don't have
// that problem — they're terminal (done) or attention-needed (blocked/
// waiting) and persist until the user acts. Retained "done" snapshots are
- // consulted too so the sky dot keeps glowing after the agent process exits,
+ // consulted too so the done dot keeps glowing after the agent process exits,
// matching the dashboard's retention behavior.
//
// Priority (highest first): permission (blocked/waiting) > heuristic