needs_you 与未知 kind 的诚实降级:它确实有未读,但既不是成功也不是失败。active_run_id 指针非空就显转圈;否则检查未读;都没有则空槽。未读标记不会因为显示了转圈而丢失。末态刻意不叫「已读 / 无事」——已读过的
needs_you 仍然可能待你处理,
清掉的只是提示,不是那件事。
rev2 的第一条纪律:稿子里不许出现系统给不出的数字。 上一版有 14 类虚构表达(排队位置、已等时长、单步耗时、命中数、上传百分比…), 本轮逐条换成真实字段能支撑的说法,换法写在每条右上角。 另一条纪律:产品界面里不写设计理由——解释只出现在这些图鉴标注里,不出现在框内。
图鉴里哪些能真点、哪些只是外观(rev11b 核查)。
样张是简化 DOM,浮层类组件在这里被摊平成静态外观,按钮没有接线——
不写明的话,评审者会以为交互坏了。
能真实触发:① 折叠(过程行 / 思考 / 原始事件 / 技术细节 / Agent 行)——
共 10 处,它们只切换就近祖先的 data-open,不依赖任何浮层载体;
② 长按 / 右键出完整值——rev11b 补了文档级弹层载体后现在全页可用。
仅静态外观(各自旁边已标黄条):心跳浮层 G6-1、三点菜单 G7-2、
删除确认 G7-3、命令面板 G5-3、完整值弹层 G8-2 的那一块。
图鉴里没有任何 data-ov-open / data-menu /
data-goto 钩子——那三类交互只在「原型」页可用。
用户原话:「跑完之后是绿点,点击进去查看后绿点消失,同理红点。」 —— 这不是「状态」语义,是「未读」语义。所以这些点不能叫「这条会话的状态」: 已读之后活儿依然在,只是不再提示你。
槽位按顺序取第一个成立的条件,不是「几种运行状态视觉」:
active_run_id 指针非空 → 转圈gm_unread_kind === "done" → 绿点gm_unread_kind === "stopped" → 红点(cancelled 归此档)needs_you 或未知 kind → 中性点
点进会话即调 POST /sessions/:id/read,槽位回到空白。
绿点 / 红点 / 中性点只用于会话行——它们是未读提示,不是 Run 终态视觉;
其它位置的终态一律用文字徽章。
两条不得越界的措辞:
① 转圈只能说「指针非空」,不能说「有活还没结束」。
on_busy=queue 时后一个 Run 会覆盖指针、前一个终态又无条件清空、
后一个被 claim 时不回写 Session——所以指针为空不等于没有在途的 Run。
要成为完整的在途事实源,需后端从 Session 的非终态 Runs 派生(或终态时重指向下一个非终态 Run)。
② 空槽只能说「无未读提示」——既不能说「无事」,也不得从空槽反推「没有在途 Run」。
已读过的 needs_you 仍然可能待你处理,清掉的只是提示,不是那件事。
本分区共同前提:gmLoopNotifyUser 目前只被 GM 自主循环调用,
普通会话的 run 进入终态时不会点亮未读。在后端补上那一次点亮之前,
本分区画的绿点/红点对普通会话不会真的出现。转圈不受影响。
needs_you 与未知 kind 的诚实降级:它确实有未读,但既不是成功也不是失败。active_run_id 指针非空就显转圈;否则检查未读;都没有则空槽。未读标记不会因为显示了转圈而丢失。needs_you 仍然可能待你处理,
清掉的只是提示,不是那件事。
queued 是还没开始、waiting_for_worker 是没有可用 Worker、waiting_for_rate_limit 是暂停中,把它们统称「在跑」是不实的。可见文字与 aria-label 分别写清楚。status_changed_at)。「计划 15:47 重试」只在 available_at 有值时才写。正在改绑定逻辑
引擎锁已经扩到所有已绑原生会话。一条会话只认它第一次绑定的 runner;请求里带别的 runner 时服务端回正,不再静默切换。
import_count。只有真实的 system/import 消息存在时才原样显示它;按 transcript 数组计数时只能写「当前快照加载 N 个 turn」并注明可能不完整。rev5 再更正:消息/Transcript 契约不返回真实 CLI argv,所以那行不能叫「runner 原始」并贴一条拼出来的命令——运行时直接渲染真实 message.content,不改写。rev7 再更正:上一版那句「已回正到…codex-cli 未生效」是我自己写的转述,不是 producer 的原文,而我手上没有 producer 的逐字文案——所以这里改成显式的「界面转述示意」,等拿到真实文案再逐字替换。命令形态另起一行标「调用形态示意(非真实 argv)」。message.content,不改写、不拼装claude --print --resume <id> <原话>rev2 改动:八种情况一律默认折叠,包括在跑(上一版在跑时默认展开,用户 #5 要求改掉)。 过程无卡无影,只有一条左缘线;只有 diff 摘录块与代码块允许有边界。
retry_of 有值时出现——那是真字段;上一版的「1 次失败重试」是把 error activity 当成了 retry。target/args_summary,Codex tool call 同样没有),所以缺值时只显示真实工具名。② 原始事件不再自己拼 JSON——渲染已存储、已脱敏的 data(来自 workbench.runner.raw)。rev8 再更正:上一版把 runner_type 写成 codex-cli | claude-code 是错的——那两个是 runner ID,真实 runner_type 是事件子类型(形如 codex:… / claude:… / worker:…)。拿不到真实 fixture 时只写占位、不给枚举。result_excerpt 实际行数算(本例就是 +1/−1),没有 hunk header 就不补行号。Claude 的 args_summary 即使有也只是这一次 Edit 的 old/new 行数;Codex 没有 excerpt,降级成普通工具行,不画这张卡。if (!session.native_session_id) return requested;const locked = session.native_session_runner_id;$ npm run cc:test:runner-default exit code 0
+19 / −6——changed_files 里没有逐文件增删数。状态域远不止 M/A/D:porcelain 的 XY 还包括 R/C/U/?/! 与双列组合。rev9 更正一处越权断言:上一版写「worker 原样上报 XY、解析器已经支持」是不成立的——worker 在 worker.mjs:675 对行做了 .trim(),会丢前导列位;现有前端 apps/web/index.html:9964 只取 code.trim()[0],AM 会显示成 A;测试也只覆盖单字符。所以这张样张画的是 producer / parser 的新增能力:保留精确两列、渲染完整状态,并补 AM/MM/UU/?? 等测试。补完之前不得写「已经支持」。未知值中性降级为「Changed」。services/orchestrator/server.mjs | 25 +++++++++++++------ scripts/cc-test-runner-default.mjs | 133 ++++++++++++++++++++ scripts/legacy-runner-probe.mjs | 41 ------------ 3 files changed, 158 insertions(+), 47 deletions(-)
changed_files === null 只能写「未上报」,只有明确 [] 才能写「没有改动文件」。混成一句就是替后端下结论。http(s) 前缀一律不渲染 <a>。rev7 大改:上一版那组样例当前 producer 产不出来——tests_run 在 Mac Worker 里固定初始化为 [],没有任何 collector 会生成「13 passing」;自动产物最多 document 12 + HTML 3,凑不出「共 23 个」;GitHub PR、/tmp/last-message.txt、design 目录 HTML 预览也都没有当前 collector。所以:空 tests_run 整块隐藏,产物只留 artifact_refs producer 真实产出的形态,外链等能力列为后端新增项。tests_run 这一块在样张里不存在,因为当前它恒为空数组——
摆一个采集不到的样子,就是给下游一个做不出来的预期。要展示先补真实采集器。parent_run_id / child_run_ids,Dispatched run… 消息不能证明归属,/tasks 默认 40 条也不能穷举。数不出来的数就不写。服务端不再切换 runner,而是回正到 native_session_runner_id。
| 场景 | 旧行为 | 新行为 |
|---|---|---|
| 已绑会话 + 异引擎 | 静默切换,记忆串 | 回正 + runner 消息 |
| 新会话 | 按请求 | 按请求(不变) |
换引擎 = 新记忆。
按用户参考图 ref-21:一个实体圆角容器,控件全部在容器内的底边一行,
容器上方没有任何分隔线(容器自身的边界就是分隔),发送与停止共用同一个圆形按钮位。
左侧 + 同时是上传文件入口,粘贴图片直接进附件区。
「@ 上下文 3」已整个删掉——产品里不存在这个功能。
png/jpg/txt/md/patch/log 既漏了 GIF 与 WebP,又不能保证 .patch / .log 会被浏览器识别成允许的 MIME。样例文件名也一并换成能确定落在允许集里的。big.txt 超了)· 只收图片(PNG / JPEG / GIF / WebP)与纯文本、Markdown(demo.mov 不收)/goal:它不是「改这条会话的目标」——Session 没有可修改的目标字段;实际行为是以该目标新建一条 GM 会话并发送首条消息,文案照实写。要改当前会话的目标,需另立后端能力。Mac 心跳灯常驻在左栏页脚。它只代表 worker 心跳,不代表 agent 在线——系统里没有后者。
最后一次心跳 12 秒前,在在线窗口内。
rev2 改动:删掉注意力区四行、删掉 workspace 名、删掉 Agent 行的 ▾ 箭头、
AGENTS 从弱眉标升成可折叠的大目录、新对话入口移进 Agent 行的三点菜单。
visibility 不用 display,所以 agent 名、缩进、右边界一个像素都不动。会话行同理,让位的是状态点。触屏没有 hover,≤640 改成两格并列常显。POST /sessions)DELETE /sessions/:id 遇非终态 Run 固定回 409,所以禁用并写明原因active_run_id,而 on_busy=queue 会让指针漏报;
后端扫描全部 Runs 后仍可能回 409。所以确认框里必须有可见的失败反馈——
has_non_terminal_run / deletable,前端据此禁用。
会话会从列表隐藏。消息与执行记录留作审计,目前没有恢复入口。
服务返回 502(网关错误),根因尚未确认。客户端已自动重试 3 次。你刚才打的字还在输入框里,没丢。
text-overflow: ellipsis(flex/grid 子项必须显式 min-width: 0)。对话正文不截断,长了换行。rev9 改方案(用户 2026-08-17 拍板 R-2 → 方案 B):上一版说「H5 完整值在详情浮层里出现」,但那个详情层根本不存在——会话名、Session 标题、附件名、路径都没有全文入口,触屏用户没有任何办法看到全文。现在:桌面 hover 出全文;H5 长按被截断的元素弹出完整值(可关闭、可复制、值自身换行)。完整原值一律存在 data-full 里,显示层才截断——原生会话 ID 之前直接写成 8f1c…,完整 ID 就再也取不回来了。仍禁止中间省略、direction: rtl 头部省略、动态放宽、跑马灯。const locked = session.native_session_id ? session.native_session_runner_id : null; // 故意写很长一行,用来验证内滚而不是换行撑破卡片
title,H5 长按必出弹层。
没截断时不弹层是正确行为,所以验收要用这里,不要用不一定截断的行。
这条会话绑的是 design-agent。之后每一句:存在有效的同-runner binding 时续接原来那条原生会话,否则新开一条。
接进来了,但那边还没发生过对话。
或者在某个 Agent 上开一条新的。
last_used_at);「断开」→「从 Workbench 断开」并说明本地授权不变。rev7 大改:上一版列了 5 个 provider 并给它们都配了动作——超出真实 catalog。严格按 GET /connectors 渲染:当前唯一 connectable 的是飞书;GitHub / Notion / Google Drive 是 coming_soon,无动作(对它们调 connect/refresh/disconnect 会返回 400);Vercel / PostHog 不在 registry,已整行删除。
逐条对照 design/wb-ui-redesign/scenario-inventory.md 第 2 节。
每一行要么指向原型某屏(P-*)/ 图鉴某条(G*-*),要么写明不做 + 为什么。
全表共 — 行,其中不做 — 条。
rev2 新增标记 ▲:本轮相对 rev1 被删除或改写的行,共 — 条。
rev5 收尾:设计侧只剩一条悬而未决,且它不是设计问题,是环境问题。 每条按角色约定给出:业务背景 → 各选项利弊与业务影响 → 我的推荐。
已答复,已落实,不再重列:
rev1 的 Q1–Q7(用户批注 #15–#19 + Tech Lead 查证)·
A-1 失败红点的横杠 → 去掉 ·
A-2 cancelled → 归红点,文字保持「已取消」(Tech Lead 确认成立)·
A-3 composer → 不照搬深色,用阴影/边框/更实的底色做立体感 ·
A-4 「⇢ 接入本地会话」→ 不做 ·
B-1 会话子项加 icon → 不加(Session 没有 icon/avatar 字段)·
B-2 AGENTS 行右侧 → 只放真实计数,不放 ⋯(当前没有 Agent 创建/编辑/删除端点)·
C-1 未读缺口标注 → 在 G1 分区标题下加一句总说明,已落地 ·
C-2 Tech Lead 完整报告 → 已解决,剩余 3 条已逐字收到并在 rev5 修掉。
这一页是写进稿子的事实约束,不是设计说明。上一版正是因为没有这一页, 才在界面里编出了排队位置、等待时长、单步耗时这些系统给不出的数字。
rev7 重写:上一版这一节把它写成了绝对事实,那是错的。 「Session 就是一条连续原生 CLI 会话」「第一句必创建、之后每句必 resume、上下文完全连续」 三句都过度绝对。下面是条件事实版本。
--resume 续上;否则新开一条原生会话。
绑定失效会被清空;绑定窗口内排队的 Run 也可能被冻结为 fresh。
所以不能说「上下文完全连续」。run.prompt 是 HTTP 请求内容经 .trim() 后的持久化值
(run.prompt === trim 后的 message.content),不是请求的逐字节副本。不拼历史。--continue(会 fork 原生会话状态)与 --dangerously-skip-permissions 一律禁止。started_at → completed_at,在跑时 started_at → now)。
稿子里所有时长都写成「本回合…」,不出现「整个 session 累计」这类表达。调用形态示意(非真实 argv) Run 创建时:有 native ID && 同 runner && 在 allowlist 内 ├── 是 → claude --print --resume <id> <prompt> 续上同一条原生会话 └── 否 → claude --print <prompt> 新开一条原生会话
用户 2026-08-16 定案:「跑完之后是绿点,点击进去查看后绿点消失,同理红点。」 已读之后活儿依然在,只是不再提示你——所以稿子里任何地方都不把这两个点说成「这条会话的状态」。
总控代码级确认(services/orchestrator/server.mjs):系统已有完整的未读骨架,不需要新造字段——
session.gm_notified_at + session.gm_unread_kind(写入见 gmLoopNotifyUser,1213–1230 行)、
session.gm_read_at、POST /sessions/:id/read 清零、
sessionView 据此派生 gm_unread 布尔(905 行)。
前端切会话即 fire-and-forget 调 /read,「点进去就消失」这半边已经现成。
当前唯一缺口:gmLoopNotifyUser 目前只被 GM 自主循环调用,
普通会话的 run 进入终态时不会点亮未读。这需要后端在 run 终态处理里补一次点亮,
已记入需求,不在设计范围内。在补上之前,稿子里画的绿点/红点对普通会话
还不会真的出现——这一点在此写明,不越权断言后端已支持。
转圈是另一回事:它只表示 active_run_id 指针非空,与已读未读无关。
注意指针为空不等于没有在途 Run(见上方 G1 分区的说明)。
取值顺序:active_run_id 指针非空就显转圈;否则检查未读;都没有则空槽。
| rev1 的写法 | 为什么不成立 | rev2 改成 |
|---|
rev4 修掉前 9 条;末尾 3 条(标 (rev5))是 rev4 交付时 Tech Lead 报告不在本工作树、只拿到前 9 条造成的漏网,rev5 逐条补掉。
| rev3 的写法 | 为什么不成立 | rev4 改成 |
|---|
其中第 5、6、9、12 条是我在 rev4/rev5 声明已改、实际只改了主路径的——
同一串字还留在场景说明、README 与 DESIGN 里。
本轮改完每条都做了全文件搜索验证,不再只看 index.html。
| rev6 的写法 | 为什么不成立 | rev7 改成 |
|---|
其中 #1 / #3 / #5 是同一个根因:rev7 补上了中性点,但旧的 「三种运行状态视觉」「四态」定义还留在 CSS 注释、图鉴文案、覆盖表和 DESIGN 里,新旧并存。 rev8 把槽位统一表述为五种条件,并在五个文件里清掉了全部旧说法。
| rev7 的写法 | 为什么不成立 | rev8 改成 |
|---|
本轮同时并入用户拍板的 R-2 → 方案 B:上一版把 H5 的全文入口指向「详情浮层」,
而那个浮层并不存在——触屏用户当时没有任何办法看到被截断的完整值。
现在改成长按弹出,且完整原值一律存在 data-full 里。
| rev8 的写法 | 为什么不成立 | rev9 改成 |
|---|
#6 / #7 / #8 不是措辞问题——它们让本轮的核心交付「长按看全文」实际是个空壳: 长按弹出来立刻被兼容 click 关掉、桌面 hover 因为隐藏时尺寸为 0 而根本不存在、 复制失败还报「已复制」。三条都已修。
| rev9b 的状态 | 为什么不成立 | rev10 改成 |
|---|
由 Tech Lead 提出、本轮记录在案。本稿不实现这些,但产品实现必须满足。
前两条是为了消掉 data-full 重复存储带来的漂移风险——
稿子里 62 处 data-full 与可见文本是两份人工维护的副本,
产品里不许这样做。
data-full,
禁止人工维护两份。" M path"、" D path"
两个用例,守住前导列位(那正是 .trim() 会吃掉的东西)。Path 取 session.project_root || "—",
不与 native_cwd 混用——两者是不同的东西。changed_files 为 null 只能写「未上报」;只有明确 [] 才能写「没有改动文件」。见 G4-1。tests_run 只能原样呈现真实上报,不得自行总结成「13/13 绿」。见 G4-2。waiting_for_approval,也没有同一 Run 的放行通道,普通消息只会新建另一条 Run。parent_run_id / child_run_ids,只逐条列出能由真实 dispatch marker 的 Run ID 对上 TaskView 的那几条。或者在某个 Agent 上开一条新的。
if (!session.native_session_id) return requested;const locked = session.native_session_runner_id;回归确认了问题:已绑会话换引擎后仍然复用旧记忆。我把回正逻辑
引擎锁已经扩到所有已绑原生会话。一条会话只认它第一次绑定的 runner:
native_session_runner_id,不再静默切换;| 场景 | 旧行为 | 新行为 |
|---|---|---|
| 已绑会话 + 异引擎 | 静默切换,记忆串 | 回正 + runner 消息 |
| 新会话 | 按请求 | 按请求(不变) |
换引擎 = 新记忆。这不是限制,是把已经发生的事实说清楚。
const locked = session.native_session_id ? session.native_session_runner_id : null; // 故意写很长一行,用来验证内滚而不是换行撑破卡片
services/orchestrator/server.mjs | 25 +++++++++++------ apps/web/index.html | 53 ++++++++++++++--------- scripts/cc-test-runner-default.mjs | 133 +++++++++++++++++++++ 3 files changed, 194 insertions(+), 17 deletions(-)
没跑成 停在 npm ci,退出码 1。原始输出在过程里。
密度按图调过了:过程行行高从 1.6 收到 1.55,步骤之间去掉了额外的 4px。
部署完了,gallery 里能看到那张卡。
这条会话绑的是 design-agent。之后每一句:存在有效的同-runner binding 时续接原来那条原生会话,否则新开一条。
服务返回 502(网关错误),根因尚未确认。客户端已自动重试 3 次。你刚才打的字还在输入框里,没丢。
已经停掉自动重试了——再自动重试下去只会一直空转。 读取超时不代表执行了删除;当前无法确认那份原生记录是否可读。
native_session_id 与 native_session_runner_id 同时存在派生,锁住的引擎取后者(不是 Agent 的 default_runner)。没有 runner_locked 这个字段。
。解析顺序:binding → Session 默认 → Agent 默认 → 全局默认。
native_session_id 后产生绑定;adopt-native 创建时直接带绑定。
最后一次心跳 12 秒前,在在线窗口内。
会话会从列表隐藏。消息与执行记录留作审计,目前没有恢复入口。