Skip to content

feat(dashboard): 会话状态重构——运行态×任务态正交(P1命名收敛 + P2任务态TODO) - #798

Merged
deepcoldy merged 12 commits into
deepcoldy:masterfrom
xiaoxueSunn:feat/session-state-redesign
Aug 10, 2026
Merged

feat(dashboard): 会话状态重构——运行态×任务态正交(P1命名收敛 + P2任务态TODO)#798
deepcoldy merged 12 commits into
deepcoldy:masterfrom
xiaoxueSunn:feat/session-state-redesign

Conversation

@xiaoxueSunn

@xiaoxueSunn xiaoxueSunn commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

一句话结论

把 dashboard「会话控制」的状态体系从一台运行状态机的可视化,重构为一套状态、两个正交维度(运行态 + 任务态)。本 PR 落地了设计方案的 P1(命名收敛)+ P2(任务态 TODO 提取与可视化);P3(「需要你」自动触发源)、P4(看板去拖拽 + 与监控室融合)为后续。全部 54 个相关单测通过,dashboard bundle 构建通过。

颜色/按钮配色属可后续灵活微调项,本 PR 采用当前评审通过的效果(TODO 徽标橙红、「需要你」信号红)。


本 PR 实际改了什么(落地范围)

# 改动 阶段 涉及文件
1 统一状态列命名:砍掉"待确认"(与"需要你"同物异名),"启动中"并入"进行中" P1 sessions.ts / preferences.ts / i18n.ts
2 新增任务态字段 openTodos: {total, done, remaining, hasInProgress, items[]},由 transcript 解析器顺带提取,读不到的 CLI 标"未知/不支持"、不硬猜 P2 services/todo-state.ts / core/dashboard-rows.ts
3 覆盖三种 todo 方言:Claude Code TodoWrite、本环境内建 Task* 工具增量重放、Codex/traex update_plan P2 services/todo-state.ts
4 「待办」列改由任务态驱动(有未完成 TODO),不再拿 idle 冒充 P2 sessions.ts
5 卡片显式展示任务态 TODO 徽标;徽标可悬浮预览、点击固定、复制清单;浅色模式配色(完成项浅、未完成项深,不发白) P2 sessions-page.tsx / style.css

关键:运行态与任务态正交。 会话可以「运行态=空闲 + 任务态=有未完成 TODO」(机器停了但活没干完 ← 正是要抓的),也可以「空闲 + 已交付」(真没事)。旧设计把两者混成一个 idle/待办,所以没用。因此 TODO 徽标只要"还有未完成项"就贴,与它在哪列无关——列告诉你"现在轮到谁动",TODO 告诉你"这会话手上还压着几项活"。

可视化验证

浅色模式下用合成数据铺满四列(覆盖 Claude + Codex 的 TODO 展示,及「需要你」三种触发),确认徽标与信号渲染正确。样式改的是真实代码,非 mock 页面。(真机此刻无"正在等你操作"的会话,「需要你」列无法用真机触发,故用合成数据演示样式。)

四列状态板(需要你 / 进行中 / 待办 / 空闲,橙红 TODO 徽标 + 红色「需要你」信号):

四列状态板

TODO 徽标点击固定后的浮层(列出完整清单:完成项浅色划线、进行中/待办深色,带复制清单按钮):

TODO 悬浮浮层

测试

  • test/todo-state.test.ts:19 通过(三方言解析、items 顺序、计数)
  • test/dashboard-sessions-ui.test.ts:17 通过
  • test/dashboard-preferences.test.ts:18 通过
  • dashboard bundle:node scripts/build-dashboard.mjs 通过

