Skip to content

fix(runtime): prevent cross-namespace fixture shutdown - #6718

Closed
loopx-agent wants to merge 2 commits into
mainfrom
codex/runtime-namespace-ownership-20261017
Closed

loopx-agent wants to merge 2 commits into
mainfrom
codex/runtime-namespace-ownership-20261017

Conversation

@loopx-agent

@loopx-agent loopx-agent commented Oct 17, 2026 •

Copy link
Copy Markdown
Collaborator

Disposition: closed as an alternative source proposal

The automatic-cleanup safety outcome is now implemented on main by #6720, using the runtime server's authenticated frozen publication path. That merged change also repairs the departing-reader case observed in this proposal's older full run. Current main-based regression batches exercise the real owned/foreign source and copied-package cleanup plus HTTP reader lifecycle: 119 passed on macOS and the same 119 passed in an independent native Linux ARM64 profile.

I am closing this unmerged author proposal instead of maintaining a second overlapping shutdown mechanism. This is an outcome-based supersession, not a claim that the two patches are identical: the extra optional shutdown-guard wire parameter proposed here is retired. The branch, exact head and review history remain available. #6730 owns the remaining repository-invariant/test-isolation source repair; original timeout failures, complete hosted CI, installed behavior and sustained acceptance remain open.

中文:主干已通过 #6720 实现原来的自动清理归属保护,并修复断开读者的释放问题;这里关闭尚未合并的替代方案,停止重复维护额外 shutdown 参数。保留分支及评审历史;这不代表原有完整 CI、超时或持续验收已经完成。


Goal And Delivered Outcome

An authentic runtime locator copied into a test directory authenticated the original server, so global pytest teardown could shut down a different namespace. This addresses the confirmed finding on #6688, adopted through #6697.

The serving TypeScript runtime now confirms its canonical startup locator before automatic cleanup and checks it again at shutdown. Pytest and canary use the same existing shutdown bridge. A borrowed locator preserves the original process; owned source and copied-package runtimes still drain and actually exit.

Author Declaration

  • Written by: OpenAI Codex model agent.
  • Basis: the reproduced public review finding above and the existing process-isolation/retirement contract in docs/development/testing-and-quality.md at 555a83df107c4711de49ee5451af5e34f4fff558, corrected in this proposal.
Criterion Disposition Owner / decisive validation
Identity alone cannot authorize namespace cleanup implemented TS serving context; ordinary-copy global pytest regression, both retention policies
Owned source/copied packages exit after success and failure implemented Existing teardown and real process-exit regressions
Drain, cache retirement and normal operator restart retain their behavior implemented Runtime integration/restart/cache suites and default ping/shutdown parity
Missing or malformed ownership proof sends no cleanup effect implemented Adapter and TS negative tests; canary retains its failed fixture

Scope And Continuation

This completes the bounded source repair. The broader full-Python qualification and sustained-operation work remains open; this PR does not certify the public Time Machine, an installed release, or full CI.

expected_info_path and owns_locator extend the existing authenticated internal runtime contract. No new capability, configuration, frontend/Lark operation or parallel Python ownership rule is introduced. Ordinary requests without the guard retain their wire behavior. Automatic cleanup now requires positive server proof: an older runtime that ignores the parameter is not shut down. Canary reports failure and retains the fixture; pytest ignores borrowed/unproven records. This intentional safety change is documented in English and Chinese.

The related refactor consolidates fixture-local ping validation into the existing shutdown bridge. The ownership probe keeps its one-second bound, and all three cleanup phases share the existing canary deadline. Test/resource selection and timeout policies are unchanged.

Validation

  • Author revision: 73230074f73109fc02c2e15c3fb65fb537ca1fd9; source baseline 555a83df107c4711de49ee5451af5e34f4fff558.
  • Native hosted checkout: merge 6370f1b7951d821474847f56f198910f10b4fb0b (base 937a011d9d507e499697046bcd86b9db0a925461 + author head); tree 4ad67ce33678a538bceb8307f75df76be6dbbded.
  • Completed natural run 39008021039, attempt 1, read at 2026-10-17T10:37:36Z: shards 1/3/5/6 failed, 2/4 exceeded the existing 60-minute ceiling, and the pytest / final required gates failed. Full hosted and sustained qualification remain unproved.
  • Input classes: synthetic, public_fixture. No active Goal/store was used as a test fixture.
