diff --git a/packages/views/issues/components/execution-log-section.test.tsx b/packages/views/issues/components/execution-log-section.test.tsx
new file mode 100644
index 000000000..0343f354a
--- /dev/null
+++ b/packages/views/issues/components/execution-log-section.test.tsx
@@ -0,0 +1,72 @@
+// @vitest-environment jsdom
+
+import { cleanup, screen } from "@testing-library/react";
+import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
+import type { AgentTask } from "@multica/core/types";
+import { renderWithI18n } from "../../test/i18n";
+
+const mockState = vi.hoisted(() => ({
+ taskMessagesOptions: vi.fn(),
+}));
+
+vi.mock("@multica/core/chat/queries", () => ({
+ taskMessagesOptions: mockState.taskMessagesOptions,
+}));
+
+vi.mock("../../common/actor-avatar", () => ({
+ ActorAvatar: () => ,
+}));
+
+vi.mock("../../common/task-transcript", () => ({
+ TranscriptButton: ({ title }: { title?: string }) => (
+
+ ),
+}));
+
+vi.mock("./terminate-task-confirm-dialog", () => ({
+ TerminateTaskConfirmDialog: () => null,
+}));
+
+import { ActiveTaskRow } from "./execution-log-section";
+
+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",
+ trigger_summary: "Started from comment",
+ ...overrides,
+ };
+}
+
+beforeEach(() => {
+ cleanup();
+ vi.clearAllMocks();
+ vi.useFakeTimers();
+ vi.setSystemTime(new Date("2026-06-08T08:05:04Z"));
+});
+
+afterEach(() => {
+ vi.useRealTimers();
+});
+
+describe("ActiveTaskRow", () => {
+ it("renders running status as elapsed time only", () => {
+ renderWithI18n();
+
+ expect(screen.getByText("5m 04s")).toBeInTheDocument();
+ expect(screen.queryByText(/events?/i)).not.toBeInTheDocument();
+ expect(screen.getByText("Started from comment")).toBeInTheDocument();
+ expect(screen.getByText("View transcript")).toBeInTheDocument();
+ expect(mockState.taskMessagesOptions).not.toHaveBeenCalled();
+ });
+});
diff --git a/packages/views/issues/components/execution-log-section.tsx b/packages/views/issues/components/execution-log-section.tsx
index debe9c8fa..91f21e554 100644
--- a/packages/views/issues/components/execution-log-section.tsx
+++ b/packages/views/issues/components/execution-log-section.tsx
@@ -15,9 +15,7 @@ import {
} from "@multica/ui/components/ui/tooltip";
import { ActorAvatar } from "../../common/actor-avatar";
import { formatDuration } from "../../agents/components/agent-activity-hover-content";
-import { TranscriptButton, buildTimeline } from "../../common/task-transcript";
-import { taskMessagesOptions } from "@multica/core/chat/queries";
-import { RunningStat } from "./running-stat";
+import { TranscriptButton } from "../../common/task-transcript";
import { failureReasonLabel } from "../../agents/components/tabs/task-failure";
import { useT } from "../../i18n";
import { TerminateTaskConfirmDialog } from "./terminate-task-confirm-dialog";
@@ -245,15 +243,10 @@ function useStatusLabel(status: AgentTask["status"]): string {
}
}
-// One active (running / queued / dispatched / parked) task row. Exported so
-// the issue-detail header live-chip popover renders the exact same row as
-// this panel — same trigger text, same status treatment, same hover-reveal
-// Logs/Stop. The popover hosting it must use `keepMounted` so this row (and
-// its internal confirm dialog) survives the popover closing on Stop click.
-// Running rows read the shared per-task message cache (taskMessagesOptions,
-// kept live by useRealtimeSync's global task:message handler) so every surface
-// — this panel, the header-chip popover, and the transcript dialog — shows the
-// same live "N events (elapsed)" and the same streaming Logs from one source.
+// One active (running / queued / dispatched / parked) task row. Running rows
+// keep status to a single live elapsed timer; transcript and stop stay available
+// as hover actions. Transcript content lazy-loads on click via TranscriptButton,
+// so the row no longer fetches task messages just to render a count.
export function ActiveTaskRow({
task,
issueId,
@@ -268,15 +261,6 @@ export function ActiveTaskRow({
const label = useStatusLabel(task.status);
const trigger = useTriggerText(task);
- // Live message stream for this task — only fetched while running. The shared
- // cache means the panel row, the popover row, and the chip all dedupe to one
- // fetch + one WS-maintained entry.
- const { data: msgs } = useQuery({
- ...taskMessagesOptions(task.id),
- enabled: task.status === "running",
- });
- const items = useMemo(() => (msgs ? buildTimeline(msgs) : undefined), [msgs]);
-
// Running rows show a live-ticking elapsed timer (the ticking digits carry
// "alive", the duration carries "how long"). Only running rows tick.
const [now, setNow] = useState(() => Date.now());
@@ -320,7 +304,7 @@ export function ActiveTaskRow({
{task.status === "running" ? (
<>
-
+ {elapsed}
{label}
>
) : (
@@ -332,8 +316,7 @@ export function ActiveTaskRow({
$.execution_log.transcript_tooltip)}
/>
)}
diff --git a/packages/views/issues/components/running-stat.tsx b/packages/views/issues/components/running-stat.tsx
deleted file mode 100644
index ead204ed9..000000000
--- a/packages/views/issues/components/running-stat.tsx
+++ /dev/null
@@ -1,26 +0,0 @@
-"use client";
-
-import { useT } from "../../i18n";
-
-// Shared "running" stat for the header chip and the execution-log / popover
-// rows so all three read identically: live event count (primary, info blue)
-// followed by the elapsed time in muted parens — e.g. "58 events (2m45s)".
-// Both the count and the elapsed are live (event count via the shared
-// task-messages cache, elapsed via the caller's 1s tick).
-export function RunningStat({
- eventCount,
- elapsed,
-}: {
- eventCount: number;
- elapsed: string;
-}) {
- const { t } = useT("issues");
- return (
-
-
- {t(($) => $.agent_live.event_count, { count: eventCount })}
-
- ({elapsed})
-
- );
-}
diff --git a/packages/views/locales/en/issues.json b/packages/views/locales/en/issues.json
index 131515e88..f72210bd1 100644
--- a/packages/views/locales/en/issues.json
+++ b/packages/views/locales/en/issues.json
@@ -307,8 +307,6 @@
"fallback_name": "Agent",
"tool_count_one": "{{count}} tool",
"tool_count_other": "{{count}} tools",
- "event_count_one": "{{count}} event",
- "event_count_other": "{{count}} events",
"transcript_button": "View transcript",
"stop_button": "Stop",
"stop_tooltip": "Stop agent",
diff --git a/packages/views/locales/ja/issues.json b/packages/views/locales/ja/issues.json
index 8df784905..930acf952 100644
--- a/packages/views/locales/ja/issues.json
+++ b/packages/views/locales/ja/issues.json
@@ -295,7 +295,6 @@
"queued_elapsed_prefix": "{{elapsed}} 待機中",
"fallback_name": "エージェント",
"tool_count_other": "ツール {{count}} 件",
- "event_count_other": "イベント {{count}} 件",
"transcript_button": "トランスクリプトを表示",
"stop_button": "停止",
"stop_tooltip": "エージェントを停止",
diff --git a/packages/views/locales/ko/issues.json b/packages/views/locales/ko/issues.json
index 8eee373f4..4aa8b901c 100644
--- a/packages/views/locales/ko/issues.json
+++ b/packages/views/locales/ko/issues.json
@@ -307,8 +307,6 @@
"fallback_name": "에이전트",
"tool_count_one": "도구 {{count}}개",
"tool_count_other": "도구 {{count}}개",
- "event_count_one": "이벤트 {{count}}개",
- "event_count_other": "이벤트 {{count}}개",
"transcript_button": "트랜스크립트 보기",
"stop_button": "중지",
"stop_tooltip": "에이전트 중지",
diff --git a/packages/views/locales/zh-Hans/issues.json b/packages/views/locales/zh-Hans/issues.json
index 4b14d6963..f72774fc3 100644
--- a/packages/views/locales/zh-Hans/issues.json
+++ b/packages/views/locales/zh-Hans/issues.json
@@ -300,7 +300,6 @@
"queued_elapsed_prefix": "已排队 {{elapsed}}",
"fallback_name": "智能体",
"tool_count_other": "{{count}} 次工具调用",
- "event_count_other": "{{count}} 个事件",
"transcript_button": "查看记录",
"stop_button": "停止",
"stop_tooltip": "停止智能体",