仍未做(后续 PR)

  • P3:「需要你」自动触发源——@真人自动判定 + 意图参数(for-you=action / fyi
  • P4:看板去拖拽改列(改为钉住/归档 + 标注可过期)、与监控室融合为一份"关注集"的两种视图密度

📐 完整设计方案 v1(送评审原文)

botmux 会话状态管理 · 重设计方案 v1

版本基线 v3.7.1|源码 Env/repos/botmux

0. 一页结论

  • 问题:dashboard「会话控制」的状态体系是一台运行状态机的可视化,不是任务管理。状态被起了两次名(状态板/看板列名不通)、用 idle 冒充"待办"、手动拖拽会永久钉死且不自更新、「需要你」靠 agent 自觉上报易漏。
  • 方案:底层收敛成一套状态、两个正交维度(运行态 + 任务态),两视图共用同名;「待办」改由任务态(有没有未完成 TODO)定义;「需要你」新增**@真人 / 意图参数等自动触发源;看板去掉拖拽改列**、退化为"关注集 + 钉住/归档标记",并与监控室融合为同一份"关注集"的两种视图密度。
  • 可行性:核心增量(读 CLI transcript 拿 todo/plan)已有地基——botmux 已在解析 Claude/Codex 的 transcript,只是没提取 todo。优先只做 Claude + Codex。

1. 现状(问题定位,均已核对源码)

1.1 底层信号只有一份,但被两视图各起各名

  • 运行态 statussrc/types.ts:113):starting / working / idle / limited / analyzing(遗留) / dormant + closed
  • 识别机制(src/worker.ts):不理解 CLI 在做什么,每 2 秒扫终端屏幕,用每个 CLI 适配器的 readyPattern / busyPattern 正则判忙/闲。非语义,CLI 换措辞就可能误判。
  • 「需要你」四个平行标志:pendingRepo / tuiPromptActive / agentAttention / status===limited。其中 agentAttention 靠 agent 主动 botmux send --attentiondaemon.ts:4776),daemon 不自动检测

1.2 两视图映射(同源、异名)

底层 状态板(sessions.ts:332,实时只读) 看板(kanban-model.ts:24,可拖)
needs-you 信号任一 需要你 待确认 ← 同物异名
starting 启动中 进行中(合并)
working/analyzing 干活中 进行中
idle/dormant 空闲 待办
closed (不显示) 已完成
手动/建群未启动 无此概念 待办池

1.3 五个结构性问题

  1. 同物异名:「需要你」=「待确认」。
  2. idle 冒充待办:进程闲着 ≠ 有事要做;做完没关的会话也躺"待办"。
  3. 「启动中」单列意义弱,与「干活中」难区分。
  4. 手动拖拽永久钉死deriveKanbanColumn 判定顺序 closed > 手动 kanbanColumn > 自动,手动值只在三处被写(session-manager.ts:2303/2381dashboard-ipc-server.ts:933),没有任何逻辑因运行态变化去更新它。拖过一次就停在那列,底层 working→idle→dormant 看板纹丝不动。
  5. 待办池混装:拖入"待办池"只改 kanbanColumn 一行、不停 CLI(dashboard-ipc-server.ts:920),导致"真空壳(未启动)"与"被拖进来的活会话"长一样。列位置 ≠ 真实状态。

2. 重设计:一套状态、两个正交维度

2.1 维度 A|运行态(进程/CLI 层,沿用现有)

进行中(starting+working 合并)/ 空闲(idle)/ 已完成(closed)。回答"机器在不在跑"。

2.2 维度 B|任务态(内容/语义层,新增)

有未完成TODO / 已交付 / 无任务。回答"这件事做完没、要不要人管"。

关键:A、B 正交。 一个会话可以是"运行态=空闲 + 任务态=有未完成TODO"(机器停了但活没干完 ← 正是要抓的),也可以"空闲 + 已交付"(真没事)。现状把这俩混成一个"待办/idle",所以没用。

2.3 统一状态列(状态板 & 看板共用同名,只起一次)

