fix(chat): preserve inline corrections across view changes and reload - #5401
Conversation
Synthetic before/after evidenceGQ08: type “先核对依赖,再继续当前任务。”, receive an unconfirmed response, then leave and return to Chat. Before — packaged baseline 3ad3269: the inline instruction is gone after the view roundtrip. The separate composer still contains its own request. After — head 3f72ba6: the original inline instruction survives both the view roundtrip and reload. Accepting the composer request does not erase it. Reload dispatches no request; the retry keeps its original ingress identity. Mobile, 390×844: the same retained instruction and current-turn controls remain readable alongside the composer. All data are synthetic. These screenshots show packaged frontend behavior with a synthetic executor; they do not establish installed App or live executor adoption. The browser scenario separately checks receipt mismatch, confirmed non-delivery, exact-turn isolation and blocked storage. |
huangruiteng
left a comment
There was a problem hiding this comment.
Approval conclusion (author-owned PR; GitHub blocks formal self-approval)
审阅 head 3f72ba66b77769ecad8765d4107712c2b290be48:APPROVE。未发现阻塞问题;交付的是可独立验证的 App 纠偏恢复,完整 R3 协作验收仍然开放。
动机
正在执行时,用户输入“先核对依赖,再继续当前任务。”,回执未确认后切换总览或刷新,旧版会丢掉消息内的纠偏草稿和请求身份。用户需要重输,还可能在上游已接收时产生第二条指令。这次消除了这个具体负担,没有把客户端恢复误报成完整团队协作已经实现。
改动思路
入口仍是管家与 Goal Chat 共用的活动控件、以及普通输入框。最小可靠修复是扩展已有输入框缓存,让两处共享一个 TS owner;仅把组件状态上提不能跨刷新,另建一份缓存又可能覆盖输入框的未知请求。缓存保存的是用户尚未确认的意图,不能从服务端推导。执行、去重和送达判断仍由现有 Chat ingress/store 与 API 回执校验负责;恢复只读取,不重新发送。
具体改动
关键代码讲解
steering-recovery.ts:42的共享缓存惰性读取已有浏览器格式;retainSteeringRequest写入一个页面 Map,retireSteeringRequest只移除匹配身份。删除旧的 composer-only 模块与页面重复 Map,避免两个控件各写一份旧快照。存储不可用时保留页面内恢复,不能声称刷新也恢复。message-activity.tsx:50的updateDraft在普通输入时保存原 Session/Turn、文本和 UUID。同样的去空白文本沿用身份;编辑成不同指令才生成不同请求。初始化与目标变化只恢复对应草稿,不推送上游。message-activity.tsx:64的steer延用现有精确回合回调,未知或不匹配回执保留身份;只有明确not_delivered才换身份。成功只退休发送的那个条目,旧异步返回不能抹掉较新的草稿。personal-workspace-page.tsx:1631的输入框分支改用同一缓存,原有 Session/文本匹配和回执反馈保留。独立输入框与消息内草稿并存、接受前者后不丢后者,已通过浏览器验证。原有 conversation-activity 场景覆盖恢复与负例;两个 RFC 更新只声明已证明的客户端边界。
对主干的风险
最强反例是刷新之后换了请求身份,或两个控件互相覆盖,导致重复执行。已用真实打包前端和浏览器 HTTP/SSE 跑同一回合:旧包在切换视图后找不到草稿,当前 head 切换与刷新后保留文本/身份,而且没有新增发送。结束回合保留草稿但禁用发送,另一个 Goal 不继承旧草稿;存储写入失败仍可在本页切换恢复。桌面与窄屏的完整可见区域、保留草稿与普通输入框一并检查,见前后对照。
语义与 CI 对齐
复用现有 steering、delivery_state 和 ingress 语义,无消息正则分类、激活门槛或授权扩张,也无 default-off 声明。构建及来源核验通过;完整打包场景 29 项通过,其前端源码与最终 head 一致,最终 head 重跑了受影响场景。真实 Chat HTTP/文件 store/协议子进程测试 10 项通过;风险 canary 的 19 项与 3 个直接检查通过,无跳过或手工 hold。按本次能力契约没有查询 CI。浏览器执行器和协议子进程均为隔离合成环境,不能证明在线模型采用或本机 App 已安装新版。
我的整体评价
这是 justified increment:持续性与用户体验都在这个有界路径上改善,用户不再重新输入已知指令,也没有额外确认;恢复不自动重放。新增代码围绕现有缓存和真实调用点,删除重复 owner 的未来维护收益已兑现。保留真实浏览器缓存格式有明确读者,未保留旧模块包装或新增协议版本。没有阻塞发现;剩余风险是存储不可用时刷新不能恢复,以及 live/installed/Lark 与多轮团队采用尚未验收。PR5396/5399 仍为未合入的独立工作,本 PR 不依赖它们。产品行为按当前仓库规则留给维护者合并。
English verdict: APPROVE - head 3f72ba6. Shared TS draft/retry recovery removes a reproduced navigation/reload loss without replay or authority expansion; packaged regression and production Chat HTTP/store tests pass. Live executor adoption and installed App promotion remain outside this slice.
Signed-off-by: huangruiteng <huangrt01@163.com>
3f72ba6 to
b853293
Compare
huangruiteng
left a comment
There was a problem hiding this comment.
Approval conclusion (author-owned PR; GitHub blocks formal self-approval)
Reviewed exact head b853293f184c1fe0deab5ff8aa50eee7cbced431
(rebase of 3f72ba66b77769ecad8765d4107712c2b290be48 onto
main@98acf52e7e41c193959bd45db622c65298273714).
动机
正在执行时输入"先核对依赖,再继续当前任务。",回执未确认后切换视图或刷新,旧版会丢掉消息内的
纠偏草稿和请求身份:用户需要重输,如果上游其实已经接收,还可能产生第二条指令。这次让管家与 Goal
Chat 的普通输入框和消息内纠偏控件共用一份客户端缓存,恢复只读取、不重放,未确认/不匹配的回执保留
原 ingress 身份,只有明确 not_delivered 才换身份。交付的是这个有界路径,完整 R3 协作验收
(live executor 采用、已安装 App 回读、Lark 纠偏)仍然开放,PR 正文已如实标注。
改动思路
入口仍是共用的活动控件与普通输入框;最小可靠修复是扩展已有输入框缓存,让两处共用一个 TS owner,
并删除旧的 composer-only 模块与页面里重复的 Map。仅把组件状态上提无法跨刷新,另建一份缓存又可能
覆盖输入框里未知投递状态的请求。执行、去重与送达判断仍由现有 Chat ingress/store 与回执校验负责。
rebase 到当前 main 时按语义解冲突:RFC §5.11 同时保留 main 新增的"视图/导航恢复"段落与本 PR 的
纠偏草稿恢复段落(两者是不同边界,合并后读起来是连续的 App 端契约),代码侧与 main 的
#5399(类型化视图)、#5405(回复来源上下文)合并后由 tsc/npm run build 验证无冲突。
具体改动
steering-recovery.ts(新增 59 行):共享缓存惰性读取已有浏览器格式;retainSteeringRequest
写入页面 Map,retireSteeringRequest只移除匹配身份;删除composer-steering-recovery.ts,
避免两个控件各写一份快照。存储不可用时只保留页内恢复。message-activity.tsx(+44/−8):updateDraft按原 Session/Turn 保存文本与 UUID;初始化与
目标变化只恢复对应草稿,不推送上游;steer未知/不匹配回执保留身份,成功只退休发送的那一条,
旧异步返回不会抹掉较新的草稿。personal-workspace-page.tsx(+2/−15):输入框分支改用同一缓存,保留原有 Session/文本匹配
与回执反馈。conversation-activity.mjs(+42/−3):浏览器场景覆盖视图往返/刷新恢复、并存的两个草稿、
身份保持、不匹配回执、明确未投递、结束回合与阻塞存储等负例。- 两个 RFC 更新只声明已证明的客户端边界。
对主干的风险
- 最强反例是刷新后换身份或两个控件互相覆盖导致重复执行。打包前端 + 浏览器 HTTP/SSE 的同一回合
验证:旧包切换视图后草稿丢失,本 head 切换与刷新都保留文本/身份且没有新增发送;结束回合保留草稿
但禁用发送;另一个对话不继承旧草稿;存储写入失败仍可在本页恢复。 - 已验证(本 head):
npx tsc --noEmit与npm run build干净;
pytest -q tests/test_chat_turn_steering.py10 passed;conversation-activity场景在
开发态与打包态都通过;PR 代码/测试补丁内容与评审过的3f72ba66b完全一致(只有 hunk 偏移差异),
rebase 未改变语义。 - 未验证(独立 hold,非本 PR 引入):
typed-actions场景在干净main@98acf52e7上以完全相同的
签名失败(等待.personal-gated-summary超时),因此它不能用来证明或否定本改动;本 PR 不改动
该判定。作者正文里"完整打包 29 项"的证据是在 rebase 前、该 main 变更之前取得的。 - 未验证:live executor 采用、已安装 App 回读与 Lark 纠偏仍在本 PR 边界之外;浏览器执行器与协议
子进程都是隔离合成环境。 - 非阻塞建议:无新增。缓存格式沿用既有浏览器键,未新增协议版本或旧模块包装。
我的整体评价
这是 justified increment:持续性与用户体验在这个有界路径上改善(不再重输已知指令、不自动重放),
同时删除了重复 owner、没有扩张授权或新增协议。rebase 解决冲突的方式没有回退 main 的改进。
没有阻塞性发现,APPROVE;typed-actions 的 main 既有红项是独立 hold,不应由本 PR 承担。
English verdict: APPROVE. Exact head b853293f184c1fe0deab5ff8aa50eee7cbced431 rebases the
reviewed 3f72ba66b onto current main with an unchanged code/test patch and a both-sides RFC
resolution; shared TS draft/retry recovery removes a reproduced navigation/reload loss without
replay or authority expansion. tsc, npm run build, the real Chat steering suite (10 passed)
and the packaged plus development conversation-activity scenarios pass at this head. No
blocking findings; the typed-actions failure reproduces identically on clean
main@98acf52e7 and is a separate merge-readiness hold.
|
已 rebase 到当前 冲突解决:只有 顺带清掉一个 main 红灯:#5399 遗留了 本 head 验证: 独立 hold(非本 PR 引入):整目录场景跑时 |