Check kind Result Evidence / limitation
regression_parity passed Both retention cases of the ordinary-copy global pytest regression fail before the fix. After it, the original authenticated PID remains usable; direct guarded shutdown is independently refused.
integration / real_backend passed Nine complete macOS runtime/fixture modules: 189 passed, 0 failures/errors/skips on the author source. Real File/SQLite (Node v24.21.0, SQLite 3.53.4, Python 3.11.17), disposable synthetic stores/process namespaces, source/copied-package success/failure and both retention policies.
unit / static passed Three complete local TS runtime modules: 35 passed, no failures/skips; configured control-plane typecheck, Ruff and configured mypy (17 files).
static / real_entrypoint passed Diff/semantic checks and native premerge 6 direct + 14 selected commands, no command failures; all nine public candidate paths scanned. Overall CQR remains FAIL for required unqualified Linux Python/full hosted acceptance.
integration / native Windows passed within named batch Windows job 117326793040 on the merged checkout above: Python 3.11.9, Node v24.21.0, lifecycle 203 passed / 19 skipped in 212.97s. Skip identities are not reported. The batch includes existing runtime integration and the global fixture, but excludes the new fixture-negative module. Later steps switch to Node v22.22.3: SQLite CLI 9 passed, primary TS 448 passed, separate TS group 252 passed / 28 skipped. These explicit Windows TS batches exclude the new handler-negative module.
unit / native Linux TS passed within named shard TS shard 3 job 117326793076 on the same merged checkout, Node v22.22.3: 1556 passed / 34 skipped / 0 failed. All three new locator-proof, shutdown-recheck and malformed-guard tests explicitly passed. This does not qualify Linux Python fixture processes or the other TS shards.
real_backend / unchanged provider passed within provider boundary PostgreSQL job 117324370487, same merged checkout: actual server startup PostgreSQL 16.15, client pg 8.23.0, Node v22.22.3; store_direct conformance 365 passed, typed service module 11 passed, no failures/skips. Provider/service qualification does not cover runtime transport, fallback or sustained operation; the service count is not eleven real end-to-end scenarios.
integration / local Linux Python failed, then setup blocked Initial incomplete environment: 10 failed / 16 passed, retained. Follow-up native-tools and editable-dependency setups timed out before tests. The cached full-image route also paired a default amd64 image with an arm64 Node archive; it was discarded before Node/tests executed. Exact frozen dependencies are unavailable offline. All owned containers were removed. No Linux Python pass is claimed.
real_entrypoint / full qualification failed / incomplete Python run 39008021039, attempt 1: shards 1/3 have the existing lock/module-budget failures. Shard 5 reports census teardown/next-setup errors and CLI preview-home contamination; shard 6 reports the departing-reader assertion. Shards 2/4 have provider-confirmed 60-minute cancellations, with no JUnit and no proven causal stack. pytest and merge-gate failed; combined coverage and sustained qualification remain unproved. No manual dispatch/comparison run.

The executed merge is not byte-identical to the whole author tree: seven proposal files match, while the Python bridge and TS server each add the base's existing runtime.checkpoint_artifacts snapshot whitelist entry. There are seventeen total author-to-merge changed paths. The newer review base 73aa2bc8946f3d5e0fb5600580dfa233427eb462 also adds an adjacent snapshot method/handler and other product changes; the hosted results above do not qualify that newer composition. The fresh change-quality record remains FAIL. Required CI and independent exact-head review still apply; maintainer merge authority remains separate.

The two deterministic repository failures also reproduce on the immutable author baseline and the later main baseline. Follow-up #6730 repairs those baseline invariants and places the same checks before PR long-test admission; it keeps the full six-shard collection and deadlines. That separate source proposal does not resolve the two timeout causes or qualify this PR's full hosted/installed/sustained behavior. This head also has a published exact-head COMMENTED review with an APPROVE conclusion; it is not a GitHub formal approval. The independent review's same-base catalog-fixture ENOENT remains a red native premerge observation, separate from the earlier author validation above.

Frontend / Visual Evidence

UI impact: none. This changes existing internal lifecycle/pytest/canary consumers; no product entrypoint or presentation changes.

Boundary Checklist

  • Public-safe source, docs and synthetic regressions only; no private state, raw traces, credentials or local machine paths.
  • Two focused DCO-signed commits; explicit path staging and clean worktree.
  • No paired RFC change; bilingual cleanup guidance updated together.
  • No benchmark job, resource/billing change, merge, deployment or installation.