来源
需要你 4 信号 + 新增:@了真人 / 意图=action(见 §3)
进行中 运行态 = starting + working
待办(真) 任务态 B = 有未完成 TODO(不再拿 idle 冒充
空闲 idle 且无未完成 todo
已完成 closed(=任务态:已交付)

改动点:① 砍掉"待确认"这个名字;② 「待办」重定义为任务态;③ "启动中"并入"进行中"。

3. 「需要你」的自动触发源(补覆盖缺口)

现状只有 4 个信号且核心靠自觉。新增两个自动/半自动源,与现有互补:

类型 说明
pendingRepo / tuiPromptActive / limited 自动(现有) 保留
agentAttention(--attention) 手动上报(现有) 保留
@了真人 自动(daemon 可判) mention 列表里含真人 open_id 是结构化数据,daemon 直接拿得到,不靠自觉
意图参数 半自动(模型声明) --mention 真人时必带意图,如 for-you=action(等你处理,进"需要你") / fyi(完成通知,不进或只进弱提醒)

兜底策略:只要@了真人 → 至少进"待你看";agent 用意图参数把它升级为"需要你处理"或降级为"仅通知"。即使模型忘带参数也不漏。

  • 与任务态互补:todo 判"活干完没"(客观);意图参数判"要不要你介入"(agent 主观声明)。

4. 看板去拖拽 + 与监控室融合为"关注集"

4.1 看板拖拽:建议砍掉"拖动改列"

  • 拖拽改列现在就是纯人工标签、还会永久钉死误导。改造后状态全自动、命名唯一,"分列"不再需要人干预。
  • 保留的人工价值只有两件:📌置顶/钉住(标注我关心的)、🗄归档(我认为完了)。这两件用筛选/标记即可,不必整块拖拽看板。
  • 标注可过期:底层运行态明显变化时(空闲→进行中),自动解除手动覆盖或提示"手动位置可能已过期",杜绝永久钉死。

4.2 监控室现状(已核对源码)

  • 选择会持久化monitor-room-store.ts 用 localStorage(monitorRoom.sessions.v1),上次添加的会话会记住,不是每次进来重选
  • 展示是自适应网格monitor-room.ts 按会话数自动算行列),多路 Web Terminal 平铺;但每格只是原始终端画面,没有状态/todo/需要你等结构化信息

4.3 融合设计:一份"关注集",两种视图密度

看板与监控室本质是同一东西的两极:看板有结构化状态但没实时画面,监控室有实时画面但没状态。
统一为一个"我关注的会话"集合,一键切换两种密度

  • 卡片密度(=改造后看板):状态列 + todo 进度 + 钉住/归档标记。
  • 终端密度(=监控室):同一批会话的实时画面网格。
  • 卡片上可直接展开 mini 终端;"钉住/关注"只维护一份。

5. L2(读 transcript 拿 todo)落地范围

5.1 可行性:地基已具备

botmux 支持 24 个 CLI,已有 10 个 transcript 解析器。定位+读+解析 jsonl 是现成的,L2 是在已有解析器里多提取一个字段。

5.2 分三档

  • A 档(直接可读完成度):Claude Code TodoWrite(每项 status = pending/in_progress/completed);Codex update_plan 事件(plan[] 每步 {step, status})。
  • B 档(有 transcript 无结构化 todo):cursor/grok/coco/hermes/mtr/pi/traex,只能判"轮次是否终结"。
  • C 档(无解析器):gemini/copilot/kimi/opencode/… 退回运行态+时间推断。

5.3 建议

先只对 Claude + Codex 做 A 档(覆盖绝大多数会话、零 LLM 成本、实时准确)。会话状态加字段 openTodos,由解析器顺带填;读不到的 CLI 标"未知/不支持",不硬猜。不做 LLM 节选判断(太重)。

6. 改动点清单

# 改动 涉及 依赖
1 统一状态列命名,砍"待确认","启动中"并入"进行中" sessions.ts / kanban-model.ts / i18n
2 新增任务态字段 openTodos,Claude/Codex 解析器提取 todo/plan claude-transcript.ts / codex-transcript.ts / 会话模型 L2
3 「待办」列改由任务态驱动 分列函数 #2
4 「需要你」新增"@真人"自动源 daemon mention 处理
5 「需要你」新增意图参数 --mention ... for-you=action/fyi send 硬门 / daemon
6 看板去拖拽改列,改为钉住/归档标记 + 标注可过期 看板前端
7 看板与监控室融合为"关注集"两种密度 看板 + 监控室前端 #6

🤖 Generated with Claude Code

@xiaoxueSunn
xiaoxueSunn requested a review from deepcoldy as a code owner August 9, 2026 06:25

@deepcoldy deepcoldy left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review 结论:Request changes

我在 c54cf25b2(master) + c93b5d6fb(PR head) 的合并树上核对。两处冲突解法是正确的:

  • stalled 仍优先进入 needs-you
  • TODO 徽标已织入新版表格的 renderCell(row, 'status')

但合并树仍有以下阻塞项:

1. [P0] publicReadOnly 会泄露 TODO 明文

openTodos.items[].text 来自 transcript 中的计划/任务正文。master #793src/dashboard/public-redact.ts 没有处理这个新字段,因此匿名 /api/sessionssession.spawned 和未来补上的 session.update 都会原样返回任务正文。我用 REST、spawn SSE、update SSE 三条投影均复现出明文。

请在公开投影中移除 items(可按产品需要只保留计数),并补 REST + 两类 SSE 的回归测试。

2. [P1] 新会话的任务态没有进入实时 dashboard 数据流

src/core/worker-pool.tsscreen_update 状态切换会调用 composeRowFromActive(ds),但发布的 patch 只取 status/token/preview,没有 openTodos。新会话的初始 session.spawned 通常发生在 transcript/TODO 产生前;之后即使 daemon 算出了任务态也被丢掉,中央 aggregator 中的行仍无 openTodos,所以会话停到 idle 后仍进「空闲」而不是「待办」。

请在 settle/status patch 中显式发送 openTodos(清空时也要用 null 覆盖旧值),并用真实 store.applySse 路径锁住 working → idle + unfinished TODO → todo

另:Codex rollout 首次 lookup miss 会被 resolver 缓存 30 秒;readSessionOpenTodos 没有 fresh 语义。实测 rollout 在首次 miss 后创建,立即再读仍为 null,reset resolver cache 后才读到。修实时链路时需一并处理首轮 miss。

3. [P1] sandbox-on 的受支持 CLI 定位不到 transcript

sessionOpenTodos() 没把 session 的 larkAppId 传给 readSessionOpenTodos,后者也没传给 resolveSessionTranscriptPath。sandbox 的 Claude/Codex/Seed/Relay/Aiden transcript 位于 BOT_HOME;resolver 明确依赖 larkAppId 才启用该 fallback。现有 resolver 回归测试已证明“只有 BOT_HOME 文件、无 larkAppId”必为 null。

请把 app id 贯穿查询类型/调用链,并补 Claude 与 Codex BOT_HOME 的 todo-state 测试。

4. [P1] 同步全量解析位于 daemon 主线程热路径,性能应视为 blocker

这里不只是按需 HTTP:每次 worker screen_update 的状态切换都会同步走 composeRowFromActive → readSessionOpenTodos。transcript 在一轮内增长后,mtime/size 必变,缓存 miss 就执行 readFileSyncsplit、逐行 JSON.parse、保留完整 entries 数组;当前算出的 openTodos 还会被上述 patch 丢弃。

本机最小基准:31MB、约 44.5 万条短 JSONL,单次冷读约 398ms,额外 heap 约 117MiB;整个期间阻塞 daemon 事件循环。建议复用已有 JSONL cursor/增量 fold,或移出主线程,不能以 32MB 全量同步解析作为保护。

同时,文件越过 32MB 后当前代码返回 cached?.todos:我已复现末尾从 pending 更新为 completed 后仍永久返回旧 pending。这违反“未知不硬猜”,至少不能把越界前快照冒充当前状态;增量方案可自然解决。

5. [P2] 空快照不满足 last-write-wins

TodoWrite([]) / update_plan({plan: []}) 被折叠成 null,外层又用 if (snap) latest = snap,导致先前 pending 快照继续保留。两种方言均已最小复现。需要区分“没看到快照/快照损坏”和“看到了合法空快照”,后者必须清掉旧状态。

6. 类型需补齐

SessionRow.openTodos 少了运行时实际返回的 items,导致公开投影、SSE patch 与 UI 无法获得完整的静态约束。