Goal And Delivered Outcome
main; merge base5514c231e3838573e03e1532c9b2a3a806f176a6.Scope And Continuation
Complete for client draft/retry recovery in both Manager and Goal Chat. One shared TS cache replaces the composer-only cache; Chat ingress/store remain the delivery authority. Accepting one draft cannot erase another. An ended turn retains its draft with sending disabled, and another conversation starts empty. Blocked browser storage supports recovery within the page only.
Live executor adoption, installed App readback, Lark correction and the wider R3 collaboration journey remain unqualified. This independently reversible client fix does not wait for those separate runtime paths. The existing R3/RFC checkpoint preserves that distinction. Lark needs no companion code for this slice: it has no browser draft cache, and its delivery/steering acceptance remains separate.
Validation
3f72ba66b77769ecad8765d4107712c2b290be48. The full packaged suite ran before commit/rebase with identical changed frontend sources; build/source verification and the affected packaged scenario were repeated at this exact head.finishedsynthetic,public_fixturestaticpasseduv run --extra test python scripts/chat_bundle.py build --installandverify --source: TypeScript build and packaged-source manifest;git diff --check.real_entrypointpassednpm run smoke:personal-workspace-packaged: all 29 scenarios passed. The packaged conversation-activity scenario was repeated at the final head using browser HTTP/SSE transport and an isolated synthetic executor.real_backendpasseduv run --extra test pytest -q tests/test_chat_turn_steering.py: 10 tests pass against production Chat HTTP/store/subprocess entrypoints with disposable synthetic state. No provider credentials or active Goal mutation.regression_paritypassed3ad3269af; head restores it. Additional cases cover reload without dispatch, composer/inline coexistence, unchanged retry identity, mismatched receipt, confirmed non-delivery, ended turns, other conversations and blocked storage.integrationpassedloopx canary premerge --from-git-diffagainst the pinned merge base: 19 selected checks and 3 direct checks pass; no failure, skip or manual hold. The explicit browser/backend runs cover UI paths the automatic selector does not select.manualpassedCoverage and gaps: restoration and retirement share one typed client owner; existing backend tests qualify ingress/delivery invariants. No backend or authority-store migration, paid-model evaluation, live executor adoption or installed App promotion is claimed. No identified skip within this bounded slice. Change-quality receipt
cqr_583d8349b954b866a812matches the pinned base and head.Frontend / Visual Evidence
changedsyntheticType of Change
LoopX Area
Technical Direction
R3 capable conversation continuity; shared TS client recovery. The future-facing pass removes the composer-only cache and duplicate ownership without moving provider or delivery rules into the frontend.
Shared-authority RFC fixture impact
N/A: client draft recovery does not change shared Goal authority, runtime routing or persisted backend contracts.
Boundary Checklist