Skip to content

fix(chat): preserve inline corrections across view changes and reload - #5401

Merged
huangruiteng merged 1 commit into
mainfrom
codex/conversation-steering-recovery-1001
Oct 1, 2026
Merged

huangruiteng merged 1 commit into
mainfrom
codex/conversation-steering-recovery-1001

Conversation

@huangruiteng

Copy link
Copy Markdown
Collaborator

Goal And Delivered Outcome

  • Outcome basis: GQ08 correction and semantic-handoff RFC §5.11, within roadmap R3.
  • Gap: typing “先核对依赖,再继续当前任务。” through Adjust this turn lost the inline draft and retry identity when switching views or reloading after an unconfirmed response. Retyping could submit a second request.
  • Before → after: the same instruction survives view changes/reload and retries the original ingress after unknown delivery or a mismatched receipt. Restoration sends nothing; confirmed non-delivery permits a fresh identity. The packaged regression fails on the previous bundle and passes here.
  • Intended base: main; merge base 5514c231e3838573e03e1532c9b2a3a806f176a6.

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

  • Tested revision: 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.
  • Run state: finished
  • Input classes: synthetic, public_fixture
Check kind Result Public-safe evidence / limitation
static passed uv run --extra test python scripts/chat_bundle.py build --install and verify --source: TypeScript build and packaged-source manifest; git diff --check.
real_entrypoint passed npm 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_backend passed uv 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_parity passed The same view-roundtrip fixture loses the inline draft on packaged baseline 3ad3269af; 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.
integration passed Risk-based loopx canary premerge --from-git-diff against 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.
manual passed Inspected synthetic before/after desktop and mobile screenshots: retained instructions, uncertainty feedback and the ordinary composer remain readable. Public-boundary scan and exact-diff change-quality qualification pass.

Coverage 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_583d8349b954b866a812 matches the pinned base and head.

Frontend / Visual Evidence

  • UI impact: changed
  • Before/after: synthetic screenshots in the accompanying visual-evidence comment.
  • States and viewports: unknown/mismatched receipt after returning to Chat; desktop 1512×982 and mobile 390×844.
  • Source data: synthetic
  • Attention review: preserves the existing inline correction and composer instead of adding navigation or confirmations. The changed state restores the user's own instruction beside its exact turn; uncertainty remains visible and recovery triggers no work. First viewport, opening navigation and primary CTA presentation are unchanged.

Type of Change

  • Bug fix
  • Documentation update
  • Test update

LoopX Area

  • Public docs or presentation surface (dashboard)

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

  • Diff, text and attachments exclude private state, credentials, raw traces, internal links and local machine paths.
  • No duplicated maintainer-owned benchmark work.
  • Scoped to the reproduced correction-recovery defect.
  • Visual evidence includes synthetic before/after states.
  • Every commit includes a DCO sign-off.

@huangruiteng
huangruiteng requested a review from maxliux5 as a code owner October 1, 2026 08:43
@huangruiteng

Copy link
Copy Markdown
Collaborator Author

Synthetic before/after evidence

GQ08: 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.

Before: lost inline correction

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.

After: retained correction beside its original turn

Mobile, 390×844: the same retained instruction and current-turn controls remain readable alongside the composer.

Mobile retained correction

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 huangruiteng left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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>
@huangruiteng
huangruiteng force-pushed the codex/conversation-steering-recovery-1001 branch from 3f72ba6 to b853293 Compare October 1, 2026 10:13

@huangruiteng huangruiteng left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.py 10 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.

@huangruiteng

Copy link
Copy Markdown
Collaborator Author

已 rebase 到当前 main@98acf52e7 并推送,新 head b853293f1(--force-with-lease)。

冲突解决:只有 docs/architecture/rfcs/capable-manager-semantic-handoff-v0.md §5.11 冲突。main(#5399)加了"视图/导航恢复"段落,本 PR 加的是"纠偏草稿恢复"段落,两者是不同边界,因此两段都保留、按 main 段落在前排列;代码侧 6 个文件自动合并。git diff 对比显示本 PR 的代码/测试补丁内容与评审过的 3f72ba66b 完全一致,只有 hunk 偏移不同。

顺带清掉一个 main 红灯:#5399 遗留了 ManagerHomeBoard 里对已删除 setManagerChatOpen 的调用,导致 main 的 dashboard 无法 tsc --noEmit、npm run build 失败(TS2552),这也会让本 PR 的 rebase 无法通过构建。已用独立 PR #5406 前进修复(改为 setSelectedGoalTab("chat")——与页面其它会话入口一致),已合并;本 head 的 tsc 与 npm run build 因此干净。

本 head 验证:npx tsc --noEmit、npm run build 干净;pytest -q tests/test_chat_turn_steering.py 10 passed;conversation-activity 场景在开发态与打包态都通过。

独立 hold(非本 PR 引入):整目录场景跑时 typed-actions 失败(等待 .personal-gated-summary 超时)。它在干净 main@98acf52e7(不含本 PR 任何内容)的 worktree 上以完全相同签名复现,因此与本改动无关;我会把它当作 main 的独立红灯另行处理,不作为本 PR 的阻塞项。精确 head b853293f1 的完整评审已发布(APPROVE)。

@huangruiteng
huangruiteng merged commit f2517a8 into main Oct 1, 2026
6 of 8 checks passed
@huangruiteng
huangruiteng deleted the codex/conversation-steering-recovery-1001 branch October 1, 2026 10:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant