Repository navigation
fix(chat): load delegated originals by exact turn before paging - #7971
Conversation
|
This pull request has merge conflicts with Choose the remote for the base repository, not an out-of-date fork. git fetch upstream
git rebase upstream/main
# Resolve each conflict, git add the resolved files, then git rebase --continue.
git push --force-with-lease origin HEADFor a same-repository clone whose Keep the DCO |
loopx-agent
left a comment
There was a problem hiding this comment.
Reviewer: actor_kind=model_agent; model=gpt-6.1-sol; provider=OpenAI; reasoning_effort=max; declaration_source=runtime_reported
Request changes conclusion (author-owned PR; GitHub blocks formal self-review)
动机 / Motivation
在长会话的委派清单中展开准确原对话的人。先读完整 Session 再筛选原 Turn,变为在同一 HTTP owner 内先选 Turn 再完整分页。File/SQLite 2,526 条合成消息、63 条委派中,完整 24,056 UTF-8 字节原文及两条可见消息保持,13 次 HTTP 读取变成 1 次。不认证已安装 App、冷启动/p95、模型效果、委派清单全部慢读或发送/回传工作已经兑现。新增模块增长未在同一 diff 更新既有 ceiling:chat_server.py 从2035增至2039,当前 ceiling 仍2035。
改动思路 / Approach
复用 manager-context 历史、Chat HTTP 和前端既有原文读取 owner;Python 仅在现有 IO/投影边界增加过滤,未新增控制面决策。现有分页 helper 绑定 Session+Turn,前端核对回显和每条消息的 Turn,完整组装后才显示。旧版完整快照仍可本地筛选;带分页但没有准确 scope 的中间版本拒绝读取,需要配套交付客户端与服务端。
具体改动 / Changes
完整审阅10文件 +161/-19,并先按 既有语义交办规格 的 A28 判断;spec_ref:docs/architecture/rfcs/capable-manager-semantic-handoff-v0.zh-CN.md,spec_revision:feea5a6e56066eef87889d470d7991922f9f00ec。本次 A28 的相关读取切片通过:63条委派、两个接收方、2,526条消息,File/SQLite 的目标原文及所有投影元数据与全历史筛选完全一致。24,056 UTF-8字节正文、600次长段落、结束标记及回复完整;原路径13次/708,568响应字节,选定Turn为1次/28,858字节。这是合成局部读取比较,不外推安装或p95收益。
当前332项Python测试通过,历史客户端真实HTTP smoke、TypeScript/Vite和Chat打包通过。实际Ego检查1440/390页面、搜索定位、键盘展开、错误scope/后续页失败0残片、真实日志损坏及重新展开恢复;File和SQLite均可读。原文滚动时固定页脚保留,窄屏横向溢出0。当前公开图证标注f71,独立当前构建另做了实际整页检查。
对主干的风险 / Risks
[P2] 在同一PR收口新增模块 ceiling 失败。 loopx/chat_server.py:932 的必要分支使模块从2035增至2039行,既有 loopx/canary/module_metric_baseline.json 仍为2035。实际 premerge 的 module_ceiling_colocation 失败;immutable base/head 配对确认基线没有该新增模块债务,当前有。按 预算决定流程,保留有意义的严格读取/恢复隔离语义,说明新增4行的作用,在同一diff更新既有审阅ceiling或做有parity的局部整理,再跑原命令。无需为这4行启动无关重构或删安全语义。
完整premerge未通过:上述新增直接门和相应ratchet债务需修复;两端原有 render_canonical_todo_sections oversized finding相同且magnitude_regressions=0。issue-fix-pr-lifecycle 在两端同一fixture子命令均exit1,具体根因未进一步归因,保留为失败而非通过。其余选中risk/profile和公开边界检查通过。初次测试命令中不存在的文件路径已更正,后续实际332项结果独立保留。未重复作者的strict-mypy结果为自己的通过。
wait_for_ci=false,CI未查询、轮询或等待。上述阻塞来自当前本地必需检查;远端CI未知未产生阻塞。没有权限、计费、调度或发送语义变更;客户端/服务端配套与全历史严格读取成本保留。
我的整体评价 / Verdict
读取优化与失败恢复有独立证据,仍需在本PR补齐新增模块预算决定,当前结论 REQUEST_CHANGES。现有page helper和前端reader复用适量;更大控制面迁移不属于本次边界。当前可交付切片是完整原文的读取优化;预算门仍需在本 PR 收口。合并、安装及完整父目标验收分别由原owner处理。
已检查当前源码的默认、展开、窄屏与失败恢复整页;合成数据和模拟边界已明确。不认证已安装 App、冷启动/p95、模型效果、委派清单全部慢读或发送/回传工作已经兑现。
English verdict: REQUEST_CHANGES f71be82. Complete scoped history and recovery pass independent local validation; settle the newly crossed module ceiling in this diff. CI was not consulted.
Signed-off-by: LoopX Agent <337587101+loopx-agent@users.noreply.github.com>
Signed-off-by: huangruiteng <huangrt01@163.com>
Signed-off-by: huangruiteng <huangrt01@163.com>
f71be82 to
9151bb2
Compare
huangruiteng
left a comment
There was a problem hiding this comment.
Reviewer: model_agent · GPT-5 · OpenAI · self_reported
动机 / Motivation
在个人工作台中展开委派原对话的主人,以及承载历史读取的 Chat HTTP、manager-context projection 与 File/SQLite store。
当前会先下载整个 Session 的历史,再在客户端筛出 source_turn_id。Session 越长,这个动作就会把越多无关 Turn 带过严格日志投影、分页和 HTTP;同一条原对话却仍只需要一个 Turn 的完整正文。同一个长历史 Session 中,旧路径先读取整个 Session 再筛选,新路径携带 turn_id 只投影并分页目标 Turn,最后仍组装完整可见消息。
本次沿既有历史入口传入可选 turn_id:服务端仍先做严格 Session 快照,但按准确 Turn 选出全部消息后才分页;客户端继续完整拼装这一 Turn,并核对服务端回显、每页 cursor 和每条消息的 Turn。File/SQLite 打包 smoke 均显示完整正文、附件、collaboration 和 return metadata,错误 Turn、外来消息、跨 scope cursor、损坏日志和后续页失败均拒绝残片。
这是一个完整的有界切片:普通主对话仍读取完整历史,不带 turn_id 的请求保持旧响应和旧恢复行为;不新增持久缓存、索引、权限、provider 或已安装 App 性能结论。它也不声称解决所有委派列表慢读。
改动思路 / Approach
入口仍是 OriginalRequest -> fetchChatSessionHistory -> GET /api/chat/sessions/<session>;权威数据仍是 File/SQLite ChatStore 中的 Session、Turn、严格 append journal 和已有 collaboration/return metadata。turn_id 只是历史选择器,不携带权限。
新路径没有第二套历史 owner,selector 只放在既有 projection/page 边界。project_chat_session_snapshot 继续读取同一份严格 Session 快照,只在既有 page_chat_messages 前筛出目标 Turn;page_chat_messages 的 opaque cursor 在 scoped read 中绑定 Session + Turn。HTTP composition root 负责拒绝缺少 page limit、重复或空 selector 的请求,并在历史读取时跳过 recover_exited_turn,避免展开旧对话顺带恢复同 Session 的另一执行。
客户端只在全部页都满足 message_page.turn_id、消息 turn_id、offset、total、digest 和 completeness 后返回 snapshot;旧服务端返回的完整无分页快照仍可本地筛选。交付精确 Turn 原对话读取和失败恢复,不建设通用历史索引或 provider 迁移。
具体改动 / Changes
相对 6f90e561ee99a1ef2e3db4e90c94e3c767990c58,当前 head 修改 12 个文件,+162/-21:3 个运行时 owner、3 个前端/reader 文件、1 个协议文档、fixture/browser/Python 回归,以及 2 个同 diff 门禁收口。没有新增持久字段、配置、后台 worker 或权限入口。
规格引用:docs/architecture/rfcs/capable-manager-semantic-handoff-v0.zh-CN.md,revision 6f90e561ee99a1ef2e3db4e90c94e3c767990c58。其中 A28 要求长详情、分页、快照变化及历史 Turn 冲突下全部获准原文可达、不混快照、不藏记录且不绕来源门;该条在当前 head 标记为 implemented,由精确 Turn projection、Session+Turn cursor、客户端完整性验证和 File/SQLite 失败恢复 smoke 覆盖。
关键代码讲解
chat_history.py:12的page_chat_messages复用原有 opaque cursor,在 scoped read 中把 source identity 从 Session 扩展为[Session, Turn];跨 scope 或 digest 变化直接拒绝,并在message_page回显 Turn。roundtrip.py:336的project_chat_session_snapshot要求 selector 与分页同时出现,先验证 Turn 属于该 Session,再从严格快照筛选消息后分页;完整消息、附件和 return-delivery 投影仍由原 owner 生成。chat_server.py:922的_session_snapshot接受可选turn_id,拒绝不完整的参数组合;只有历史 scoped read 跳过 exited-Turn recovery,普通 Chat refresh 仍执行原恢复路径。chat.ts:976的fetchChatSessionHistory逐页核对message_page.turn_id、每条消息的 Turn、offset/total/digest 和 cursor 不重复,任何残片都不会返回给 UI;无分页的旧完整快照仍兼容。conversation-requests.tsx:161的OriginalRequest只把已有的source_turn_id传给 reader,仍保留 abort、active guard 和现有可见内容渲染。
同 diff 还处理了两个实际门禁结果:chat_server.py 为 query 分支、恢复隔离和 selector 传递净增 4 行,因此把审阅 ceiling 从 2035 调到 2039,未新增 any/dict_any 债;issue-fix-pr-lifecycle-smoke 改为从 canonical list_goal_todos 校验完成 evidence,而不是假设展示层 Markdown 仍携带已完成 prose。后者是当前 main 已有 contract 的 smoke 修复,不改变产品代码。
对主干的风险 / Risks
最强风险是错误 Turn 或残片被展示成原对话。当前实现从 HTTP 参数、Turn 归属、Session+Turn cursor、页面 digest、每条消息 Turn 到完整分页逐层拒绝;新测试覆盖 wrong-turn echo、foreign message、跨 scope cursor、损坏日志和后续页 503,浏览器均显示错误入口而不是部分正文。
第二个风险是历史读取意外恢复同 Session 的另一活跃 Turn。turn_id 路径明确跳过 recover_exited_turn,同时断言 unscoped Chat refresh 仍调用原恢复;File/SQLite smoke 也验证读取期间没有新 Turn 或 provider 写入副作用。错误 choice 仍返回现有 400/404/503,可由重新展开恢复。
兼容性风险是 scoped client 必须与支持该字段的服务端一起交付。当前采用 co-deployed additive contract:旧请求字段、旧完整 snapshot 和普通历史路径保持;带分页却没有准确 scope 的中间响应会被客户端拒绝,而不是静默本地兜底。
当前验证:
pytest tests/test_chat_conversation_bindings.py tests/test_manager_context_roundtrip.py tests/test_chat_process_recovery.py tests/test_chat_agent_model_http.py tests/test_chat_transport_composition.py -q:248 passed。npm run build:TypeScript、Vite 和 Chat bundle PASS;仅保留既有大 chunk warning。npm run smoke:conversation-history:PASS,覆盖真实 HTTP/store、完整分页、取消、旧完整快照与零读侧写入。LOOPX_REQUEST_STORAGE=sqlite|file npm run smoke:delegation-search:两者 PASS,覆盖完整 24KB 原文、错误 scope、损坏日志、重试、桌面和 390px。- 标准 premerge:19 selected checks PASS,manual holds 0;Ruff、py_compile、diff hygiene、semantic-vocabulary 和 bilingual RFC check PASS。
语义与 CI 对齐
该 PR 复用既有 loopx_chat_message_page_v1、Session/Turn 词汇、strict snapshot 和 recovery admission,只在同一 page contract 上增加可选 selector,没有创建新 authority、执行生命周期或共享决策词汇。没有查询或等待 GitHub CI;managed policy 为 wait_for_ci=false。未安装/重启真实 App,也未运行手机和真实 provider;这是剩余的端到端证据缺口。
我的整体评价 / Verdict
APPROVE。 当前精确 head 没有阻塞 finding。长期任务维度改善:展开原对话不再反复读取和投影同一 Session 中与源 Turn 无关的全部历史。用户体验维度改善:长会话中的原文更快到达,同时完整正文、附件、错误与恢复入口不变;视觉层级和文案没有变化。
代码量集中在既有 owner 和独立负例,生产机制只有一个可选 selector、一个 scoped cursor identity 和一个 recovery 条件;没有需要在本 PR 新建索引或缓存。兼容性由 co-deployed scoped contract 加旧 unscoped/完整 snapshot parity 覆盖。剩余风险是安装态、手机和真实 provider 的延迟/采用尚未验证,不能把合成 File/SQLite 结果外推为生产 p95。
English verdict: APPROVE — head 9151bb2b9348518c4d74acebaa36fd6102ab7196 reads only the selected historical Turn before paging while preserving complete content, cursor scope, ordinary history recovery, legacy snapshot behavior and File/SQLite failure recovery. No blocking finding remains; installed-App/mobile/real-provider latency remains unverified.
展开委派的“原对话”时,客户端原来先读完整个 Session 的冻结历史,再筛选一轮。历史越长,越容易把一次完整原文读取变成多次无关投影和 HTTP 往返。本次在现有历史接口增加可选
turn_id,先按准确的原 Turn 选出全部消息,再分页与投影;前端沿原入口读取并核验完整结果。复用 manager-context 历史/展示 owner 和 native Chat HTTP 组合层。Python 改动留在既有日志读取与传输适配边界;没有新增控制面决策 owner、权限、缓存、设置或执行入口。选定历史读取也不再恢复同 Session 中其它正在执行的轮次;普通 Chat 刷新保留原恢复行为。
loopx-project/loopx:mainat6f90e561ee99a1ef2e3db4e90c94e3c767990c58; tested head:9151bb2b9348518c4d74acebaa36fd6102ab7196。message_page.turn_id。跨来源游标、丢失/错误 scope、混入其它 Turn、损坏日志和后续页失败均拒绝展示残片,重新展开可重试。Rebase And Self-Repair
main,解决 browser fixture 与最新 smoke 行为之间的冲突,保留 main 对真实 submit control 的等待。chat_server.py因精确 Turn 参数、历史读取不触发恢复、selector 传递而净增 4 行;按预算决定把既有审阅 ceiling 从 2035 调整为 2039,any/dict_anyceiling 不变。main已独立完成render_canonical_todo_sections的同域 helper 提取,因此本 PR 在 rebase 时丢弃了重复实现。issue-fix-pr-lifecycle-smoke改为从 canonicallist_goal_todos校验完成 evidence,不再假设已完成 prose 必须留在 Markdown 展示层。验证与可复现比较
同一自有合成 fixture:2,429 条消息、63 条委派、两个接收方,目标原文为完整 24,054 UTF-8 字节、两条可见消息。顺序交错运行 3 对真实 HTTP 读取,所有目标消息及元数据逐项一致:
这是每次展开原对话的局部比较。普通主对话启动仍按其完整历史读取;上述数字不包含实际已安装 App、真实 provider 全链路、冷启动或 p95,也没有证明委派清单慢读已解决。比较脚本和原始运行资料保留为私有临时证据,不进入仓库;fixture 的
--earlier-messages选项供相同工作负载复现。npm run build通过(TypeScript、Vite、Chat 打包;既有大 chunk 提示保留)。File / SQLite 的完整smoke:delegation-search均通过,包含真实 HTTP 精确 Turn、24KB 完整原文、跨页失败、scope 污染、取消/迟到/关闭重开、中英文和窄屏。npm run smoke:conversation-history通过,覆盖真实 HTTP/store、完整分页、取消、旧完整快照与零读侧写入。loopx canary premerge --from-git-diff --git-diff-base origin/main:19 selected checks passed,manual holds 0。wait_for_ci=falsepolicy。页面证据
截图的原始视觉版本为
f71be82be39c3f1a9648d9d32e3b2b27255496f9;真实打包页面 + 自有 File/SQLite HTTP fixture,所有画面均为合成数据。当前 head 在该视觉契约和最新main上重新构建,并重新通过 File/SQLite 桌面、窄屏、错误 scope、损坏日志与恢复 smoke。旧候选实测每次展开从 13 次无 selector 请求变成 1 次精确请求;24KB 正文完整。该数字不冒充当前安装态延迟。默认列表首屏:混合待回复/待结论/待送达、多接收方及分页。
完整原文桌面视图(前后显示一致;变化在读取路径):
390px 窄屏末尾:完整正文结束标记及回复可见,横向溢出 0px。
严格日志失败时显示恢复提示且没有残片;恢复来源后重新展开,两条完整消息均可读。