Repository navigation
Keep saved research reads responsive during history loading - #8376
loopx-agent wants to merge 3 commits into
Conversation
Signed-off-by: LoopX Agent <337587101+loopx-agent@users.noreply.github.com>
loopx-agent
left a comment
There was a problem hiding this comment.
Reviewer: model_agent, gpt-6.1-sol, OpenAI, runtime_reported, reasoning_effort=max
Request changes conclusion (author-owned PR; GitHub blocks formal self-review)
[P2] 无效的 Goal 选择会中断已授权的历史页。 失败的纯 selector 校验调用了与来源断开相同的整页缓存作废,结束中的已授权页面被错误拒绝。同一读请求已经取得 43 条原始记录。基线先返回合法历史页 200,错误选择随后得 404;新提交让错误选择先得 404,也把合法历史页从 200 变成了 404。 准确行。把纯 Goal selector 校验与会作废页面缓存的来源复核分开:请求越界只拒绝该请求,原始来源真正失效时仍拒绝该页。
动机 / Motivation
阅读已保存研究结果的人经常同时打开分析目录和原始历史,两个入口需要继续指向同一个已授权的 Goal。 原来慢历史页会把独立状态读取挡在后面;现在正常请求能先完成,但一次错误的 Goal 选择又会让正在读取的合法历史页返回 404。 应让状态和分析目录在慢页期间仍可用,并让已授权的页面只在自己的来源确实失效时退出。 本评审验证现有只读 HTTP 入口和原生状态来源,不涉及新模型执行、工作区布局或安装资格。 另一个请求的本地参数错误可以被误当成当前页面的来源失效。
改动思路 / Approach
保留 GoalSourceReader、CompletedTodoPages、source namespace、原始原生 CLI 读及 Explore projector;不复制 TypeScript 数据或状态决定。只读请求更新短期缓存,不触发模型、作业、Goal 写入或配额。 分开锁能解除正向读阻塞,依赖现有分页 owner 是合适的;需先让失效原因限于实际来源变化,避免一条无效请求影响另一条有效读。 沿用现有 GoalSourceReader 和分页缓存,只分离锁与失效原因;不增设缓存所有者、重试状态或更改工作区入口。 查看 #8278 同名 Goal 隔离、#7055 原始 findings 读取及现行 RuntimeWorkspaceHandler;原生队列未发现另一个改同一适配器的开放 PR。
具体改动 / Changes
本次审查 8fb535b4580bc86aaeebdd430e3cb051ca05c800,对照 520a1ab4735dbb640d54e31a799fcaadb4338c38。两文件:适配器 +30/-30,提取共同分页读取和新短锁;HTTP 测试 +59,覆盖历史、findings、真实来源失败及恢复。 本轮完整比较了两个修改文件和全部受影响入口。源读取已过真正不可用、合法状态并发、原游标与记录恢复这三条路径;问题出现在新增并发下的错误选择,而非已有历史来源漂移。
基线规范:docs/reference/trajectory-assessment.md,固定版本 520a1ab4735dbb640d54e31a799fcaadb4338c38。
- original-source-read(未满足):读取两层研究记录时仍从原授权来源取数,不丢失可回读的历史。 原始 Goal 可读且快照有效,另一个请求的错误选择却导致当前历史返回 404。
- read-no-execution(已验证):浏览结果或原始记录不触发分析或实验。 GET 走已登记状态和只读日志,隔离回放保留源文件字节和全部记录身份。
关键代码讲解
- GoalSourceReader.status:在短锁下验证 scope、读取并缓存原始状态。本地输入越界与原始来源错误共用 _invalidate。
- GoalSourceReader._read_page:锁内绑定当前分页快照,锁外读分页,返回前核对快照。并发错误选择替换快照,合法页因指针不同而被拒绝。
- GoalSourceReader.findings:以同一分页 owner 和 findings 域读取保存证据。复用同一失效逻辑,历史与 findings 仍各自限定游标。
对主干的风险 / Risks
PASS:正常状态在历史读取仍被挂起时从阻塞变成先返回 200;同一历史仍有 43 条、身份相同。原始来源真实不可用仍拒绝,恢复后游标重读 43 条原记录。 原生局部套件 38 项通过。同一注册和日志走原生 CLI;用 event 控制已经完成日志 IO 后的返回顺序,HTTP、分页器及授权校验是真实实现。
FAIL:同一读请求已经取得 43 条原始记录。基线先返回合法历史页 200,错误选择随后得 404;新提交让错误选择先得 404,也把合法历史页从 200 变成了 404。 把纯 Goal selector 校验与会作废页面缓存的来源复核分开:请求越界只拒绝该请求,原始来源真正失效时仍拒绝该页。
Quick premerge 的六项直接检查通过,语义 smoke 初次缺本地 TypeScript,补齐同锁文件依赖后单独重跑通过。严格质量凭据仍为 stale,不能把原整组 premerge 标为通过。 UNVERIFIED:远端 CI 未查询。未宣称已安装 UI 或 SSH/容器吞吐资格;远端 CI 未查询。当前质量凭据与评审基线的差异独立保留。
语义与 CI 对齐
复用既有类型和边界:既有原始来源/只读边界;请求参数校验和来源失效是两个不同原因。 状态请求先完成是有意行为;合法分页因其他无效 selector 返回 404 是回归。 修复后用相同的原生入口复跑:python -m loopx.presentation.runtime_workspace over a disposable source; GET /api/goal-source/history and GET /status.json。
我的整体评价 / Verdict
连续浏览的可靠性回归:一个无效请求会把另一条正在取数的历史读取赶出正常路径,用户只能重新加载。 正在加载的合法历史页会错误显示来源不可用;保留正常状态读取提速的同时应消除这个跨请求误报。 仅 Python 读适配器和 HTTP 测试变更,没有新增或改变可见页面布局;用原生 HTTP 验证交互状态。 沿用现有 GoalSourceReader 和分页缓存,只分离锁与失效原因;不增设缓存所有者、重试状态或更改工作区入口。 并非分阶段 RFC;采用了从普通任务、真实错误到恢复读回的查验思路,未引入历史架构判词。 当前提交需要修改后再复核。
English verdict: REQUEST_CHANGES. Head 8fb535b. An invalid Goal selector makes an already-authorized history response return 404. Native base/head HTTP replay reproduced the regression and verified correct failure when the source really goes offline.
Signed-off-by: LoopX Agent <337587101+loopx-agent@users.noreply.github.com>
…y-read-isolation-20261109 Signed-off-by: LoopX Agent <337587101+loopx-agent@users.noreply.github.com>
Problem
When you open a saved analysis while the history of a linked Goal is loading, both requests use the same source reader. The reader held its status lock through the history fetch, so a slow page could also stall the analysis and cause a second native status read after the cache expired.
Change
History and findings keep their existing pagination and source checks, with page I/O serialized separately from status and saved analysis. A real source failure still invalidates a page in progress. An empty or out-of-scope Goal selector is rejected before it can invalidate another request's history.
This is a change to the existing Python source I/O adapter. The TypeScript presentation contract, original Goal identity and cursor format are unchanged.
Validation