正向核对

  • Claude TaskCreate/TaskUpdate 的关联、更新、deleted、metadata-only、顺序,以及 TodoWrite 优先逻辑,在非空快照范围内未发现其它错误;
  • stalled 合并与新版 renderCell(status) 合并处理正确;
  • 状态列/偏好迁移的现有测试通过。

实际验证

  • pnpm build:通过(tsc + dashboard bundle + dist audit)。
  • 相关测试:145 passed(todo-state / sessions UI / preferences / attention / token row / BOT_HOME resolver / public redaction)。
  • pnpm test:14337 passed、25 skipped;另有 1 个 suite hook timeout 与 3 个既有 integration 失败。隔离重跑时 group-join suite 通过,worker argv 仍有 2 个时序断言失败,codex-app-runner 有 1 个随机落在不同 case 的超时;这些文件不在本 PR diff 中,未计入上述 blocker,但全量树当前不能写成全绿。

本次未合并。

xiaoxueSunn and others added 10 commits August 10, 2026 11:34
砍掉「待确认」这个名字,与状态板的「需要你」统一(看板 in_review 列 id 不变,
仅重命名标签为「需要你」/「Needs You」)。「启动中」并入「进行中」:
状态板去掉 starting 独立列,starting/working/analyzing/active 同归 working 列,
标签由「干活中」改为「进行中」。

- BoardColumnId 去掉 'starting';deriveSessionBoardColumn 折叠 starting→working
- DEFAULT_BOARD_ORDER 改为三列;normalizeBoardOrder 迁移旧四列存值(剔除 starting)
- i18n zh/en 同步:board.working=进行中/In Progress、kanban.inReview=需要你/Needs You,
  删除 board.starting / board.startingHint
- 补测:deriveSessionBoardColumn 折叠用例、board-order 旧值迁移用例

方案见「botmux会话状态管理_重设计方案v1」P1。纯命名与列收敛,不改运行态判定逻辑。

Co-Authored-By: Claude <noreply@anthropic.com>
会话状态重设计 P2:把「运行态(进程忙/闲)」与「任务态(活干完没)」拆成正交
两维。运行态沿用 worker 探测;任务态新增,从 CLI 自己维护的待办列表读:
- Claude 家族(claude-code/seed/relay/aiden):TodoWrite 工具调用 input.todos[]
- Codex 家族(codex/traex):update_plan 函数调用 arguments.plan[]
两者都是整表快照后写覆盖,取 transcript 最后一次即当前状态。

- 新增 services/todo-state.ts:纯解析 parseOpenTodos + 带 mtime 失效的缓存读取
  readSessionOpenTodos(对齐 cost-calculator 思路,32MB 上限/512 条 LRU)。
  不支持的 CLI、无 transcript、从未建 todo 一律返回 null,绝不硬猜完成度。
- dashboard-rows:SessionRow 增 openTodos 字段,仅活跃行填充。
- 状态板新增「待办(真)」列:运行态=空闲但有未完成 TODO → 待办,不再拿 idle
  冒充;needs-you / 运行态仍优先于任务态。preferences 迁移旧存值补 todo 列。
- traex rollout 与 codex 逐字节同构,dialect 映射到 codex(含回归用例)。

Co-Authored-By: Claude <noreply@anthropic.com>
验证 P2 时发现:本 botmux 环境的 Claude 系 CLI(relay/seed/claude-code)用的是
内建 Task* 工具(TaskCreate/TaskUpdate),而非开源版的 TodoWrite——全盘无一条真实
TodoWrite 调用,原 P2 的 Claude 分支抓不到任务态。

Task* 是增量模型(不同于 TodoWrite 整表快照):
- TaskCreate 无 taskId,分配号在紧邻 tool_result 文本「Task #N created successfully」
- TaskUpdate {taskId,status} 改状态,status ∈ pending/in_progress/completed/deleted

新增 replayClaudeTaskState:按 tool_use.id 关联 Create 与其结果回填 taskId,重放出
Map<taskId,status> 末态再折叠成 OpenTodos。TodoWrite 快照优先,缺失时回退 Task* 重放。
纯元数据 TaskUpdate(无 status)保留任务默认 pending;deleted 移出清单。

