diff --git a/packages/views/issues/components/workspace-agent-working-chip.test.tsx b/packages/views/issues/components/workspace-agent-working-chip.test.tsx
new file mode 100644
index 000000000..036cbe023
--- /dev/null
+++ b/packages/views/issues/components/workspace-agent-working-chip.test.tsx
@@ -0,0 +1,149 @@
+// @vitest-environment jsdom
+
+import { cleanup, screen } from "@testing-library/react";
+import { beforeEach, describe, expect, it, vi } from "vitest";
+import type { AgentTask } from "@multica/core/types";
+import { renderWithI18n } from "../../test/i18n";
+
+const mockState = vi.hoisted(() => ({
+ snapshot: [] as unknown[],
+ // Captures the agent ids handed to the avatar stack so a test can assert
+ // the stack still reflects distinct agents even when the count counts issues.
+ avatarAgentIds: undefined as string[] | undefined,
+}));
+
+vi.mock("@multica/core/hooks", () => ({
+ useWorkspaceId: () => "ws-1",
+}));
+
+vi.mock("@multica/core/agents", () => ({
+ agentTaskSnapshotOptions: (wsId: string) => ({
+ queryKey: ["agents", "task-snapshot", wsId],
+ }),
+}));
+
+vi.mock("../../agents/components/agent-avatar-stack", () => ({
+ AgentAvatarStack: ({ agentIds }: { agentIds: string[] }) => {
+ mockState.avatarAgentIds = agentIds;
+ return
{agentIds.length}
;
+ },
+}));
+
+vi.mock("../../agents/components/agent-activity-hover-content", () => ({
+ AgentActivityHoverContent: ({ tasks }: { tasks: AgentTask[] }) => (
+ {tasks.length}
+ ),
+}));
+
+vi.mock("@tanstack/react-query", async () => {
+ const actual =
+ await vi.importActual(
+ "@tanstack/react-query",
+ );
+ return {
+ ...actual,
+ useQuery: (opts: { queryKey?: readonly unknown[] }) => {
+ if (opts.queryKey?.[1] === "task-snapshot") {
+ return { data: mockState.snapshot };
+ }
+ return { data: undefined };
+ },
+ };
+});
+
+import { WorkspaceAgentWorkingChip } from "./workspace-agent-working-chip";
+
+function makeTask(overrides: Partial): AgentTask {
+ return {
+ id: "task-1",
+ agent_id: "agent-1",
+ runtime_id: "runtime-1",
+ issue_id: "issue-1",
+ status: "running",
+ priority: 0,
+ dispatched_at: null,
+ started_at: "2026-06-08T08:00:00Z",
+ completed_at: null,
+ result: null,
+ error: null,
+ created_at: "2026-06-08T08:00:00Z",
+ ...overrides,
+ };
+}
+
+beforeEach(() => {
+ cleanup();
+ vi.clearAllMocks();
+ mockState.snapshot = [];
+ mockState.avatarAgentIds = undefined;
+});
+
+describe("WorkspaceAgentWorkingChip", () => {
+ it("counts distinct active issues, not running agents", () => {
+ // Two agents working the SAME issue: the count is about issues, so it
+ // must read "1", not "2" (the old unique-agent behavior). MUL-3875.
+ mockState.snapshot = [
+ makeTask({ id: "t-1", agent_id: "agent-1", issue_id: "issue-1" }),
+ makeTask({ id: "t-2", agent_id: "agent-2", issue_id: "issue-1" }),
+ ];
+
+ renderWithI18n(
+ {}} />,
+ );
+
+ expect(
+ screen.getByRole("button", { name: /working/i }),
+ ).toHaveTextContent("1");
+ // The avatar stack still shows both distinct agents behind that work.
+ expect(mockState.avatarAgentIds).toEqual(["agent-1", "agent-2"]);
+ });
+
+ it("counts each distinct issue once when agents span several issues", () => {
+ mockState.snapshot = [
+ makeTask({ id: "t-1", agent_id: "agent-1", issue_id: "issue-1" }),
+ makeTask({ id: "t-2", agent_id: "agent-2", issue_id: "issue-2" }),
+ makeTask({ id: "t-3", agent_id: "agent-1", issue_id: "issue-3" }),
+ ];
+
+ renderWithI18n(
+ {}} />,
+ );
+
+ expect(
+ screen.getByRole("button", { name: /working/i }),
+ ).toHaveTextContent("3");
+ });
+
+ it("ignores non-running tasks and respects scopedIssueIds", () => {
+ mockState.snapshot = [
+ makeTask({ id: "t-1", issue_id: "issue-1", status: "running" }),
+ makeTask({ id: "t-2", issue_id: "issue-2", status: "queued" }),
+ makeTask({ id: "t-3", issue_id: "issue-3", status: "running" }),
+ ];
+
+ renderWithI18n(
+ {}}
+ scopedIssueIds={new Set(["issue-1"])}
+ />,
+ );
+
+ // Only the running task within scope counts → "1".
+ expect(
+ screen.getByRole("button", { name: /working/i }),
+ ).toHaveTextContent("1");
+ });
+
+ it("shows 0 when no agents are running", () => {
+ mockState.snapshot = [];
+
+ renderWithI18n(
+ {}} />,
+ );
+
+ expect(
+ screen.getByRole("button", { name: /working/i }),
+ ).toHaveTextContent("0");
+ });
+});
diff --git a/packages/views/issues/components/workspace-agent-working-chip.tsx b/packages/views/issues/components/workspace-agent-working-chip.tsx
index 2ffe82772..e9b1a8573 100644
--- a/packages/views/issues/components/workspace-agent-working-chip.tsx
+++ b/packages/views/issues/components/workspace-agent-working-chip.tsx
@@ -65,7 +65,7 @@ export function WorkspaceAgentWorkingChip({
const wsId = useWorkspaceId();
const { data: snapshot = [] } = useQuery(agentTaskSnapshotOptions(wsId));
- const { runningTasks, agentIds } = useMemo(() => {
+ const { runningTasks, agentIds, issueIds } = useMemo(() => {
const running: AgentTask[] = [];
for (const task of snapshot) {
if (task.status !== "running") continue;
@@ -75,11 +75,21 @@ export function WorkspaceAgentWorkingChip({
if (scopedIssueIds && !scopedIssueIds.has(task.issue_id)) continue;
running.push(task);
}
- const unique = [...new Set(running.map((tk) => tk.agent_id))];
- return { runningTasks: running, agentIds: unique };
+ // The count tracks active *issues*, not active agents: several agents
+ // can work the same issue at once, and the chip answers "how many
+ // issues are agents working on right now?" (its filter narrows the
+ // list to exactly those issues). The avatar stack still shows the
+ // distinct agents behind that work.
+ const uniqueIssues = [...new Set(running.map((tk) => tk.issue_id))];
+ const uniqueAgents = [...new Set(running.map((tk) => tk.agent_id))];
+ return {
+ runningTasks: running,
+ agentIds: uniqueAgents,
+ issueIds: uniqueIssues,
+ };
}, [snapshot, scopedIssueIds]);
- const hasAgents = agentIds.length > 0;
+ const hasAgents = issueIds.length > 0;
// Active (brand-filled) class — must explicitly re-pin text and bg in
// every interactive state. Button's `outline` variant ships
// `hover:text-foreground` + `aria-expanded:bg-muted aria-expanded:text-foreground`,
@@ -140,7 +150,7 @@ export function WorkspaceAgentWorkingChip({
max={3}
opacity="full"
/>
- {agentIds.length}
+ {issueIds.length}
{label}
}