8.9 KiB
8.9 KiB
失败插入卡片重试与删除:发现记录
用户需求
- 用户希望“插入失败”的卡片增加“重试”和“删除”按钮。
- 截图中的失败项位于用户消息卡片内,包含图片预览、文件名、尺寸/状态信息和红色“插入失败”状态。
视觉观察
- 当前失败状态只有红点和“插入失败”文本,没有恢复或清理入口。
- 操作应靠近失败状态区域,保持卡片宽度和当前主题视觉,不应抢占消息正文。
工作区约束
- 当前分支为
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后才提交,避免失败删除后的缓存幽灵和索引漂移。