真实数据验证:本会话 4 任务(2完成2待办) 重放得 {total:4,done:2,remaining:2},精确匹配。
补 5 个重放单测(含 deleted、metadata-only、TodoWrite 优先、空清单返 null)。

Co-Authored-By: Claude <noreply@anthropic.com>
待办列此前只显示运行态徽标(空闲/休眠),看不出「为什么这张卡在待办列」——
运行态(机器闲)与任务态(还有未完成 TODO)正交,单看运行态解释不了列归属。

在卡片/详情抽屉/表格的 status 组里加 TodoBadge:显示 done/total,hasInProgress
时带脉冲圆点,title 展开「剩 N 项(共 M 项,已完成 K 项)」。openTodos 缺失或
已全部完成时不渲染。琥珀色区别于运行态 status 的灰/蓝/绿。

Co-Authored-By: Claude <noreply@anthropic.com>
此前 TODO 徽标只显示计数(done/total),悬浮/点击看不到具体是哪几条。
用户反馈:想看到 TODO 列表。

数据层:OpenTodos 加 items:{status,text}[]。文字随方言取——TodoWrite 的
content、Task* 的 subject、Codex update_plan 的 step;Task* 增量重放改用
Map<id,{status,text}> 保序累积;文字清洗(trim/合并空白/限长 120)。

前端:TodoBadge 悬浮/聚焦时弹浮层,列出每条(✓完成删除线灰 / ▶进行中高亮 /
○待办)+ 文字,顶部带「还有 N 项未完成」摘要。卡片和列都是 overflow:hidden,
故浮层 portal 到 body + fixed 定位逃出裁剪,按徽标位置对齐。

测试:补 items 顺序与文字断言(TodoWrite content / Codex step / Task* subject
三方言各一),原计数断言改 toMatchObject 不锁 items。19 passed。

Co-Authored-By: Claude <noreply@anthropic.com>
反馈:悬浮浮层过一会就消失,想复制里面的内容。原实现是 tooltip,鼠标一移开
就 setPos(null) 关掉,根本选不中文字。

改成两态:
- 悬浮=预览(移开即关),顶部提示「点击固定以复制」;
- 点击=固定(pinned),不自动消失,浮层文字可选中(user-select:text),
  右上出现「复制清单」按钮;点浮层/徽标之外或按 Esc 关闭。
复制走 navigator.clipboard(不可用时回退 textarea+execCommand),写入纯文本
清单:顶部摘要 + 每行「[x]/[>]/[ ] 文字」,方便贴到别处。徽标固定态高亮。

document 级 mousedown/keydown 监听用 globalThis.MouseEvent/KeyboardEvent
类型(避开 React 合成事件类型),pinned 时才挂、卸载时移除。

Co-Authored-By: Claude <noreply@anthropic.com>
反馈:浅色模式下浮层文字太浅几乎看不清;且完成/未完成深浅关系反了。

根因:浮层用了 var(--text)/var(--text-secondary),但本主题从未定义这两个 token,
全部回退到近白色 #e6e8ee——浅色底上几乎不可见。改用主题真实存在的 --fg/--faint。

配色语义修正:完成项用 --faint(浅灰)+删除线弱化,待办/进行中用 --fg(深色主文字),
进行中额外加粗——「还没做的」在视觉上更突出,与用户预期一致。

