diff --git a/src/main/services/team/TeamConfigReader.ts b/src/main/services/team/TeamConfigReader.ts
index 3f6a830c..d1de9a24 100644
--- a/src/main/services/team/TeamConfigReader.ts
+++ b/src/main/services/team/TeamConfigReader.ts
@@ -8,6 +8,7 @@ import { getTeamFsWorkerClient } from './TeamFsWorkerClient';
import { TeamMembersMetaStore } from './TeamMembersMetaStore';
import type { TeamConfig, TeamMember, TeamSummary, TeamSummaryMember } from '@shared/types';
+import { createCliAutoSuffixNameGuard } from '@shared/utils/teamMemberName';
const logger = createLogger('Service:TeamConfigReader');
@@ -244,6 +245,15 @@ export class TeamConfigReader {
}
}
+ // Defense: drop CLI auto-suffixed duplicates (alice-2) when base name exists.
+ const allNames = Array.from(memberMap.values()).map((m) => m.name);
+ const keepName = createCliAutoSuffixNameGuard(allNames);
+ for (const [key, member] of Array.from(memberMap.entries())) {
+ if (!keepName(member.name)) {
+ memberMap.delete(key);
+ }
+ }
+
const members = Array.from(memberMap.values());
const summary: TeamSummary = {
teamName,
diff --git a/src/main/services/team/TeamMemberResolver.ts b/src/main/services/team/TeamMemberResolver.ts
index 313f3349..d2e5a42d 100644
--- a/src/main/services/team/TeamMemberResolver.ts
+++ b/src/main/services/team/TeamMemberResolver.ts
@@ -6,6 +6,8 @@ import type {
TeamTaskWithKanban,
} from '@shared/types';
+import { createCliAutoSuffixNameGuard } from '@shared/utils/teamMemberName';
+
export class TeamMemberResolver {
resolveMembers(
config: TeamConfig,
@@ -78,6 +80,14 @@ export class TeamMemberResolver {
// (recipient of SendMessage to "user"). It's not a real AI teammate.
names.delete('user');
+ // Defense: hide CLI auto-suffixed duplicates (alice-2) when base name (alice) exists.
+ const keepName = createCliAutoSuffixNameGuard(names);
+ for (const name of Array.from(names)) {
+ if (!keepName(name)) {
+ names.delete(name);
+ }
+ }
+
const members: ResolvedTeamMember[] = [];
for (const name of names) {
const ownedTasks = tasks.filter((task) => task.owner === name);
diff --git a/src/main/services/team/TeamMembersMetaStore.ts b/src/main/services/team/TeamMembersMetaStore.ts
index 66ff8f43..1fb221ff 100644
--- a/src/main/services/team/TeamMembersMetaStore.ts
+++ b/src/main/services/team/TeamMembersMetaStore.ts
@@ -7,6 +7,8 @@ import { atomicWriteAsync } from './atomicWrite';
import type { TeamMember } from '@shared/types';
+import { createCliAutoSuffixNameGuard } from '@shared/utils/teamMemberName';
+
interface TeamMembersMetaFile {
version: 1;
members: TeamMember[];
@@ -90,6 +92,15 @@ export class TeamMembersMetaStore {
deduped.set(normalized.name, normalized);
}
+ // Defense: drop CLI auto-suffixed duplicates (alice-2) when base name exists.
+ const allNames = Array.from(deduped.keys());
+ const keepName = createCliAutoSuffixNameGuard(allNames);
+ for (const name of allNames) {
+ if (!keepName(name)) {
+ deduped.delete(name);
+ }
+ }
+
return Array.from(deduped.values()).sort((a, b) => a.name.localeCompare(b.name));
}
@@ -103,6 +114,15 @@ export class TeamMembersMetaStore {
deduped.set(normalized.name, normalized);
}
+ // Defense: drop CLI auto-suffixed duplicates (alice-2) when base name exists.
+ const allNames = Array.from(deduped.keys());
+ const keepName = createCliAutoSuffixNameGuard(allNames);
+ for (const name of allNames) {
+ if (!keepName(name)) {
+ deduped.delete(name);
+ }
+ }
+
const payload: TeamMembersMetaFile = {
version: 1,
members: Array.from(deduped.values()).sort((a, b) => a.name.localeCompare(b.name)),
diff --git a/src/renderer/components/layout/SortableTab.tsx b/src/renderer/components/layout/SortableTab.tsx
index 84dd9f36..bf366be6 100644
--- a/src/renderer/components/layout/SortableTab.tsx
+++ b/src/renderer/components/layout/SortableTab.tsx
@@ -125,11 +125,7 @@ export const SortableTab = ({
role="tab"
tabIndex={0}
aria-selected={isActive}
- className={
- isTeamTab
- ? 'group flex min-w-0 max-w-[200px] shrink-0 cursor-grab flex-col rounded-md'
- : 'group flex min-w-0 max-w-[200px] shrink-0 cursor-grab items-center gap-2 rounded-md px-3 py-1.5'
- }
+ className="group flex min-w-0 max-w-[200px] shrink-0 cursor-grab items-center gap-2 rounded-md px-3 py-1.5"
style={style}
onClick={(e) => onTabClick(tab.id, e)}
onMouseDown={(e) => onMouseDown(tab.id, e)}
@@ -143,32 +139,18 @@ export const SortableTab = ({
}
}}
>
-
-
- {tab.fromSearch && (
-
-
-
- )}
- {isPinned && (
-
-
-
- )}
-
{tab.label}
-
-
+
+ {tab.fromSearch && (
+
+
+
+ )}
+ {isPinned && (
+
+
+
+ )}
+ {tab.label}
{isTeamTab && (
)}
+
);
};
diff --git a/src/renderer/components/layout/TabBar.tsx b/src/renderer/components/layout/TabBar.tsx
index 4efd68ef..74b5f215 100644
--- a/src/renderer/components/layout/TabBar.tsx
+++ b/src/renderer/components/layout/TabBar.tsx
@@ -309,7 +309,7 @@ export const TabBar = ({ paneId }: TabBarProps): React.JSX.Element => {
outline: isDroppableOver ? '1px dashed var(--color-accent, #6366f1)' : 'none',
outlineOffset: '-1px',
overflowX: 'auto',
- overflowY: 'clip',
+ overflowY: 'hidden',
} as React.CSSProperties
}
>
diff --git a/src/renderer/components/layout/TeamTabSectionNav.tsx b/src/renderer/components/layout/TeamTabSectionNav.tsx
index 913fe1ce..c43125ff 100644
--- a/src/renderer/components/layout/TeamTabSectionNav.tsx
+++ b/src/renderer/components/layout/TeamTabSectionNav.tsx
@@ -71,11 +71,11 @@ export const TeamTabSectionNav = ({
}, [open]);
return (
- e.stopPropagation()}>
+
e.stopPropagation()}>