中文:修复“合法定位文件副本可导致跨隔离目录误关进程”的 P1;归属由真实 TypeScript 服务端确认,pytest 与 canary 共用受保护的关闭入口。本地验证与 Linux/完整 CI/持续验收分别披露,后者仍保留。

Signed-off-by: LoopX Agent <337587101+loopx-agent@users.noreply.github.com>
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

Approval conclusion (author-owned PR; GitHub blocks formal self-approval)

Exact head: 73230074f73109fc02c2e15c3fb65fb537ca1fd9;不可变基线:555a83df107c4711de49ee5451af5e34f4fff558。这是完整当前提交的独立审查,没有读取、轮询或等待 GitHub CI;作者正文里的托管环境结果没有计作本次独立证据。

动机

运行测试或 canary 检查的开发者需要在检查结束后关闭自己创建的后台进程,同时保留其他工作区仍在使用的进程。 原来把一份合法的进程定位文件复制进临时测试目录,就可能让自动清理误关原工作区;现在服务端先确认请求指向自己启动时的真实定位文件,并在关闭动作前再次确认,副本只能被拒绝。 本次在真实隔离进程上验证了副本无法关闭原进程,而自有源码和复制包进程在成功、失败及两种保留策略下仍会排空并退出。 本次修复限定于源码中的自动清理归属边界,不宣告完整 CI、安装版、持续运行或其他产品验收已经完成。 标准 canary 中一项基线已有的目录夹具失败仍保留;完整托管环境、Windows 新增 Python 负例和持续验收尚未由本次独立验证。

改动思路

现有 TypeScript 生命周期 owner 在副作用前维护隔离不变量;Python 只适配证明并复用关闭桥接,没有新增平行决策源。当前 PR 交付现有 pytest/canary 调用链上的完整源码清理修复;完整托管、安装、持续资格与无关目录夹具修复仍在原验收边界。这里要保护的是“自动清理只能处理自己的临时进程”。合法 token、PID 和 fingerprint 能证明连接的是哪一个进程,无法证明复制文件的目录拥有它。现有 TypeScript 服务端启动时已有真实定位文件,因此把归属判断留在这个生命周期 owner;Python 负责传输、核对正向证明和观察进程退出。关闭动作再次检查同一绑定,避免把一次早先 ping 当成关闭授权。pytest 复用原有关闭桥接并删除重复 ping 逻辑,canary 复用既有截止时间。这个边界属于必须成立的隔离不变量;不需要新增能力、开关、状态库或前端配置。当前 PR 交付现有真实调用链上的源码清理修复,更广完整/安装/持续资格与无关目录夹具修复保留给原 owner。此处采用文字分支说明,未引入未经渲染检查的 Mermaid。

具体改动

关键代码讲解

  1. runtimeLocatorMatch 从服务端上下文比较 canonical 路径;参数缺席保留旧调用,参数出现则严格校验非空字符串。ping 只投影 owns_locator;shutdown 在请求副作用前再检查,不匹配返回 stopped=false。
  2. server.listen 发布回调 在发布前捕获真实父目录加文件名。证明来自实际进程启动事实,不来自可复制的定位文件内容,也不会因测试目录稍后删除就重新解释归属。
  3. _stop_effect_runtime 有 guard 时先要求 ready、PID、fingerprint 和严格为 true 的归属证明;旧进程忽略新参数也不得进入 shutdown。随后把 guard 送入关闭请求,沿用实际 OS 退出观察。没有 guard 的操作员重启保持原来的完整 wire/返回行为。
  4. _retire_fixture_runtimes 延续非符号链接和指纹约束,把剩余截止时间分给探测、关闭和退出三段,不能通过多一次探测延长原清理预算。失败保留 canary 夹具;全局 pytest 夹具使用同一桥接。

共九个路径、+254/-27;生产逻辑集中在现有四个 owner 文件,其余是两种语言的安全说明和真实进程负例。规范依据为修复前 testing-and-quality.md(555a83df107c4711de49ee5451af5e34f4fff558)及 #6688 已确认的普通文件副本反例。验收映射:归属边界:implemented,不可变基线在两种 retention 下均失败,当前提交保留原进程;失败恢复:implemented,自有源码/复制包在成功及失败后真实排空退出,错误 guard 后也能由原定位文件完成合法关闭;兼容边界:implemented,完整无 guard TS 返回/副作用及 Python 参数/返回对象与基线相等;更广验收:out_of_scope,没有借本次源码修复宣布完整托管、安装或持续验收完成。

对主干的风险

必须同时防止误关外部进程和过度阻塞自有清理。本次独立运行 macOS 十个完整模块 221 passed、0 skipped,现有真实 Linux 环境六个完整模块 161 passed、0 skipped;七个 TS runtime 模块 55 passed、0 skipped,typecheck、Ruff、先 advisory 后完整语义检查、双语检查均通过。负例包含合法普通文件副本、错误 PID/符号链接、缺失/false/非法证明以及直接 guarded shutdown;不仅检查伪造数据,也检查原进程仍能响应,并随后合法退出。完整对象 parity 的 factory/adapter 探针与真实网络/进程证据分别记账,不把 mock 当后端验证。

标准 native premerge 仍为失败:6 个直接检查+14 个选中命令,其中两处选择到同一个 examples/canary/catalog-run-e2e-smoke.py 失败。 该 smoke 在空临时目录期待 git-required skip,却实际尝试执行不存在的 examples/control_plane/control-plane-maintainability-ratchet-smoke.py,内层退出码 2/ENOENT,外层退出码 1;不是清理 guard 的失败。相同命令在不可变基线复现同一检查身份与缺文件细节,e2e 文件 blob 完全相同;变更也未触及这一跳过/选择路径。故按 pre_existing_unrelated 归因,保留红检查与原验证 owner,未放松任何限额或把 premerge 报绿。Linux 首次自建探针遗漏子进程源码路径的 5 failed/156 passed 也保留;独立 import 探针区分该环境问题后,带明确父子源码来源的六模块完整运行全部 161 例通过,覆盖首次失败用例。当前测试不等于复原作者的冻结托管依赖。

语义与 CI 对齐

复用既有 runtime 词汇并扩展内部请求参数,没有新增枚举、字符串黑名单或平行 Python 决策源。自动清理的默认行为有意收紧:老 runtime 不返回正向证明时不自动关闭;这是机器约束,双语文档已披露,不能称为仅供参考的 guidance。现有不带 guard 的操作员路径完整输出和副作用保持一致。没有新增可发现的用户能力或 Lark/前端设置;无需为了内部隔离修复追加配置界面。远程 CI 不在本次审查证据源内。

我的整体评价

APPROVE,交付判断为 justified_increment。 long_horizon=preserved:真实排空、退出、失败保留与重启验证覆盖受影响的有限生命周期,不把单次成功当持续资格;user_experience=improved:现有自动检查避免中断无关工作,不增加人的准备、确认或重复输入。机制与原 P1 风险相称;未来重构检查已经在本 PR 内把夹具重复校验合并进现有桥接,进一步建框架没有当前调用价值。副本身份不再能冒充归属,合法清理和无 guard 兼容均有相反方向证据。以上是代码审查认可;无关红 canary、完整托管/Windows 新 Python 负例/安装/持续验收及维护者合并授权分别保留,未执行 bypass、合并、安装或资源动作。

English verdict: APPROVE — 73230074f73109fc02c2e15c3fb65fb537ca1fd9. Independent real macOS/Linux lifecycle tests, exact-base borrowed-locator negatives and full unguarded contract probes support this bounded source repair. Native premerge remains red for the same immutable-base catalog-fixture ENOENT; broader hosted, installed and sustained qualification and merge authority remain separate.

@mergify

mergify Bot commented Oct 17, 2026

Copy link
Copy Markdown

This pull request has merge conflicts with main and cannot be merged
until they are resolved. Please rebase or merge the base branch, @loopx-agent.

Choose the remote for the base repository, not an out-of-date fork.
For a fork clone, first inspect git remote -v; upstream must point
to https://github.com/loopx-project/loopx.git. If it is absent, add it
with git remote add upstream https://github.com/loopx-project/loopx.git.
Then run:

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 HEAD

For a same-repository clone whose origin points to
https://github.com/loopx-project/loopx.git, use origin instead of
upstream for fetch/rebase. If you prefer merging the base, use
git merge <base-remote>/main and push normally.

Keep the DCO Signed-off-by trailer on every commit when you rebase.
https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/syncing-a-fork

@mergify mergify Bot added the needs-rebase Mergify: the pull request has merge conflicts with its base branch label Oct 17, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs-rebase Mergify: the pull request has merge conflicts with its base branch

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant