触发与范围
桌面聊天的结构化回答提交后,异步投递结果尚未返回,ConversationApproval 就删除当前 request 的保存答案。若投递失败、请求仍等待输入,当前组件暂时保留 React 内存中的答案;但刷新或重挂载后已经没有可恢复的保存内容。
按 P2 条件性答案丢失追踪。2026-09-30 只执行原组件、原 store 和 Shell 原函数的隔离合成验证,没有真实 UI 提交、服务故障注入、工具/Provider 或权限事故,不报告 P0。范围仅为 desktop-owned 聊天用户输入的持久化与确认/恢复合同;现有共享 Web 文件用于定位,不要求本轮修改 Web 或后端。
原逻辑验证与保护对照
fixture 原样转译 ConversationApproval 和 userInputDraftStore;从 Shell 的 TypeScript AST 提取 execute、handleUserInputResolve 的原正文,未改写函数。React test renderer 中的问题展示子组件为 inert leaf,仅暴露原父组件的答案 props 和 callbacks;不能据此声称视觉/AX 或真实子组件点击已复现。
输入为一个同 thread/request 的必填自由文本问题及已保存的合成答案。存储是全成功内存 StorageLike,sender 只记录参数并返回 deferred Promise;没有接入真实 profile、localStorage、网络或命令 API。
| 合成条件 |
已核验结果 |
| 提交,deferred 尚未返回 |
原 Shell 已记录当前 request 的提交意图,保存答案立即变为 null |
| 上述 deferred 失败 |
原 execute 显示错误且不 refresh;当前组件仍有答案,同 pending request 重挂载后答案为空 |
| 取消,deferred 失败 |
同样在结果前清保存内容;按同一清理路径记录,不另拆 Issue |
| 提交成功对照 |
原成功提示、refresh 与最终清理工作;此 mock 不代替后端 request 消费验证 |
| busy / 必填答案为空 |
submit guard 不投递;busy 的已保存答案保留 |
| 有保存答案但未提交,直接重挂载 |
完整恢复答案 |
共六个条件。完整原函数、输入、存储操作、组件状态和输出仅保存在本地 ignored artifact。测试中的 pending view 在失败后保留原请求,符合原 execute 失败分支不刷新该 view 的行为;没有真实后端或 Provider 请求。
根因与影响边界
父组件的 submit、handleCancel 调用 void onUserInputResolve 后紧接 clearUserInputDraft,没有等待确认结果。Shell handler 又用 void execute 执行实际异步 resolve;失败由 execute 捕获,清理调用无法获知失败。保存状态因此早于确认结果被删除。
实际影响限定于恢复:已挂载组件仍可让用户复制或重试内存中的答案,不能声称点击失败时立即丢掉所有输入。若该组件随后卸载/刷新,同一请求无法从该 store 恢复。Shell 源码仍保留错 request 校验;busy 与必填为空的 submit 对照通过。本条没有错误目标提交或授权突破证据。
桌面优化与验收
- 让 desktop 输入 resolve 返回明确的确认结果;提交失败或结果未知时保留当前 thread/request 的保存答案,成功消费后才清理。
- 清理绑定到此次提交的 request 和答案版本,避免迟到成功清掉随后编辑的内容。取消应说明是已确认取消请求,还是用户独立选择丢弃本地答案,不能混淆两种结果。
- 重跑相同原逻辑 fixture:deferred 未返回或失败时,重挂载仍完整恢复;成功后按确认范围清理。busy、必填为空、错 request 和未提交恢复保护继续成立。
- 在独立可弃置桌面测试环境补 UI 失败/重试/刷新验收,sender 先接合成 recorder,不连接真实工具或供应商;未经此结果不宣称端到端事故或修复完成。
补充同根的忙碌取消路径:QuestionCard 顶部关闭按钮在 status=submitting 时仍可触发原 handleCancel;该函数没有 submit 的 busy guard,调用 onUserInputResolve 后立即清理本地答案。若 Shell 的 execute 已因前一提交处于 busy,它直接返回 false,新的取消命令没有投递,但草稿仍被清理。此为原源码调用链推断,尚未在 Electron 真实点击复现;验收需覆盖“提交在途→点关闭→取消未受理”的答案保留与按钮禁用/状态反馈。源码分别见 QuestionCard 关闭按钮、父组件取消路径、Shell 忙碌拒绝。
去重与源码
#58 是写盘失败回退;本条所有 storage 操作成功。#61 是普通 composer 的迟到发送成功清理;这里是结构化答案在结果前清理。#69 是认证失效全局擦除;本条不触发认证。#75 是跨视图整桶竞争;本条单组件/单 realm 即成立。关联 桌面聊天融合 Epic #31。
固定公开提交仅作导航,隔离证据保存当前源文件哈希,不宣称发行包或生产事故已复现:ConversationApproval、userInputDraftStore、ConversationShell。
触发与范围
桌面聊天的结构化回答提交后,异步投递结果尚未返回,
ConversationApproval就删除当前 request 的保存答案。若投递失败、请求仍等待输入,当前组件暂时保留 React 内存中的答案;但刷新或重挂载后已经没有可恢复的保存内容。按 P2 条件性答案丢失追踪。2026-09-30 只执行原组件、原 store 和 Shell 原函数的隔离合成验证,没有真实 UI 提交、服务故障注入、工具/Provider 或权限事故,不报告 P0。范围仅为 desktop-owned 聊天用户输入的持久化与确认/恢复合同;现有共享 Web 文件用于定位,不要求本轮修改 Web 或后端。
原逻辑验证与保护对照
fixture 原样转译
ConversationApproval和userInputDraftStore;从 Shell 的 TypeScript AST 提取execute、handleUserInputResolve的原正文,未改写函数。React test renderer 中的问题展示子组件为 inert leaf,仅暴露原父组件的答案 props 和 callbacks;不能据此声称视觉/AX 或真实子组件点击已复现。输入为一个同 thread/request 的必填自由文本问题及已保存的合成答案。存储是全成功内存 StorageLike,sender 只记录参数并返回 deferred Promise;没有接入真实 profile、localStorage、网络或命令 API。
共六个条件。完整原函数、输入、存储操作、组件状态和输出仅保存在本地 ignored artifact。测试中的 pending view 在失败后保留原请求,符合原 execute 失败分支不刷新该 view 的行为;没有真实后端或 Provider 请求。
根因与影响边界
父组件的
submit、handleCancel调用 voidonUserInputResolve后紧接clearUserInputDraft,没有等待确认结果。Shell handler 又用 void execute 执行实际异步 resolve;失败由 execute 捕获,清理调用无法获知失败。保存状态因此早于确认结果被删除。实际影响限定于恢复:已挂载组件仍可让用户复制或重试内存中的答案,不能声称点击失败时立即丢掉所有输入。若该组件随后卸载/刷新,同一请求无法从该 store 恢复。Shell 源码仍保留错 request 校验;busy 与必填为空的 submit 对照通过。本条没有错误目标提交或授权突破证据。
桌面优化与验收
补充同根的忙碌取消路径:
QuestionCard顶部关闭按钮在status=submitting时仍可触发原handleCancel;该函数没有 submit 的 busy guard,调用onUserInputResolve后立即清理本地答案。若 Shell 的execute已因前一提交处于 busy,它直接返回 false,新的取消命令没有投递,但草稿仍被清理。此为原源码调用链推断,尚未在 Electron 真实点击复现;验收需覆盖“提交在途→点关闭→取消未受理”的答案保留与按钮禁用/状态反馈。源码分别见 QuestionCard 关闭按钮、父组件取消路径、Shell 忙碌拒绝。去重与源码
#58 是写盘失败回退;本条所有 storage 操作成功。#61 是普通 composer 的迟到发送成功清理;这里是结构化答案在结果前清理。#69 是认证失效全局擦除;本条不触发认证。#75 是跨视图整桶竞争;本条单组件/单 realm 即成立。关联 桌面聊天融合 Epic #31。
固定公开提交仅作导航,隔离证据保存当前源文件哈希,不宣称发行包或生产事故已复现:ConversationApproval、userInputDraftStore、ConversationShell。