From 7ada9dc1c632f76a93d81ac815b35588232976d0 Mon Sep 17 00:00:00 2001 From: LoopX Agent <337587101+loopx-agent@users.noreply.github.com> Date: Sat, 17 Oct 2026 17:47:57 +0800 Subject: [PATCH 1/4] refactor(chat): colocate turn wakeups with native chat Signed-off-by: LoopX Agent <337587101+loopx-agent@users.noreply.github.com> --- .../native_chat/turn_release.py} | 0 loopx/chat_runtime.py | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename loopx/{chat_turn_release.py => capabilities/native_chat/turn_release.py} (100%) diff --git a/loopx/chat_turn_release.py b/loopx/capabilities/native_chat/turn_release.py similarity index 100% rename from loopx/chat_turn_release.py rename to loopx/capabilities/native_chat/turn_release.py diff --git a/loopx/chat_runtime.py b/loopx/chat_runtime.py index a9e775a5c8..0aa496ae5f 100644 --- a/loopx/chat_runtime.py +++ b/loopx/chat_runtime.py @@ -44,7 +44,7 @@ from .chat_dsh import DshChatAdapter from .chat_endpoint_catalog import builtin_chat_endpoints from .chat_endpoints import AgentEndpointRegistry -from .chat_turn_release import TurnReleaseWakeup +from .capabilities.native_chat.turn_release import TurnReleaseWakeup from .control_plane.turn_driver.host_binding import MANAGED_TURN_HOST from .control_plane.turn_driver.execution_profile import managed_execution_profile from .kiro_cli_goal_mode import ( From b109110efdd0fbad19b1419477b0b762dae7bba5 Mon Sep 17 00:00:00 2001 From: LoopX Agent <337587101+loopx-agent@users.noreply.github.com> Date: Sat, 17 Oct 2026 17:47:57 +0800 Subject: [PATCH 2/4] fix(ci): sync the Stage 2C MCP hash pin Signed-off-by: LoopX Agent <337587101+loopx-agent@users.noreply.github.com> --- tests/requirements-stage2c-linux-py311.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/requirements-stage2c-linux-py311.txt b/tests/requirements-stage2c-linux-py311.txt index e1413654d9..b534e2cba3 100644 --- a/tests/requirements-stage2c-linux-py311.txt +++ b/tests/requirements-stage2c-linux-py311.txt @@ -670,9 +670,9 @@ librt==0.15.0 \ --hash=sha256:fc1ed11c4ad0b91af24def2050f2840ea4567828e3dd058fbe608d982f6e5465 \ --hash=sha256:febb1ce6cac545a54e6b769982824e955a700fdd9fbf3a08a3d82c990968b57d # via mypy -mcp==1.28.1 \ - --hash=sha256:2726bca5e7193f61c5dde8b12500a6de2d9acf6d1a1c0be9e8c2e706437991df \ - --hash=sha256:d51e36a5f5644faea4f85ea649bfffa6bc6c26770d42798ad6a3de3d2ba69683 +mcp==1.30.0 \ + --hash=sha256:445414625fce5c295faa505bb11bacece661ab6f4028d57c935db57820b7a3e4 \ + --hash=sha256:666edb5009503e1047c9d60346a756f94b261f05cc2625f23d41c728ffc484d0 # via loopx (pyproject.toml) mypy==1.20.2 \ --hash=sha256:0c64e5973df366b747646fc98da921f9d6eba9716d57d1db94a83c026a08e0fb \ From d9192d8ff9586bc0f39b4fc184e2ab82c2f7949d Mon Sep 17 00:00:00 2001 From: LoopX Agent <337587101+loopx-agent@users.noreply.github.com> Date: Sat, 17 Oct 2026 17:55:20 +0800 Subject: [PATCH 3/4] ci: check repository invariants before long test admission Signed-off-by: LoopX Agent <337587101+loopx-agent@users.noreply.github.com> --- .github/workflows/python-tests.yml | 6 ++++++ docs/development/testing-and-quality.md | 9 +++++++++ 2 files changed, 15 insertions(+) diff --git a/.github/workflows/python-tests.yml b/.github/workflows/python-tests.yml index 173414ba10..365fea73f1 100644 --- a/.github/workflows/python-tests.yml +++ b/.github/workflows/python-tests.yml @@ -225,6 +225,12 @@ jobs: - name: Install test dependencies run: python -m pip install --disable-pip-version-check -e ".[test]" + - name: Check repository invariants before long tests + run: >- + python -m pytest -q --no-cov + tests/test_stage2c_dependency_lock.py + tests/architecture/test_top_level_module_budget.py + - name: Verify file authority on the core runtime run: >- node --no-warnings --experimental-strip-types --test diff --git a/docs/development/testing-and-quality.md b/docs/development/testing-and-quality.md index e4f7206de1..40acbe6915 100644 --- a/docs/development/testing-and-quality.md +++ b/docs/development/testing-and-quality.md @@ -707,10 +707,19 @@ pull requests. It intentionally excludes provider-backed evaluation and the full smoke catalog, so ordinary iteration does not depend on credentials, network latency, provider availability, or a two-hour matrix. +The existing `kernel-static-checks` job first checks the Stage 2C dependency +lock and top-level module budget. A failed invariant makes the existing +`checks` preflight fail, so PR long-test shards do not enter their shared queue. +These tests also remain in the complete six-shard collection. + `.github/workflows/python-tests.yml` 会在相关 Python PR 上运行这条快速通道。 它刻意不包含真实模型调用和 full smoke catalog,因此普通迭代不依赖凭证、网络 时延、模型服务可用性或两小时级测试矩阵。 +既有 `kernel-static-checks` 作业先检查 Stage 2C 依赖锁和顶层模块预算。不变量失败 +会使既有 `checks` 前置门失败,阻止 PR 的长测试分片进入共享队列。这些测试仍保留 +在六分片的完整 collection 中。 + The Linux suite uses six hosted runners with two xdist workers each. `pytest-split` partitions the complete collection using `least_duration`; without a timing file, tests have equal weight and alternate between shards. From 5a5af8553898803338df26028e5434257d12b044 Mon Sep 17 00:00:00 2001 From: LoopX Agent <337587101+loopx-agent@users.noreply.github.com> Date: Sat, 17 Oct 2026 18:45:34 +0800 Subject: [PATCH 4/4] test(ci): isolate fault injection and preview subprocess environments Signed-off-by: LoopX Agent <337587101+loopx-agent@users.noreply.github.com> --- ...st_chat_capabilities_route_single_owner.py | 13 ++++--- tests/test_cli_entrypoint.py | 37 +++++++++++++------ 2 files changed, 34 insertions(+), 16 deletions(-) diff --git a/tests/architecture/test_chat_capabilities_route_single_owner.py b/tests/architecture/test_chat_capabilities_route_single_owner.py index f2822f6154..e4f55a5c64 100644 --- a/tests/architecture/test_chat_capabilities_route_single_owner.py +++ b/tests/architecture/test_chat_capabilities_route_single_owner.py @@ -69,8 +69,10 @@ def guarded_scan(path): raise FileNotFoundError("dependency tree was concurrently replaced") return scan(path) - monkeypatch.setattr(os, "scandir", guarded_scan) - assert _web_sources(tmp_path) == [source] + # The fault belongs to the census, not pytest's later runtime cleanup scan. + with monkeypatch.context() as patch: + patch.setattr(os, "scandir", guarded_scan) + assert _web_sources(tmp_path) == [source] def test_web_census_does_not_hide_first_party_io_errors( @@ -82,9 +84,10 @@ def test_web_census_does_not_hide_first_party_io_errors( def inaccessible_source(path): raise PermissionError("first-party source is unreadable") - monkeypatch.setattr(os, "scandir", inaccessible_source) - with pytest.raises(PermissionError, match="first-party source"): - _web_sources(tmp_path) + with monkeypatch.context() as patch: + patch.setattr(os, "scandir", inaccessible_source) + with pytest.raises(PermissionError, match="first-party source"): + _web_sources(tmp_path) def _module_bindings(root: Path) -> dict[str, int]: diff --git a/tests/test_cli_entrypoint.py b/tests/test_cli_entrypoint.py index bde0ff23c7..7d11afb5ac 100644 --- a/tests/test_cli_entrypoint.py +++ b/tests/test_cli_entrypoint.py @@ -44,11 +44,13 @@ def test_machine_json_keeps_values_capture_and_human_renderer( assert captured == [original, original] -def run_isolated_script(script: str) -> subprocess.CompletedProcess[str]: +def run_isolated_script( + script: str, *, environment: dict[str, str] | None = None, +) -> subprocess.CompletedProcess[str]: return subprocess.run( [sys.executable, "-c", script], cwd=REPO_ROOT, - env={**os.environ, "PYTHONPATH": str(REPO_ROOT)}, + env={**os.environ, **(environment or {}), "PYTHONPATH": str(REPO_ROOT)}, text=True, capture_output=True, check=False, @@ -60,6 +62,7 @@ def run_cli_main( argv: list[str], *, forbidden_modules: tuple[str, ...] = (), + environment: dict[str, str] | None = None, ) -> subprocess.CompletedProcess[str]: script = f""" import sys @@ -75,7 +78,7 @@ def run_cli_main( assert forbidden_module not in sys.modules raise SystemExit(exit_code) """ - return run_isolated_script(script) + return run_isolated_script(script, environment=environment) def run_cli_batch(module: str, argv_cases: list[list[str]]) -> list[dict[str, object]]: @@ -537,17 +540,23 @@ def test_selected_task_lease_rejections_match_full_cli(tmp_path: Path, arguments @pytest.mark.parametrize("command", ["authority-archive", "extension", "slash-commands"]) def test_selected_admin_preview_matches_full_cli_without_writes( - tmp_path: Path, monkeypatch: pytest.MonkeyPatch, command: str, + tmp_path: Path, command: str, ) -> None: + parent_environment = os.environ.copy() registry, runtime_root = write_command_fixture(tmp_path) runtime_root.mkdir() host_home = tmp_path / "host-home" host_home.mkdir() - monkeypatch.setenv("HOME", str(host_home)) - monkeypatch.setenv("CODEX_HOME", str(host_home / ".codex")) - monkeypatch.setenv("LOOPX_RUNTIME_ROOT", str(runtime_root)) - monkeypatch.setenv("LOOPX_REGISTRY", str(registry)) - monkeypatch.setenv("LOOPX_USAGE_PING", "0") + # Only the CLI subprocesses use this home. Existing worker threads must not + # discover it through a temporary change to the pytest process environment. + cli_environment = { + "HOME": str(host_home), + "USERPROFILE": str(host_home), + "CODEX_HOME": str(host_home / ".codex"), + "LOOPX_RUNTIME_ROOT": str(runtime_root), + "LOOPX_REGISTRY": str(registry), + "LOOPX_USAGE_PING": "0", + } argv = ["--registry", str(registry), "--runtime-root", str(runtime_root), "--format", "json", command] if command == "authority-archive": @@ -559,12 +568,18 @@ def test_selected_admin_preview_matches_full_cli_without_writes( before = {str(path.relative_to(tmp_path)): path.read_bytes() for path in tmp_path.rglob("*") if path.is_file()} - selected = run_cli_main("loopx.entrypoint", argv, forbidden_modules=("loopx.cli",)) - full = run_cli_main("loopx.cli", argv) + selected = run_cli_main( + "loopx.entrypoint", argv, forbidden_modules=("loopx.cli",), + environment=cli_environment, + ) + full = run_cli_main("loopx.cli", argv, environment=cli_environment) assert selected.returncode == full.returncode == 0, (selected.stderr, full.stderr) assert selected.stdout == full.stdout assert selected.stderr == full.stderr == "" + assert {key: os.environ.get(key) for key in cli_environment} == { + key: parent_environment.get(key) for key in cli_environment + } assert {str(path.relative_to(tmp_path)): path.read_bytes() for path in tmp_path.rglob("*") if path.is_file()} == before assert not list(host_home.iterdir())