diff --git a/src/renderer/components/team/members/MemberCard.tsx b/src/renderer/components/team/members/MemberCard.tsx
index 7b0e01c4..55db083a 100644
--- a/src/renderer/components/team/members/MemberCard.tsx
+++ b/src/renderer/components/team/members/MemberCard.tsx
@@ -126,6 +126,10 @@ export const MemberCard = ({
const spawnCardClass = launchPresentation.cardClass;
const launchVisualState = launchPresentation.launchVisualState;
const launchStatusLabel = launchPresentation.launchStatusLabel;
+ const displayPresenceLabel =
+ launchVisualState === 'runtime_pending' || launchVisualState === 'permission_pending'
+ ? (launchStatusLabel ?? presenceLabel)
+ : presenceLabel;
const colors = getTeamColorSet(memberColor);
const { isLight } = useTheme();
const pending = taskCounts?.pending ?? 0;
@@ -155,8 +159,7 @@ export const MemberCard = ({
(presenceLabel === 'starting' ||
presenceLabel === 'connecting' ||
launchVisualState === 'runtime_pending');
- const launchBadgeLabel =
- presenceLabel === 'starting' ? presenceLabel : (launchStatusLabel ?? presenceLabel);
+ const launchBadgeLabel = presenceLabel === 'starting' ? presenceLabel : displayPresenceLabel;
const showRuntimeAdvisoryBadge =
!isRemoved &&
Boolean(runtimeAdvisoryLabel) &&
@@ -201,7 +204,7 @@ export const MemberCard = ({
@@ -295,7 +298,7 @@ export const MemberCard = ({
variant="secondary"
className="shrink-0 bg-red-500/15 px-1.5 py-0.5 text-[10px] font-normal leading-none text-red-400"
>
- {presenceLabel}
+ {displayPresenceLabel}
@@ -325,7 +328,7 @@ export const MemberCard = ({
className={`shrink-0 px-1.5 py-0.5 text-[10px] font-normal leading-none ${isRemoved ? 'bg-zinc-600 text-zinc-300' : 'text-[var(--color-text-muted)]'}`}
title={isRemoved ? 'This member has been removed' : activityTitle}
>
- {isRemoved ? 'removed' : presenceLabel}
+ {isRemoved ? 'removed' : displayPresenceLabel}
) : null}
{showStartingSkeleton ? (
diff --git a/src/renderer/components/team/members/MemberDetailHeader.tsx b/src/renderer/components/team/members/MemberDetailHeader.tsx
index f6b910b4..e159488a 100644
--- a/src/renderer/components/team/members/MemberDetailHeader.tsx
+++ b/src/renderer/components/team/members/MemberDetailHeader.tsx
@@ -105,7 +105,7 @@ export const MemberDetailHeader = ({
/>
diff --git a/src/renderer/components/team/members/MemberHoverCard.tsx b/src/renderer/components/team/members/MemberHoverCard.tsx
index 0ed187ad..2ab37e50 100644
--- a/src/renderer/components/team/members/MemberHoverCard.tsx
+++ b/src/renderer/components/team/members/MemberHoverCard.tsx
@@ -157,7 +157,7 @@ export const MemberHoverCard = ({
/>
diff --git a/test/renderer/components/team/members/MemberCard.test.ts b/test/renderer/components/team/members/MemberCard.test.ts
index fd23edf4..3ce27e9b 100644
--- a/test/renderer/components/team/members/MemberCard.test.ts
+++ b/test/renderer/components/team/members/MemberCard.test.ts
@@ -201,6 +201,41 @@ describe('MemberCard starting-state visuals', () => {
});
});
+ it('keeps runtime-pending accessibility copy honest even when launch badge is hidden by an active task', async () => {
+ vi.stubGlobal('IS_REACT_ACT_ENVIRONMENT', true);
+ const host = document.createElement('div');
+ document.body.appendChild(host);
+ const root = createRoot(host);
+
+ await act(async () => {
+ root.render(
+ React.createElement(MemberCard, {
+ member: {
+ ...member,
+ currentTaskId: currentTask.id,
+ },
+ memberColor: 'blue',
+ currentTask,
+ isTeamAlive: true,
+ isTeamProvisioning: false,
+ spawnStatus: 'online',
+ spawnLaunchState: 'runtime_pending_bootstrap',
+ spawnRuntimeAlive: true,
+ spawnLivenessSource: 'process',
+ })
+ );
+ await Promise.resolve();
+ });
+
+ expect(host.textContent).not.toContain('online');
+ expect(host.querySelector('[aria-label="connecting"]')).not.toBeNull();
+
+ await act(async () => {
+ root.unmount();
+ await Promise.resolve();
+ });
+ });
+
it('keeps the starting treatment and runtime summary visible while a runtime is still joining', async () => {
vi.stubGlobal('IS_REACT_ACT_ENVIRONMENT', true);
const host = document.createElement('div');
@@ -258,7 +293,7 @@ describe('MemberCard starting-state visuals', () => {
});
expect(host.textContent).toContain('awaiting permission');
- expect(host.querySelector('[aria-label="connecting"]')).not.toBeNull();
+ expect(host.querySelector('[aria-label="awaiting permission"]')).not.toBeNull();
expect(host.querySelector('.member-waiting-shimmer')).not.toBeNull();
await act(async () => {
diff --git a/test/renderer/components/team/members/MemberDetailHeader.test.ts b/test/renderer/components/team/members/MemberDetailHeader.test.ts
index 2cdfdd8d..17c21bd5 100644
--- a/test/renderer/components/team/members/MemberDetailHeader.test.ts
+++ b/test/renderer/components/team/members/MemberDetailHeader.test.ts
@@ -123,6 +123,7 @@ describe('MemberDetailHeader spawn-aware presence', () => {
expect(host.textContent).toContain('connecting');
expect(host.textContent).not.toContain('online');
+ expect(host.querySelector('[aria-label="connecting"]')).not.toBeNull();
await act(async () => {
root.unmount();
diff --git a/test/renderer/components/team/members/MemberHoverCard.test.ts b/test/renderer/components/team/members/MemberHoverCard.test.ts
index f9fe301d..fe06f72b 100644
--- a/test/renderer/components/team/members/MemberHoverCard.test.ts
+++ b/test/renderer/components/team/members/MemberHoverCard.test.ts
@@ -190,6 +190,7 @@ describe('MemberHoverCard spawn-aware presence', () => {
expect(host.textContent).toContain('connecting');
expect(host.textContent).not.toContain('online');
+ expect(host.querySelector('[aria-label="connecting"]')).not.toBeNull();
await act(async () => {
root.unmount();
@@ -225,6 +226,7 @@ describe('MemberHoverCard spawn-aware presence', () => {
expect(host.textContent).toContain('connecting');
expect(host.textContent).not.toContain('online');
+ expect(host.querySelector('[aria-label="connecting"]')).not.toBeNull();
await act(async () => {
root.unmount();