feat: overhaul task board and cross-conversation workflows

This commit is contained in:
shiyue
2026-08-12 09:41:36 +08:00
parent e7d28935ef
commit 86231f0d97
55 changed files with 11628 additions and 280 deletions

View File

@@ -0,0 +1,103 @@
# 失败插入卡片重试与删除:发现记录
## 用户需求
- 用户希望“插入失败”的卡片增加“重试”和“删除”按钮。
- 截图中的失败项位于用户消息卡片内,包含图片预览、文件名、尺寸/状态信息和红色“插入失败”状态。
## 视觉观察
- 当前失败状态只有红点和“插入失败”文本,没有恢复或清理入口。
- 操作应靠近失败状态区域,保持卡片宽度和当前主题视觉,不应抢占消息正文。
## 工作区约束
- 当前分支为 `main`,已有另一任务留下的未提交改动。
- 已确认 `.trellis/.current-task` 指向 `08-11-task-board`,本任务不会覆盖该指针。
- 与本任务可能重叠的已修改文件包括 `public/app.js`、`public/index.html`、`scripts/regression.js` 和 `server.js`;后续必须做局部差异审查。
## 待确认的代码事实
- 失败状态的数据结构、稳定标识和持久化位置。
- 原始插入请求的入口、请求参数及重试时可复用的数据。
- 删除应落在草稿状态、消息附件状态还是服务端持久化层。
- 现有测试中与运行时图片插入相关的覆盖位置。
## 初步代码定位
- `codebase-memory-mcp` 项目 `home-cc-web` 索引状态为 `ready`(5737 节点、12312 边)。
- “插入失败”不是普通上传失败文案,而是 Codex App 运行中消息插入(steer)状态:前端入口命中 `codexAppSteerStatusLabel` 与相邻的 `setCodexAppSteerStatusElement`。
- 服务端入口命中 `handleCodexAppSteerMessage`:`turn/steer` 成功发送 `inserted`,失败发送 `failed`,且消息会先以 `persistedUserMessage` 写入会话。
- 下一步需要确认状态事件携带的稳定标识、前端如何找到对应用户消息,以及重试是否可以复用现有 `handleCodexAppSteerMessage` 消息形状。
## 已确认的数据与渲染契约
- 前端 `updateCodexAppSteerMessage(clientMessageId, status, message)` 使用 `userMessageIndex` 或消息元素的 `data-message-id` 精确定位卡片,稳定标识已经存在,无需按文件名匹配。
- `setCodexAppSteerStatusElement` 目前只切换 `pending / inserted / failed` 三种 CSS class,并把状态区域写成纯文本;操作按钮应由这里或紧邻的单一渲染函数统一维护,避免重复绑定。
- 服务端回归 `runCodexAppRuntimeImageSteerRegression` 已覆盖 `clientMessageId`、失败状态和成功插入;失败的“附件不可用”消息明确不会持久化,说明截图里的失败卡片至少有一类是客户端临时项。
- 现有成功分支会持久化一条带附件的用户消息,并最终发送 `inserted`;重试实现必须避免把失败项或成功项重复写入会话。
## 调用链与主要风险
- `handleServerMessage` 仅在 `codex_app_steer_status` 分支调用 `updateCodexAppSteerMessage`;当前状态事件已是最小可扩展协议面。
- `handleCodexAppSteerMessage` 在真正调用 `turn/steer` **之前** 把用户消息写入会话。若 `turn/steer` 异步失败,该持久化消息当前不会回滚,因此“重试”若直接重发原消息会造成重复持久化。
- 失败分为两类:附件解析/空消息等同步前置失败(未持久化)与准备/`turn/steer` 异步失败(可能已持久化)。删除和重试必须能区分或统一修正这两类,否则刷新后的行为会不一致。
- 下一步要确认本地消息对象是否保存 `clientMessageId`、服务端是否已有单条消息删除/替换协议,以及附件引用在失败后是否仍可用。
## 客户端入口补充
- `sendMessage` 为运行中 Codex App 插入创建本地 `messageId`,用该值注册 `userMessageIndex`,并作为 WebSocket 消息的 `clientMessageId` 发送;因此失败卡片已有天然的单项操作键。
- 当前前后端没有通用的“删除单条消息”协议;仅有排队消息、提示卡等其他局部删除实现,不能直接用于持久化的 steer 用户消息。
- 需要在服务端把 `clientMessageId` 与持久化消息关联起来,或在 steer 失败时回滚该消息,才能让删除和重试同时满足“不重复”和“刷新一致”。
## 可行实现方向
- 本地卡片由 `sendMessage` 创建,包含完整 `text` 与克隆后的 `attachments`;扩展 `userMessageIndex` 即可保存重试载荷,不需要从 DOM 反解析。
- `createMsgElement` 是失败状态和附件预览的共同装配点,`setCodexAppSteerStatusElement` 是状态区域的唯一更新点,适合统一创建/移除按钮并维护 `disabled` / `aria-busy`。
- 推荐服务端在真实 `turn/steer` 最终失败时按对象引用回滚刚持久化的用户消息。这样所有失败卡片都保持客户端临时态,删除无需新增持久化删除协议,重试也不会制造历史重复项。
- 重试仍需校验当前 Codex App turn 是否在运行;若运行已结束,应给出明确失败状态,不能让卡片永久停在 `pending`。
## 本地源码校验
- 当前样式集中在 `public/style.css` 的 `.codex-steer-*` 区域,失败卡片已有红色边框/背景和 11px 状态行;新操作可在同一区域追加,避免影响其他消息卡片。
- 项目没有 DOM 测试依赖,仅有 `node scripts/regression.js`;前端交互测试需要使用轻量假 DOM/纯函数抽取,或在现有回归中增加协议和静态契约断言。
- 本任务开始时的重叠文件基线:`public/app.js` 已有 +155 行、`server.js` +532/-7 行、`scripts/regression.js` +73/-11 行、`public/index.html` +14 行,均来自既有“任务看板”工作,后续差异必须按目标函数/样式区段核对。
- `public/style.css` 当前没有未提交改动,是本任务新增样式的低冲突落点。
## 测试与冲突边界
- 现有 `runCodexAppStaleRunningRegression` 已有一个稳定的 `expectedTurnId` 不匹配失败夹具,适合新增“失败后持久化消息已回滚”的服务端断言。
- 现有成功恢复分支已断言用户消息只持久化一次,可继续保护“重试/恢复不重复”。
- 既有未提交 diff 的 hunk 不覆盖 `setCodexAppSteerStatusElement`、`sendMessage` 或 `handleCodexAppSteerMessage` 主体;可以对这些区段做局部补丁,但最终仍需逐 hunk 复核。
- 仓库已有未跟踪的任务看板 MiniDOM 单测脚本,但它属于另一任务;本功能不能修改或依赖该未提交测试文件。若需要 DOM 单测,应建立独立、最小的测试入口。
## 失败夹具与规范结论
- mock app-server 的 mismatch 夹具会保留一个不同的活动 turn,因此每次 `turn/steer` 都稳定返回 `expectedTurnId does not match active turn`;可用于重复验证失败与回滚,但不能作为成功重试夹具。
- 前端 Trellis 规范除“静态资源版本握手”外大部分仍是模板占位,没有额外组件框架约束;本任务应遵循仓库现有原生 DOM + CSS 组织方式。
- 回归脚本已有按 target 执行的分支,可为本功能提供小范围测试入口,避免每次执行完整回归超过 60 秒。
## 资源
- 用户截图:`sessions/_attachments/8da11ed4-5989-4d30-a12a-f27522138799.png`
- Trellis 任务:`.trellis/tasks/08-11-failed-insert-card-actions/`
## 最终实现状态
- 前端新增 `codexAppSteerRecords`,保存失败项的原文本、附件、会话、模式和 agent;重试沿用同一 `clientMessageId`,并通过 `status + inFlight` 阻止重复发送。
- failed 状态统一渲染“重试”“删除”;pending/inserted 移除操作,卡片使用 `aria-busy`,状态行使用 `role=status` 与 `aria-live=polite`。
- 本地运行中插入不再提前增加 `currentSessionMessageCount`,只有收到 `inserted` 才提交消息索引;失败/删除不会制造普通路径的索引漂移。
- 服务端为临时 steer 用户消息写入稳定 `id`,真正的 `turn/steer` 最终失败时先按 id 从最新会话回滚,再发 failed 状态;stale recovery 成功路径保持一次持久化。
- 前端 MiniDOM 单测覆盖失败操作、双击保护、载荷复用、再次失败恢复、删除单项、断线/跨会话/turn 结束保护与成功索引提交。
## 验证结果
- `node scripts/failed-insert-card-unit.js`:通过。
- `node scripts/regression.js --target runtime-image-send`:通过。
- `node --check public/app.js`、`server.js`、`scripts/regression.js`:通过。
- `node scripts/regression.js`:因首次定向命令参数错误而实际执行,完整回归通过(约 46 秒)。
## 独立审查修正
- 检查代理发现 `options.emitUserMessage` 的程序化 `session_message` 虽会显示失败操作,但缺少原始重试载荷;现已由服务端附加临时 steer 元数据,前端据此建立同样的重试记录。
- 主线程进一步将这类 `session_message` 在结果未定时保持为临时项:不提前写入消息缓存、不提前占用 `currentSessionMessageCount`,收到 `inserted` 后才提交,避免失败删除后的缓存幽灵和索引漂移。

View File

@@ -0,0 +1,63 @@
# 失败插入卡片重试与删除:进度日志
## 2026-08-11
### 初始化与定位
- **状态:** `complete`
- 已扫描可用技能并完整读取 `planning-with-files`、`todo-list-csv` 主说明。
- 已运行 planning session catchup,未发现需要恢复的未同步上下文。
- 已读取 Trellis 会话上下文,确认开发者为 `shiyue`,现有活动任务为“独立任务看板”。
- 已创建独立 Trellis 任务 `08-11-failed-insert-card-actions`,但未切换全局当前任务指针。
- 已记录截图中的失败卡片结构和现有工作区重叠风险。
- 独立计划审查已通过;已吸收稳定标识、忙碌状态和定向测试优先等建议。
- 已创建 Trellis PRD,并为实施/检查配置前端规范上下文。
- 已确认 codebase-memory 索引可用,并初步定位到 Codex App `turn/steer` 的前后端状态链路。
- 已完成调用链、失败持久化、临时消息索引和测试夹具分析,并写入 Trellis 研究文档。
### 交互契约与回归测试
- **状态:** `complete`
- 已明确失败项使用 `clientMessageId` 定位、失败持久化回滚、临时消息延迟提交索引、重试并发保护和客户端删除语义。
- 下一步先补回归测试,再写最小实现。
### 实现、样式与定向验证
- **状态:** `complete`
- 已实现失败卡片的重试、删除、防重复点击、可访问状态和消息索引延迟提交。
- 已实现服务端最终 steer 失败的精确持久化回滚及错误关联 id。
- 已新增独立 MiniDOM 前端单测并扩展 runtime image mismatch 回滚断言。
### 差异审查与完整回归
- **状态:** `complete`
- 已完成主线程首轮目标函数审查;下一步由独立检查代理按 PRD、研究契约和现有脏工作区边界复核。
- 独立检查已修复程序化 `session_message` 缺少重试载荷的问题;主线程补齐其临时缓存/索引语义,并扩展成功提交断言。
- 修正后前端单测、`runtime-image-send` 定向集成和 `node --check public/app.js` 再次通过。
- `git diff --check` 通过;目标修改保持在既定函数、CSS 区段和测试范围,未回退任务看板或消息回传的并行改动。
### 清理与交付
- **状态:** `complete`
- 已检查运行对话:除当前对话外仍有“设计任务看板状态机制”和“统一 ccweb 消息回传接口”处于 `running`,按运维规则暂不重启 `ccweb`。
- 规范沉淀判断:本次结论属于 Codex App steer 的任务级协议细节,已完整记录在 Trellis 研究文档;没有形成需要写入通用前端规范的新约定,因此不修改 `.trellis/spec/`。
- 已将 Trellis 任务元数据标记为完成,并按 `todo-list-csv` 工作流删除本任务临时 CSV;持久化计划与研究文档保留用于审计。
## 测试结果
| 测试 | 预期 | 实际 | 状态 |
|---|---|---|---|
| 前端失败卡片单测 | 重试/删除/并发/无障碍/边界保护通过 | 全部通过 | ✓ |
| Runtime image send 定向集成 | 成功、stale recovery、mismatch 回滚通过 | 全部通过 | ✓ |
| 完整回归 | 项目全部回归通过 | 全部通过(误执行但有效) | ✓ |
| JavaScript 语法检查 | 三个目标脚本语法有效 | 全部通过 | ✓ |
## 错误日志
| 时间 | 错误 | 尝试 | 处理 |
|---|---|---:|---|
| 2026-08-11 23:00 +08:00 | 新建 Trellis 任务后读取到缺失的 `prd.md` | 1 | 该脚本只生成任务元数据与上下文模板,已按提示显式创建 PRD |
| 2026-08-11 23:20 +08:00 | `codebase-memory-mcp` transport closed | 1 | 已保留先前 MCP 命中结果,剩余小范围源码用本地检索降级校验 |
| 2026-08-11 23:30 +08:00 | 前端单测跨 realm 深比较失败 | 1 | 将沙箱载荷规范化为普通 JSON 对象后再断言,并清理重复的 `inFlight` 赋值 |
| 2026-08-11 23:35 +08:00 | 定向回归漏写 `--target` | 1 | 命令实际跑成完整回归且通过;已确认正确 CLI,避免再次执行完整套件 |
| 2026-08-11 23:38 +08:00 | 计划同步补丁使用了不存在的标题锚点 | 1 | 补丁未生效;读取实际文件后以现有区段重新应用 |

View File

@@ -0,0 +1,55 @@
# 失败插入卡片重试与删除:任务计划
## 目标
为图片/附件“插入失败”卡片增加可用的“重试”和“删除”操作:重试沿用原附件并正确更新卡片状态,删除只移除对应失败项;同时保证重复点击、再次失败、会话恢复和现有发送流程不被破坏。
## 范围
- 失败卡片的前端渲染、事件处理和状态同步。
- 必要的后端/API 复用或补充。
- 定向回归测试、样式和无障碍状态。
- 不重构无关消息链路,不覆盖当前工作区中的“独立任务看板”改动。
## 阶段
1. **初始化任务上下文并定位失败附件链路** — `complete`
2. **明确重试与删除交互契约并补充回归测试** — `complete`
3. **实现失败卡片重试与删除行为** — `complete`
4. **完善操作按钮样式与无障碍状态** — `complete`
5. **运行定向测试并修复问题** — `complete`
6. **执行差异审查与完整回归** — `complete`
7. **清理临时跟踪文件并交付** — `complete`
## 验收条件
- 仅失败卡片展示“重试”和“删除”。
- 点击“重试”后复用原附件,进入明确的处理中状态;成功后不再显示失败操作,失败后恢复可重试状态并保留可理解的错误提示。
- 重试过程中禁用重复操作,避免并发插入或重复提交。
- 点击“删除”只移除目标失败卡片,不影响消息文本、其他附件或其他消息。
- 刷新/恢复会话后的失败卡片仍可执行两项操作,或按现有持久化边界保持一致行为。
- 定向测试与项目相关回归通过;无新增控制台错误。
## 关键决策
| 决策 | 原因 |
|---|---|
| 使用独立 `.planning` 与 Trellis 任务目录,不切换 `.trellis/.current-task` | 当前工作区另有“独立任务看板”任务正在运行,避免跨会话状态覆盖 |
| 先追踪现有插入请求和失败状态,再决定是否新增 API | 优先复用既有链路,减少协议与持久化风险 |
| 以失败项标识作为重试/删除目标,不按文件名匹配 | 同名附件可能同时存在,必须精确操作单项 |
## 风险与约束
- `public/app.js`、`server.js` 等文件已有未提交改动;补丁必须保持局部并在最终差异中区分本任务内容。
- 删除属于界面内失败项清理,不删除用户磁盘上的原始文件或服务器无关资源。
- 所有自动化测试单次运行控制在 60 秒内。
## 错误记录
| 错误 | 尝试 | 处理 |
|---|---:|---|
| Trellis 新任务默认未生成 `prd.md` | 1 | 按工作流要求显式创建 PRD,并保留已生成的上下文清单与任务元数据 |
| `codebase-memory-mcp` 补查 `send` / `clearUserMessageIndex` 时 transport closed | 1 | 核心链路已通过 MCP 确认;剩余局部实现按约定降级为 `rg/sed` 校验,不重复相同失败调用 |
| 前端单测跨 `vm` 上下文对象被 `deepStrictEqual` 判为原型不一致 | 1 | 对实际发送载荷先做 JSON 规范化,再比较纯数据结构;功能载荷本身完全一致 |
| 回归命令漏写 `--target`,误执行完整回归 | 1 | 完整回归已通过;查明 CLI 只解析 `--target` 或环境变量,后续仅补跑未覆盖的专项目标 |
| 计划同步补丁引用了研究文档中的错误标题 | 1 | 补丁未生效;读取实际计划文件后改用现有“资源”区段作为追加锚点 |