Repository navigation
fix(browser): wait for DOM content when reopening recovered history - #8125
huangruiteng merged 1 commit into
Conversation
`openWorkspacePage` defaults to `waitUntil: "networkidle"`, so the required browser lane dies on Playwright's 30 second navigation timeout whenever the page keeps a request in flight. The conversation-history-recovery scenario does exactly that on purpose: it interrupts the second history page and asserts that a partial read cannot masquerade as complete history. Four call sites in that file still took the default while the fifth and five other scenarios already ask for `domcontentloaded`. The helper's own `personal-goal-home` visibility wait then decides readiness, so the navigation no longer has to wait for traffic the fixture holds open. Signed-off-by: ChengLong GU <88359731+gcl-coder@users.noreply.github.com>
loopx-agent
left a comment
There was a problem hiding this comment.
Reviewer: actor_kind=model_agent; model=gpt-6.1-sol; provider=OpenAI; declaration_source=runtime_reported; reasoning_effort=max.
动机 / Motivation
维护者运行既有会话历史恢复 smoke 时,会遇到刻意保留的请求阻止导航等待结束。
之前,分页恢复场景在导航阶段等待网络完全空闲,30 秒后超时,无法验证重试;现在页面结构就绪后继续,由原有可见状态和内容断言判断恢复是否完成。
同一覆盖率开启场景的基线失败、当前 head 通过;完整分页、长正文、草稿保留、不重复派发和替换 Turn 的中断目标断言均保留。
本 PR 只修复验证入口的等待方式,不改变产品恢复逻辑,也不证明真实模型、已安装 App 或长期使用收益。
改动思路 / Approach
先挑战最容易的修法:把共享导航 helper 的默认等待改短,虽然少写几行,却会影响所有其他场景;删除刻意等待的请求也会失去恢复竞态覆盖。当前实现复用已有 gotoWaitUntil 参数,只在四个调用点选择 DOMContentLoaded。页面主页可见性仍由 helper 检查,历史是否完整、能否发送、旧读是否失效仍由各场景自己的断言决定。网络空闲和业务恢复是两个不同条件,这个边界划分合理。
具体改动 / Changes
本结论绑定 9695dd9ab10212428d8a2f3c7138cb950c0e407f,审查的一文件差异为 +4/-0,全部属于既有测试场景,无产品源码或断言删除。没有发现这四个等待选项的独立书面规格;已读 #6886 与 #6890,它们处理另一条 Goal hierarchy 请求计数竞态,不能直接变成本 PR 的验收要求。本次以独立复现的导航超时和已有恢复语义断言判断交付价值。
关键内容讲解
- pagedHistoryRecovery 保留 451 条分页、第二页 503、长正文、草稿和发送门槛;变化只让导航返回,重试仍须读取 0、200、400 三页,不能把第一页当完整历史。
- retiredHistoryRead 与 recoveryObserverHandover 继续刻意延迟旧读和旧投影,验证返回管家后可发送,以及中断按钮指向替换后的 Turn。
- 场景主入口 继续检查原始顺序、同秒消息、session 身份、投影身份和不重复派发;中间的 reload 等待没有改写。已有 overlap 场景本来就在使用相同选项,本 PR 没有增加新 smoke。对最近八项同作者 PR 的有限扫描未发现相同历史 smoke 批量复制,不能推断扫描范围以外的贡献情况。
对主干的风险 / Risks
没有发现阻塞问题。最强反例是页面结构已好、历史仍不完整:如果修改连带放宽正文和 effect 断言,就会掩盖产品错误。完整差异没有这种放宽,实际运行也覆盖了分页失败、旧读迟到、草稿保留、零重复派发和替换 Turn 中断身份。
使用相同场景与覆盖率模式,基线在 networkidle 导航等待 30 秒超时,当前 head 完整通过,进程耗时约 35.1 秒。JavaScript syntax、quick premerge、语义词汇和公开边界检查通过。完整 JSON 对照中,target main 与该 head 的唯一未审查项均为 _admitted_interaction_next_cli_actions:62 decision points,阈值 60;该函数、检查器及配置逐字节未改,完整结构化报告仅去除 repository_root 后一致。保留 failed,不修改阈值,作为无关既有债务。
这是 development browser 加合成 API 的验收;没有调用真实模型,也没有验证已安装 App。作者提供的远端 CI 失败说法保留为未归因诊断,本次按 wait_for_ci=false 未查询或等待 CI。
我的整体评价 / Verdict
交付判断为 goal_achieved,限于修复既有验证入口。long_horizon 为 preserved:产品后续执行规则不变,原有重复读取与恢复断言重新可执行;user_experience 为 preserved:没有新增用户导航、信息重填或确认轮次。仅修改 examples 中的导航等待选项,未改变渲染页面;实际浏览器语义验证不能充当新的 UI 设计验收。 未声称重新验证已安装 App 的视觉质量。
代码量与收益相称;四个调用点复用已有选项,比更改全局默认更窄。没有需要本 PR 顺带重构的兼容层,维护性旧债仍单独保留。exact-scope CQR 已有效验证;批准只针对这个未变 head,合并权限与合并就绪度仍是独立门。
English verdict: APPROVE — the exact-head browser run removes the reproduced navigation deadlock while retaining the original recovery and no-replay assertions. Real-provider and installed-App behavior remain unverified.
Goal And Delivered Outcome
Outcome basis / optional anchor: 无独立 issue;沿用 [Bug]: required dashboard-browser (3) shard asserts before the scoped Goal snapshot fan-out settles #6886 记录、由 fix(ci): stabilize required dashboard-browser goal-hierarchy shard #6890 关闭的同域诊断,本 PR 落在该结论没有覆盖到的调用点。
Goal/source and gap:
examples/personal-workspace-browser/scenario-context.mjs:23把gotoWaitUntil默认成networkidle,第 42 行原样交给page.goto,超时用的是 Playwright 导航默认的 30 秒。conversation-history-recovery.mjs里有 4 个openWorkspacePage调用点没有覆盖这个参数(第 15、135、203、283 行),而同文件第 67 行以及另外 5 个场景早就传了domcontentloaded。等待条件与场景本身的设计相冲突:这个场景会故意把第二次历史分页请求挂住,再断言局部读取不能冒充完整历史,于是网络永远不会进入 idle。Observable before → after, with the validation row that proves it: 在 fix(registry): retire the git probe process tree on timeout #8115 的 head
fd24d90b4那一轮,build、dashboard-browser (2)、chat-bundle-browser (2)三个必需 job 都在scenario-context.mjs:42抛page.goto: Timeout 30000ms exceeded … waiting until "networkidle",调用栈是conversation-history-recovery.mjs:14;主干2c24d5d102那一轮同名 job 同样是红的。改后这四处导航在domcontentloaded返回,就绪判据交给 helper 已有的personal-goal-home可见等待(15 秒)。Issue/task and intended base: Related to [Bug]: required dashboard-browser (3) shard asserts before the scoped Goal snapshot fan-out settles #6886. Base
main.Author Declaration
Implemented against
docs/development/ci-impact-selection.md说明这些 job 属于必需门。examples/personal-workspace-browser/conversation-history-recovery.mjsLOOPX_PERSONAL_WORKSPACE_SCENARIO=conversation-history-recovery node examples/personal-workspace-browser-smoke.mjsdomcontentloadedscenario-context.mjs:23git diff只含一个文件scenario-context.mjs、该场景的全部 5 个调用点,以及 fixture 里的text/event-stream响应点(fixture.mjs第 1457、1465、1907 行);跑过node --check确认语法。没有本地执行浏览器 smoke——那需要 playwright 与预构建的 dashboard——所以不声称原生浏览器或覆盖率负载下的结果,判定交给 required lane。Scope And Continuation
只改这个场景的 4 个调用点。helper 的
networkidle默认值保持原样,因为改默认值会影响约四十个场景,属于另一次带完整回归的迁移。若其它场景出现同类导航超时,按同样方式逐个迁移即可,不需要新协议或新的等待抽象。