diff --git a/.planning/2026-08-21-disable-task-board/findings.md b/.planning/2026-08-21-disable-task-board/findings.md
new file mode 100644
index 0000000..d0c5f78
--- /dev/null
+++ b/.planning/2026-08-21-disable-task-board/findings.md
@@ -0,0 +1,6 @@
+# 临时禁用任务看板发现
+
+- 页面入口位于 `public/index.html` 的 `task-board-open`,当前会话跟踪入口为 `task-tracking-control`。
+- 服务端自动状态链路有两层:`dispatchTaskBoardLifecycle()` 与 `enqueueTaskStatusClassification()`。
+- 任务 MCP 动态定义由 `taskBoardMcpToolDefinitionsForSession()` 返回;功能关闭时应返回空数组。
+- 最小改法是使用显式常量门禁,不删除实现,便于重构后重新开放。
diff --git a/.planning/2026-08-21-disable-task-board/progress.md b/.planning/2026-08-21-disable-task-board/progress.md
new file mode 100644
index 0000000..2a49587
--- /dev/null
+++ b/.planning/2026-08-21-disable-task-board/progress.md
@@ -0,0 +1,9 @@
+# 临时禁用任务看板进度
+
+- 2026-08-21:codebase-memory 索引 ready;已定位页面入口、任务跟踪控件、生命周期 Hook、分类 Hook 和任务 MCP 定义入口。
+- 2026-08-21:计划审查补充了关闭态真实 MCP、旧 WebSocket 客户端、Hook no-op、历史数据不变和重启失败处理的验收标准。
+- 2026-08-21:默认关闭门禁已覆盖页面入口、跟踪控件、任务 MCP、WebSocket、生命周期与自动分类;关闭态专项真实跑完一轮 Codex App 消息后历史 taskTracking 完全不变,退出码 0。
+- 2026-08-21:语法、service、lifecycle、integration 均退出 0;完整回归首次因旧 usage feature 静态字面量断言退出 1,已做最小契约修正。
+- 2026-08-21:完整回归修正后退出 0,`git diff --check` 退出 0;进入重启会话门禁检查。
+- 2026-08-21:重启门禁确认只有当前对话运行;`ccweb` 已重启并在线,PM2 环境确认 `CC_WEB_TASK_BOARD_ENABLED=0`。
+- 2026-08-21:CentOS 7 baseline 单文件包重新构建成功;六项语法检查、MCP initialize 和 tar 内容校验均退出 0,准备提交并推送全部修改。
diff --git a/.planning/2026-08-21-disable-task-board/task_plan.md b/.planning/2026-08-21-disable-task-board/task_plan.md
new file mode 100644
index 0000000..4d90dff
--- /dev/null
+++ b/.planning/2026-08-21-disable-task-board/task_plan.md
@@ -0,0 +1,36 @@
+# 临时禁用任务看板
+
+## 目标
+
+在任务看板重构期间,通过明确的功能门禁禁用页面入口、任务跟踪控件、任务 MCP 暴露和服务端自动 Hook;保留现有实现与数据。完成后重启 ccweb、重新构建 baseline 发布包并推送。
+
+## 阶段
+
+- [x] 1. 定位并固定页面入口与 Hook 门禁点
+- [x] 2. 写入临时功能门禁与回归契约
+- [x] 3. 运行最小验证
+- [x] 4. 检查会话门禁并重启 ccweb
+- [x] 5. 重新构建并验证 CentOS 7 baseline 包
+- [x] 6. 提交全部修改并推送 origin/main
+
+## 约束
+
+- 不删除看板实现、历史状态或配置。
+- 页面不显示看板按钮与加入看板控件。
+- 服务端不执行任务生命周期和自动分类 Hook。
+- 模型不再看到任务状态 MCP 工具。
+- 重启前必须确认除当前对话外没有 running 会话。
+
+## 完成标准
+
+- 默认页面不显示任务看板按钮,当前会话不显示加入/移出看板控件。
+- 默认真实 MCP `tools/list` 不包含任何 `ccweb_task_*`;过期客户端直接调用更新也被拒绝。
+- 默认 WebSocket 看板查询与写入被拒绝,新建会话不能通过遗留参数加入看板。
+- 用户消息、轮次完成和运行事件不会进入生命周期或自动分类 Hook。
+- 隔离测试前后既有 session 的 `taskTracking` 内容与版本不变。
+- 若重启门禁不满足则停止在重启前;满足后执行 `pm2 restart ccweb --update-env`。
+- 发布包固定为 `dist-exe/cc-web-bun-linux-x64-baseline.tar.gz`,完成语法、MCP initialize、tar 校验后提交并推送 `origin/main`。
+
+## 错误记录
+
+- 完整回归首次退出 1:认证 feature 对象新增 `taskBoard` 后,usage statistics 旧静态断言仍匹配单行旧字面量;已更新为分别检查两个 feature 字段。
diff --git a/dist-exe/cc-web-bun-linux-x64-baseline.tar.gz b/dist-exe/cc-web-bun-linux-x64-baseline.tar.gz
index 5ba1177..65dbe6e 100644
Binary files a/dist-exe/cc-web-bun-linux-x64-baseline.tar.gz and b/dist-exe/cc-web-bun-linux-x64-baseline.tar.gz differ
diff --git a/public/app.js b/public/app.js
index 43078d1..d0cc6ca 100644
--- a/public/app.js
+++ b/public/app.js
@@ -305,6 +305,7 @@
let usageDashboardRequestSeq = 0;
const taskBoardViewState = {
open: false,
+ enabled: false,
savedFocus: null,
};
let taskBoardController = null;
@@ -4587,6 +4588,16 @@
}
}
+ function setTaskBoardFeatureEnabled(enabled) {
+ taskBoardViewState.enabled = enabled === true;
+ if (taskBoardOpen) taskBoardOpen.hidden = !taskBoardViewState.enabled;
+ if (taskTrackingControl) taskTrackingControl.hidden = true;
+ if (!taskBoardViewState.enabled) {
+ if (taskBoardViewState.open) closeTaskBoard({ restoreFocus: false });
+ taskTrackingController?.setState({ sessionId: '', disabled: true });
+ }
+ }
+
function setUsageDashboardState(state, message = '') {
const loading = state === 'loading';
usageDashboardState.loading = loading;
@@ -5417,7 +5428,7 @@
}
function openTaskBoard() {
- if (!taskBoardPanel || !taskBoardRoot || !window.CcwebTaskBoard) return;
+ if (!taskBoardViewState.enabled || !taskBoardPanel || !taskBoardRoot || !window.CcwebTaskBoard) return;
if (usageDashboardState.open) closeUsageDashboard({ restoreFocus: false });
if (advancedSessionSearchState.open) closeAdvancedSessionSearch({ restoreFocus: false });
taskBoardViewState.savedFocus = document.activeElement instanceof HTMLElement ? document.activeElement : null;
@@ -5444,6 +5455,11 @@
function syncTaskTrackingControl(snapshot = null) {
if (!taskTrackingControl || !window.CcwebTaskBoard) return;
+ if (!taskBoardViewState.enabled) {
+ taskTrackingControl.hidden = true;
+ taskTrackingController?.setState({ sessionId: '', disabled: true });
+ return;
+ }
const sessionId = snapshot?.sessionId || currentSessionId || '';
const tracking = snapshot?.taskTracking || {};
if (!sessionId) {
@@ -6969,6 +6985,7 @@
loginOverlay.hidden = true;
app.hidden = false;
setUsageDashboardFeatureEnabled(msg.features?.usageStatistics === true);
+ setTaskBoardFeatureEnabled(msg.features?.taskBoard === true);
const flushedSessionSwitch = flushPendingSessionSwitch();
const flushedSessionResume = flushedSessionSwitch ? false : flushPendingSessionResume();
if (!flushedSessionSwitch &&
@@ -6996,6 +7013,7 @@
authToken = null;
wsAuthenticated = false;
setUsageDashboardFeatureEnabled(false);
+ setTaskBoardFeatureEnabled(false);
localStorage.removeItem('cc-web-token');
document.dispatchEvent(new CustomEvent('cc-web-auth-failed'));
loginOverlay.hidden = false;
@@ -11515,6 +11533,8 @@
setUsageDashboardFeatureEnabled(false);
}
+ setTaskBoardFeatureEnabled(false);
+
if (taskBoardOpen && taskBoardPanel && taskBoardRoot) {
taskBoardOpen.addEventListener('click', openTaskBoard);
taskBoardClose?.addEventListener('click', () => closeTaskBoard());
diff --git a/public/index.html b/public/index.html
index 3a23a18..bdec15d 100644
--- a/public/index.html
+++ b/public/index.html
@@ -75,7 +75,7 @@
-