Co-Authored-By: Claude <noreply@anthropic.com>
原来的黄橙(#f5a623)偏"信息"感,行动号召弱。换成偏红的橙红
(#ea580c),保留柔和描边+透明底的克制样式,但更有"该处理"的
提示感,又不像纯红报错。「需要你」列的红色信号保持不变。

颜色/按钮配色后续可再灵活微调。

Co-Authored-By: Claude <noreply@anthropic.com>
board-status-columns.png:四列状态板(需要你/进行中/待办/空闲),
展示橙红 TODO 徽标与红色「需要你」信号。
todo-popover-pinned.png:TODO 徽标点击固定后的浮层,列出完整清单
(完成项浅色划线、进行中/待办深色),带复制清单按钮。

浅色模式合成数据演示,用于 PR 描述可视化验证。

Co-Authored-By: Claude <noreply@anthropic.com>
审核在最新 master 合并树上发现并坐实:

- P0 隐私泄露:openTodos.items[].text 是从 CLI transcript 读出的任务明文,
  与 preview 内容等价,但未进 PRIVATE_SESSION_FIELDS 黑名单,会透传给匿名
  只读看板。整字段加入黑名单——匿名侧不显示 TODO 徽标,列归属靠运行态即可。
  REST 投影与 SSE patch 两条路径都覆盖,补单测坐实。
- P1 实时性:worker-pool 的 screen_update 边沿 patch 未带 openTodos,
  working→idle 时看板「待办」列停在旧值。补 openTodos: ?? null(支持清空)。
- P1 sandbox:sessionOpenTodos / readSessionOpenTodos 未透传 larkAppId,
  bot-home 沙箱下 transcript resolver 定位不到文件。补齐。
- 类型:SessionRow.openTodos 补 items 字段,与 OpenTodos 结构一致。

Co-Authored-By: Claude <noreply@anthropic.com>
@xiaoxueSunn
xiaoxueSunn force-pushed the feat/session-state-redesign branch from c93b5d6 to a42f706 Compare August 10, 2026 03:42
xiaoxueSunn and others added 2 commits August 10, 2026 16:56
三处合并前必修(双评审 Request Changes 共识):
① fresh 贯穿:readSessionOpenTodos 已透传 fresh 到 resolver,绕过 30s miss
   负缓存;worker-pool 状态边沿以 {fresh:true} 读,首轮徽标不再等 30s。补回归测试。
② 主线程全量解析改增量:记住 (dev,ino)+字节前沿 offset+折叠状态,size 未变直接
   返缓存、size 增长只 fold [offset,size 新行;全量与增量共用同一套 fold 语义。
③ >32MiB 一律 fail-closed 返 null——护栏上移到选路之前,增量路径(同 inode 被整体
   覆盖成超大文件)也覆盖,绝不返旧 cache 冒充当前。

顺带修复 dashboard-ipc-server 的 .map(composeRowFromActive) 把 map 的 index 当
opts 传入的类型/语义 bug(新增 opts? 参数后暴露)。

自测:todo-state(25) / public-redact(19) / sessions-ui(38) / preferences(23) 全绿;
tsc 触及文件零新增错误;dashboard bundle 构建通过。

Co-Authored-By: Claude <noreply@anthropic.com>
EOF
)
三轮双审的两处测试修复(均只碰测试,非生产代码 bug):
① fresh 回归假绿:claude-code 分支每次调用现算(newerFile 直读盘),无 30s miss
   负缓存,fresh 是 no-op——删掉生产代码 fresh 透传后 claude 用例照样过(假护栏)。
   改用 codex 分支(cachedTranscriptPathLookup + retryMiss:q.fresh 真有负缓存):
   种下 miss → 落盘 → 非 fresh 仍 null / fresh 当场解析成功。mutation 已坐实:
   把 fresh:q.fresh 改成 false,该用例即失败。
② 测试污染真实 ~/.claude:上一版 fixture 写进 join(homedir(),'.claude','projects',
   <projectKey>) 且 afterEach 只删 .jsonl、不删 projectKey 目录,历次留下空目录
   (PR deepcoldy#792 同类:单测污染开发者真实数据目录)。改为 CODEX_HOME 定向到临时目录,
   beforeAll 设置 / afterAll 还原并整体清理,绝不触碰真实 ~/.claude 或 ~/.codex。
   已清理本机历史残留的 -*-todo-cwd-* 空目录。

Claude 的 Task* 增量重放仍由纯 parseOpenTodos 单测覆盖;增量游标/fail-closed/文件
替换机制与方言无关,codex 落盘用例同样验证。todo-state 24 全绿,无 home 目录污染。

Co-Authored-By: Claude <noreply@anthropic.com>
@deepcoldy
deepcoldy merged commit ecb7dfa into deepcoldy:master Aug 10, 2026
@github-actions

Copy link
Copy Markdown

🚀 Released in v3.12.1

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants