diff --git a/Agent.md b/Agent.md
index a9c00225..37aa1ea2 100644
--- a/Agent.md
+++ b/Agent.md
@@ -121,7 +121,7 @@ pkill -f "emrg.server"; rm -f ~/.emrg/emrgd.token; python -m emrg
Python: `uv run pytest tests/ -v` (1123) — import check: `uv run python -c "from emrg.client.app import run_client"`
GUI: `cd emrg/gui && npm test` (89: 45 daemon_client + 20 conn-manager + 8 integration + 6 build-config + 7 gui-state + 3 preload-api) — syntax: `node --check main.js preload.js daemon_client.js`
-Renderer: `cd emrg/gui/renderer && npm run typecheck && npm test` (441: 5 snapshot-store + 9 utils + 3 ErrorBoundary + 2 App smoke + 11 commands + 4 copywriting + 11 i18n + 11 markdown + 15 transcript + 7 TranscriptView + 15 history + 22 composer + 14 Composer + 12 sidebar + 17 Sidebar + 9 fileTree + 9 FileTree + 16 resultPanel + 8 ResultPanel + 29 workspaceView + 8 WorkspaceView + 10 dialog + 6 Dialog + 9 ConfirmDialog + 9 RenameDialog + 10 dialogLists + 3 HelpDialog + 9 MemoryDialog + 6 SkillsDialog + 9 openSession + 6 WelcomeDialog + 8 OpenSessionDialog + 7 NewSessionDialog + 7 rewind + 8 RewindDialog + 7 GithubDeviceDialog + 12 daemonBridge + 6 DaemonBridgeProvider + 23 Shell + 15 DialogHost + 19 SettingsPanel + 6 TaskFormDialog + 5 RantDialog + 4 vendorMarkdown) + `npm run build` → `renderer/dist/`
+Renderer: `cd emrg/gui/renderer && npm run typecheck && npm test` (442: 5 snapshot-store + 9 utils + 3 ErrorBoundary + 2 App smoke + 11 commands + 4 copywriting + 11 i18n + 11 markdown + 15 transcript + 7 TranscriptView + 15 history + 22 composer + 14 Composer + 12 sidebar + 17 Sidebar + 9 fileTree + 9 FileTree + 16 resultPanel + 8 ResultPanel + 29 workspaceView + 8 WorkspaceView + 10 dialog + 6 Dialog + 9 ConfirmDialog + 9 RenameDialog + 10 dialogLists + 3 HelpDialog + 9 MemoryDialog + 6 SkillsDialog + 9 openSession + 6 WelcomeDialog + 8 OpenSessionDialog + 7 NewSessionDialog + 7 rewind + 8 RewindDialog + 7 GithubDeviceDialog + 12 daemonBridge + 6 DaemonBridgeProvider + 24 Shell + 15 DialogHost + 19 SettingsPanel + 6 TaskFormDialog + 5 RantDialog + 4 vendorMarkdown) + `npm run build` → `renderer/dist/`
CI: `uv run pytest` (ubuntu + **windows-2025 matrix** — Windows pytest 回归在 PR CI 即失败,v0.2.29 教训 #725) + GUI tests + **actionlint workflow lint** (`rhysd/actionlint@v1.7.12` gate, #444 — workflow 解析错误在 PR CI 即失败,如 `if:` secrets 上下文)
Re-trigger: `scripts/re-trigger-ci.sh [branch]` (workflow_dispatch, #527 — 替代空 commit 重触发:Actions outage 会整段丢弃 push 事件,dispatch 走 API 路径不受影响)
Git-over-https 兜底: `python scripts/sync-master-from-api.py [--repo owner/name] [--ref master]` — 受限网络下 github.com:443 不可达而 api.github.com 可达时,用 Git Data API 的 verification payload + signature 字节级重建上游 commit(含 web-flow GPG 签名 squash merge,reconstruct_commit 经 hermetic 测试验证 sha 一致)并推进本地 refs;内容对象缺失时 fail-loud 提示改用 git fetch(10+ 周期实证的恢复路径)
diff --git a/emrg/gui/renderer/src/components/Shell.test.tsx b/emrg/gui/renderer/src/components/Shell.test.tsx
index 24affa82..91275120 100644
--- a/emrg/gui/renderer/src/components/Shell.test.tsx
+++ b/emrg/gui/renderer/src/components/Shell.test.tsx
@@ -1,5 +1,5 @@
import { afterEach, describe, expect, it, vi } from "vitest";
-import { fireEvent, render, screen, waitFor } from "@testing-library/react";
+import { act, fireEvent, render, screen, waitFor } from "@testing-library/react";
import type { ReactNode } from "react";
import { Shell } from "./Shell";
import { DaemonBridgeProvider } from "./DaemonBridgeProvider";
@@ -322,6 +322,29 @@ describe("Shell (Batch 5 slice 3 chat wiring)", () => {
// ── Batch 5 slice 8:任务表单 + Rant 对话框接线 ──
+ it("任务面板激活时每 5s 轮询 listTasks(vanilla startTaskPoll 语义)", async () => {
+ vi.useFakeTimers();
+ try {
+ const m = mockEmrg();
+ render(wrapper());
+ await vi.waitFor(() => expect(screen.getByTestId("composer")).toBeInTheDocument());
+ fireEvent.click(screen.getByTestId("nav-tasks"));
+ await vi.waitFor(() => expect(screen.getByTestId("task-row")).toBeInTheDocument());
+ const callsAfterActivation = m.listTasks.mock.calls.length;
+ expect(callsAfterActivation).toBeGreaterThanOrEqual(1);
+ // 快进 5s → 再次轮询;再快进 10s → 至少再两次
+ await act(async () => {
+ vi.advanceTimersByTime(5000);
+ });
+ await act(async () => {
+ vi.advanceTimersByTime(10_000);
+ });
+ expect(m.listTasks.mock.calls.length).toBeGreaterThanOrEqual(callsAfterActivation + 2);
+ } finally {
+ vi.useRealTimers();
+ }
+ });
+
it("任务面板「+ 添加任务」→ 打开表单 → 保存 → taskCreate + 刷新列表", async () => {
const m = mockEmrg();
render(wrapper());
diff --git a/emrg/gui/renderer/src/components/Shell.tsx b/emrg/gui/renderer/src/components/Shell.tsx
index cc5f71cf..dec130e2 100644
--- a/emrg/gui/renderer/src/components/Shell.tsx
+++ b/emrg/gui/renderer/src/components/Shell.tsx
@@ -145,6 +145,18 @@ export function Shell() {
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [activeView]);
+ // 任务面板激活时每 5s 轮询任务状态(vanilla startTaskPoll,rant 2026-08-22T07:18:35 方案 B:
+ // 宿主确认轮询、可接受几秒滞后)——让"运行中→待运行"切换与下次倒计时 ≤5s 内自动更新,
+ // 不引入事件推送。幂等:重复启动先清旧定时器;离开任务视图清除防泄漏;轮询失败静默下轮重试。
+ useEffect(() => {
+ if (activeView !== "tasks") return;
+ const id = setInterval(() => {
+ void loadTasks();
+ }, 5000);
+ return () => clearInterval(id);
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [activeView]);
+
// ── workspace 面板动作(vanilla dialogs.js 语义) ──
async function addProject() {
const b = wsBridge();