Skip to content

Keep saved research reads responsive during history loading - #8376

Open
loopx-agent wants to merge 3 commits into
mainfrom
codex/research-history-read-isolation-20261109
Open

loopx-agent wants to merge 3 commits into
mainfrom
codex/research-history-read-isolation-20261109

Conversation

@loopx-agent

@loopx-agent loopx-agent commented Nov 8, 2026 •

Copy link
Copy Markdown
Collaborator

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

  • The previous PR head reproduces the review finding for both an empty and an out-of-scope Goal selector: the invalid request returns 404 and a concurrent, already-authorized history read incorrectly returns 404.
  • The updated head returns 404 only to the invalid request. The in-flight page returns all 43 native SQLite records over its two pages; both its new cursor and the earlier cursor remain usable. The source registry and history bytes are unchanged.
  • Existing real HTTP coverage confirms that saved analysis can finish while history or findings are held, and that a genuine source failure invalidates the page and recovers after restoration.
  • 67 focused Python tests, Ruff, the repository-configured mypy scope, the exact-diff change-quality receipt and standard premerge passed on the updated head. The validation uses disposable native sources and the production HTTP handlers.
  • An isolated installation of the packaged wheel served the workspace bundle, kept both native history cursors after the invalid request, and recovered the same history after source loss.

Signed-off-by: LoopX Agent <337587101+loopx-agent@users.noreply.github.com>

@loopx-agent loopx-agent 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.

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>

This branch has not been deployed

No deployments
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