git:20261104.a5dfeab to git:20261104.16db229
5 added, 4 removed. Audit A to A.
# 诊断交互层:插件点地图与取舍
这份文件记录「为什么是这几个面」与「还有哪些点可以考虑」。实现清单只看
[README.md](README.md);这里放判断依据。
## 读者与场景
- 读者:现场工程师与技术支持,白天连续处理多单;一只眼看日志,一只眼看着对话。
- 任务循环:收集材料 → 判断 → 给结论 → 回报 / 沉淀。
- 一次中断的代价:切到别的界面、找到那一单、再切回来。三分钟以上的诊断会被切走十几次。
## 六条交互原则
1. **不打断**:读状态与常见写入都在对话内完成。
2. **渐进披露**:默认一行;展开才给细节;偏好在下次打开时保留。
3. **下一步只有一个**:主按钮永远指向当前最该做的事,其余进「更多操作」。
4. **写入可核对**:写前给出对象与字段,写后给结果;并发冲突给原因与刷新入口。
5. **一处定义、多处一致**:三个面共用同一份数据、同一套动作措辞与同一份偏好。
6. **降级可读**:没数据、没权限、写不了时给出原因与下一步,不拿别处的数据顶上。
## 旅程 × 插件点
「已做」是本包已实现的面;「评估过」是看过扩展点后没做的,附理由。
| 时刻 | 摩擦 | 插件点 | 状态 |
|---|---|---|---|
| 开单 | 不记得从哪开始 | `conversation.hero.options`(新会话行加「诊断」入口) | 评估过:`/diagnose` 已在空会话首屏够用,加一个按钮收益低 |
| 诊断中 | 不知道到哪步、在等什么 | `conversation.input.dock` 状态条 | 已做 |
| 诊断中 | 信息块挡消息 | 状态条收起 + 偏好持久化 | 已做 |
| 诊断中 | 要回报 / 收尾却要开面板 | 状态条「更多操作」+ 总览写入 | 已做(回报轴仅生成指令,见 README) |
| 诊断中 | 回报结果该写什么 | 回报表单(结果 / 来源 / 备注 + 指令预览) | 已做 |
| 诊断中 | 想查 case 上游有没有新结论 | 「查 case 上游状态」生成指令 | 已做:菜单先显示 gh 就绪状态(与 doctor 同一件事),prompt 让 agent 走 `verify_upstream_outcomes.py` 幂等落账(同一结局不重复累加) |
| 诊断中 | 想把结论发给同事 | 一键复制摘要(markdown 纯文本) | 已做 |
| 诊断中 | 想跳到该诊断的会话 | `sessions` / `uiWorkspace` 的打开会话方法 | 已做,仅轨迹写了 `harness_session` 时出现 |
| 回合收尾 | 这回合产出了什么、要不要沉淀 | `conversation.chat.turnTail` 每回合一行 | 评估过:需要「本回合」的 trace 增量;当前投影只有整单状态,做出来会和状态条重复 |
| 单条回复 | 复制这一条结论 / 沉淀这条 | `conversation.chat.assistant-actions`(按 `messageId`) | 评估过:要按消息取正文,`ownerProps` 只给 messageId,成本高于收益 |
| 全库巡检 | 想看多单但不想开整页 | `shell.overlay` 诊断总览 + 键盘 | 已做 |
| 全库巡检 | 输入区就在手边 | `conversation.input.right` 工具条入口(带待回报角标) | 已做 |
| 全库巡检 | 顶栏也要一个入口 | `conversation.session.header.utilities` 小胶囊 | 评估过:与工具条入口重复;再放一个会增加视觉噪声 |
| 沉淀 | 不知道该写什么 | 「沉淀为 case」生成指令、复制升格指令 | 已做 |
| 沉淀 | trace 里没标候选的单找不到沉淀入口 | 入口只看「还没沉淀、有内容」,无候选时按 trace 起草 | 已做 |
| 沉淀 | 多单都要沉淀,一单一单点太慢 | 诊断总览「可沉淀」筛选下的批次条(范围 = 当前筛选,生成一条批量指令) | 已做 |
| 沉淀 | 改沉淀状态 | `trace_transition.py --field sedimented` | 已做 |
| 收尾 | 不知道还差什么 | 总览「收尾清单」(报告 / 回报 / 沉淀 / 交接) | 已做 |
| 收尾 | 不敢点写状态 | 写入历史 + 撤销上次收尾 | 已做 |
| 诊断中 | 上游有没有新结论、上次查证何时 | 「上游查证」块(锚点 / last_verified / 依据 / 就绪) | 已做 |
| 诊断中 | 证据与沉淀线索散在 trace 里 | 「证据与沉淀」块(路径复制 / 候选指令 / 报告内看) | 已做 |
| 诊断中 | 想快速打开总览 | `⌘⇧D` / `Ctrl+Shift+D`,提示文案按平台显示(macOS 用 ⌘,其他平台用 Ctrl) | 已做 |
| 诊断中 | 在等什么、还缺什么材料 | 「在等材料」清单 + 复制 / 续接指令 | 已做 |
| 续接 | 事件太多、要逐条回看 | 「步骤时间线」按需加载 | 已做 |
| 收尾 | 要把结论发给别人 | 「复制为 issue 文本」纯文本模板 | 已做 |
| 导航 | 单多、找一单 | 搜索框 + `⌘K` 命令面板 | 已做 |
| 通知 | 切走后欠回报看不见 | 页面标题前缀计数 | 已做 |
| 工作区 | 别的项目也出现状态条 | cwd 不在 ascend-sleuth 就隐藏(含 cwd 缺失) | 已做 |
| 会话 | 续接后不知道在哪、切不回去 | 原始 / 续接会话归属 + 切到会话 + 缺会话号补写 | 已做 |
| 配置 | 想调轮询 / 默认折叠 | `settings.section` 或插件 `Config` | 评估过:当前只有两个常量,先不加设置页 |
## 三个面为什么这样分工
- **状态条**:常驻、离输入框最近,负责「现在」与「下一步」。默认收起成一个居中的窄胶囊,点开才是完整两行;收起选择记住。
- **工具条入口**:状态条收起时仍在手边;角标把「有几单欠回报」变成环境信息,不打断。
- **总览**:全库横向比较与批量动作的主场。浮层而不是新页面,关掉就回到对话;键盘操作减少鼠标往返。
没有做第四个面(顶栏胶囊):同一个入口放三处,读者要记「哪里能点」;两处已经覆盖
「在输入区」与「不在输入区」。
## 展开态怎么收起
- 原先只有右上角一个箭头能收,目标小、离视线远;展开容易、收起要瞄准角落,操作成本不匹配。
- 点状态条外收起是浮层的通用约定,点击目标覆盖状态条以外的整片区域。点进输入框不算:收起会让输入区上移,
正在定位光标的点击会被带偏。
- 不想被收起的人用「展开后常驻」:按钮在右上角,选择与其他偏好一起存在本地。
- 不做静置自动收起:诊断进行中有人靠它盯状态,自动收起比多点一下更糟。
- 判断「这次点击是不是在状态条里」用**手势起点**(`mousedown` 时记)与 `composedPath()`,不看点击瞬间的
`contains(target)`:菜单项、选择器行点击会把自己从 DOM 换掉,到 `document` 时 target 已 detached,
只看 `contains` 会把这类点击当成「点外部」收起整条,刚展开的表单跟着消失。
- 静置淡出只改透明度。`prefers-reduced-motion: reduce` 只把过渡去掉(2.8 秒后瞬间变淡),不取消变淡:
Windows 关闭「动画效果」时 Chromium 报 reduce,若连变淡一起停掉,读者看到的是「这个效果没了」。
透明度变化不属于位移或缩放类动效,保留它与无障碍设置的意图不冲突。
- 「更多操作」与选择器浮层优先向上展开;上方可用空间不足时向下并限高。量的是状态条上方可用的视口高度,
以及最近一个会裁内容(`overflow` 为 auto / scroll / hidden)的祖先顶边,避免浮层被裁掉或被顶栏盖住。
## 第二行放不下时收哪一段
- 平台、框架来自 trace 的自由文本,`detected_platform` 能写几十字;性质是中断 / 精度 / 性能三类枚举。
- 放不下时有换行、溢出、截断三种处理。
- 换行让状态条高度随数据变,输入区跟着上移。
- 溢出把文字顶出状态条:实测 760px 宽的状态条里,最后一段身份标签被顶到条外;窄窗口下还越过窗口右边界。
- 选截断:平台 / 框架 / 性质与「在等什么」按省略号收。两段同时按比例收,没有谁先收。
- 身份标签保留完整宽度:它是离散的状态标签,逐段裁会变成半截标签。极端宽度下由第二行容器裁断,同样没有省略号。
- 截断不丢信息:两段都带 `title`,鼠标悬停看全文。完整值在诊断总览里一步可见,键盘能打开总览。
- 不在 JS 里按字数裁剪:字数与像素不等价,中文、ASCII、字号都会让同一字数占不同宽度。
裁剪规则放 CSS,换主题或改字号时自动跟着走。
## 收尾表单只对进行中的单给
`trace_transition.py` 对终态单把「`--to resolved`」解释成 correct-ending:界面若照常给「已定位」,
点一下会成功写入一条 `resolved → resolved` 转移,重写收尾方式,并用表单默认值覆盖 severity / fix_emitted。
这两个字段分别是「等上游」的判据与指标来源,写错不报错。所以收尾三项只对进行中的单出现。
终态单另给两个明确入口,动作与目标都来自 `_trace_state.py` 的 `actions`:`correct-ending` 按目标渲染
「改收尾方式:已定位 / 已转交 / 不再跟进」(换状态时脚本先写一条重新打开再收尾),`backfill-ending`
只补当前状态的收尾方式。界面不重算状态机;投影没给 `actions` 时不显示这两项。
收尾表单另补两处:`summary` 是 close 动作的 fillable,缺它脚本直接拒(表单预填 trace 现值,可改);
`fixEmitted` 初值取 trace 的 `fix_emitted`,没有记录才回落 true,避免把 false 写成 true。
所有下拉框的选项都优先取投影里的词表(`trace-status.yaml` 是唯一来源),没有该段时才用常量兜底。
## 数据新鲜度与「选中对单」
- 首屏成功后轮询失败不再静默:保留旧数据并标「数据可能过期」,折叠态胶囊、展开态标签与总览顶部都会提示;
下次成功读取时清掉标记。取数从没成功过(缺 Python、坏检出)时,总览给原因与重试,不停在骨架屏。
- 总览选中项按 `sessionId` 记住。10 秒刷新会让列表按 `updatedAt` 重排,只按下标存的话右侧详情与动作条
会静默换到另一条单上,读者按着 A 单的按钮改的是 B 单。
- 表单打开时冻结内容指纹(CAS 令牌),菜单开着期间外部改了 trace 会报冲突并提示刷新;用提交那一刻的
新指纹会让屏幕上的旧值直接覆盖外部改动。
## 沉淀入口按状态判,不按候选判
`needSed`(trace 里有 case 候选)是 agent 的建议,不是入口的前提:没有候选的单调不出「沉淀为 case」,
读者就只剩「沉淀状态…」这个改状态的入口,真正要起草时无处可点。所以菜单、收尾清单与命令面板的沉淀入口
只看三件事:还没沉淀、这单不是空骨架、命中的 case 不在库里。候选只在文案上区分(无候选时注明按 trace 起草),
以及「可沉淀」筛选与批量条里继续表示「agent 已标候选」。
## 沉淀视图(v1)的四条判断
1. **队列放总览,不新开页面。** 诊断与沉淀分别回答「这一单」与「这一批」,但入口多一个就要多记一处;
总览里加「诊断 / 沉淀」切换,键盘与动作模式复用。
2. **合入状态是「已提 PR」上的属性,不是第六步。** 队列只有五步;`state` 带 `checked_at` 与
`source`,查不到写「未确认」,不由分支是否存在推断。多一个阶段会让状态机与文案一起变复杂。
3. **提 PR 指令默认短。** 流程在 `/knowledge-groom` 里,界面只补 agent 看不到的批次、决定、
落点与边界;命令细节折叠,避免出现第二份流程说明。
4. **远端目标只问一次,只列事实。** 目标存在共享 inbox 的 `.groom/pr-state.json`;选择器列 host、
平台、HEAD 分支、适配器/token 状态,不预设哪个是团队仓、哪个是上游。场景没收敛前,默认留空。
扩展面:PR 平台是 `scripts/platforms/` 下的适配器;界面只读 `{state, checked_at, source}` 与平台显示名,
新增平台不改界面与核心脚本。doctor 从适配器声明里查 token/命令,不写平台特判,也不代装。
## 总览面板的排版与控件
- 字号取八档、字重取四档,值写在 `.scp-pal` 的 `--scp-fs-*` / `--scp-fw-*` 上;命令面板、表单控件与浮层提示同样从这两组 token 取值;状态条共用的 `.scp-menu-note` / `.scp-case` 仍是固定值。
- 诊断视图的筛选从标题栏移到左列搜索框下方,与沉淀视图一致。两个视图都在这处筛选,切换视图后位置不变;标题栏只留标题、视图切换与关闭。
- - 详情按「识别行 → 问题背景 → 区块」排列,不再把案例首句放大成标题:真机 trace 的 `summary` 常有一两百字,
- 16.5px / 650 会把首句渲染成七八行粗体(离线夹具的 `summary` 是一句话,看不出来)。现在识别行给出「这是哪一单」,
- 摘要按正文 13px / 1.75 排四行,超出时给「展开全文」,同一段不在底部再重复一次。
+ - 详情按「识别行 → 问题背景 → 区块」排列,不再把案例首句放大成标题。真机 trace 的 `summary` 常见一两百到七八百字;
+ 16.5px / 650 会把首句渲染成七八行粗体(离线夹具的 `summary` 是一句话,看不出来)。识别行一行给出「这是哪一单、做到哪一步」。
+ - 问题背景按正文 13px / 1.75 排四行,超出时给「展开全文」,同一段不在底部重复。展开入口先读实际溢出
+ (`scrollHeight` 与 `clientHeight` 比较);替身渲染拿不到布局尺寸时,退回 110 字近似。
- 摘要里的路径、错误码、函数调用与 `XxxError` 走等宽字加浅底(`inlineTokens`:只按形状识别,不改文本);
展开状态按单号记,切到别单回到收起。
- 区块标题从 12px / 600 深色降到 11.5px / 600 次要色:它只负责分节,不需要比正文更显眼;卡片边框降到 72% 透明度,
- 圆角 12 收成 10;「在等」从色块标题加正文两行收成一条窄行。
+ 圆角 12 收成 10;字段标签用 11px 次要色;「在等」从色块标题加正文两行收成一条窄行。
- 底部动作收成一个主操作、一个复制摘要和「更多操作」,常驻在详情底部。其余动作收进菜单,窄宽度下不再堆叠按钮。
- 沉淀进度轨的类名从 `.scp-step` 改为 `.scp-sed-step`(`-dot` / `-label` / `-note` 一并改名)。原类名与诊断详情里的「第 N/6 步」共用,进度轨画连线的 `::after` 因此落到了那行文字上,显示成一条盖住文字的灰线。
## 写入安全
- 能力位来自当前会话沙箱档位;只读档位下写入项禁用并显示原因。
- 每次写入带内容指纹;冲突返回 `E_STATE_CHANGED`,界面提示刷新。
- 破坏性收尾(不再跟进)在菜单里单独一组;已定位与转交需要表单字段,不靠一次点击。
- 回报轴当前没有脚本写入口,界面只生成指令,不写 `feedback`。
## 验证边界
渲染冒烟与 Host 冒烟覆盖:状态分支、折叠偏好、点外部 / Esc 收起与常驻偏好、总览键盘、沉淀队列 / 指令 / 目标选择、写入参数、
能力位、CAS、真实脚本写入(在系统临时目录的 trace 副本上做,仓库不被改)。
未覆盖:真实页面排版、鼠标点击、深浅色主题下的观感。这三项只能人工确认。