From 4c4680d4469e4ec06b08a8310fca26a64b38f8ea Mon Sep 17 00:00:00 2001 From: BigDataDZ <76271875+BigDataDZ@users.noreply.github.com> Date: Sat, 24 Oct 2026 15:01:33 +0800 Subject: [PATCH 1/2] fix(codex): read activity from relative configured homes Signed-off-by: BigDataDZ <76271875+BigDataDZ@users.noreply.github.com> --- .github/workflows/python-tests.yml | 1 + loopx/codex_app_thread_activity.py | 7 +++++-- tests/test_host_thread_activity.py | 19 +++++++++++++++++++ 3 files changed, 25 insertions(+), 2 deletions(-) diff --git a/.github/workflows/python-tests.yml b/.github/workflows/python-tests.yml index 9a221bf7f9..f6613c0f02 100644 --- a/.github/workflows/python-tests.yml +++ b/.github/workflows/python-tests.yml @@ -893,6 +893,7 @@ jobs: tests/test_doctor_install_freshness.py tests/test_file_lock.py tests/test_file_lock_cross_process.py + tests/test_host_thread_activity.py tests/control_plane/test_effect_runtime_integration.py tests/control_plane/test_shadow_observation_retirement.py tests/test_python_install_owner_platform.py diff --git a/loopx/codex_app_thread_activity.py b/loopx/codex_app_thread_activity.py index 92bc39d4a6..e46a6804d4 100644 --- a/loopx/codex_app_thread_activity.py +++ b/loopx/codex_app_thread_activity.py @@ -64,6 +64,7 @@ def codex_homes(env: Mapping[str, str] | None = None, user_home: Path | None = N ``LOOPX_CODEX_HOMES`` (``os.pathsep``-separated) is used verbatim when set. Otherwise ``CODEX_HOME``, ``~/.codex`` and sibling ``~/.codex-*`` homes are searched, because Codex App installs can each keep their own home. + Relative entries use the process working directory when their store is read. """ env = os.environ if env is None else env @@ -108,14 +109,16 @@ def _query_threads(uri: str, thread_ids: list[str]) -> dict[str, tuple[Any, Any] def _thread_rows(db: Path, thread_ids: list[str]) -> dict[str, tuple[Any, Any]]: + # Configured homes may be relative; SQLite file URIs require an absolute path. + uri = db.resolve().as_uri() try: - return _query_threads(f"{db.as_uri()}?mode=ro", thread_ids) + return _query_threads(f"{uri}?mode=ro", thread_ids) except sqlite3.OperationalError: # A WAL store whose -shm is absent cannot be read with mode=ro. # Without a -wal file the main file is the whole store. if Path(f"{db}-wal").exists(): raise - return _query_threads(f"{db.as_uri()}?immutable=1", thread_ids) + return _query_threads(f"{uri}?immutable=1", thread_ids) def current_codex_execution_identity( diff --git a/tests/test_host_thread_activity.py b/tests/test_host_thread_activity.py index 389f67800e..f6bc5cdd1c 100644 --- a/tests/test_host_thread_activity.py +++ b/tests/test_host_thread_activity.py @@ -78,6 +78,25 @@ def _observe(home: CodexHome, *thread_ids: str) -> dict[str, HostThreadActivity] return observe_codex_threads(thread_ids, homes=[home.root]) +@pytest.mark.parametrize("configuration", ["explicit", "CODEX_HOME", "LOOPX_CODEX_HOMES"]) +def test_relative_codex_home_recovers_activity_without_writing_the_store( + tmp_path, monkeypatch, configuration, +): + selected = CodexHome(tmp_path / "project # 中文" / ".codex") + rollout = selected.thread("relative-thread", [_event(T2, "task_complete")]) + before = {path: path.read_bytes() for path in (selected.db, rollout)} + monkeypatch.chdir(tmp_path) + relative = selected.root.relative_to(tmp_path) + homes = [relative] if configuration == "explicit" else codex_homes( + env={configuration: str(relative)}, user_home=tmp_path / "synthetic-user", + ) + assert homes[0] == relative + result = observe_codex_threads(["relative-thread"], homes=homes)["relative-thread"] + assert result.state is HostThreadState.IDLE + assert result.last_turn_ended_at == T2 + assert all(path.read_bytes() == content for path, content in before.items()) + + def test_current_execution_reads_the_bound_home_only_and_recovers_without_preferences(home, tmp_path): other = CodexHome(tmp_path / ".codex-other") meta = {"timestamp": T0, "type": "session_meta", "payload": {"id": "t", "model_provider": "openai"}} From e4a267dde88737c0c5114a9329df00bcec0761f3 Mon Sep 17 00:00:00 2001 From: BigDataDZ <76271875+BigDataDZ@users.noreply.github.com> Date: Sat, 24 Oct 2026 15:08:16 +0800 Subject: [PATCH 2/2] fix(codex): preserve path aliases when forming store URIs Signed-off-by: BigDataDZ <76271875+BigDataDZ@users.noreply.github.com> --- loopx/codex_app_thread_activity.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/loopx/codex_app_thread_activity.py b/loopx/codex_app_thread_activity.py index e46a6804d4..f7af51b876 100644 --- a/loopx/codex_app_thread_activity.py +++ b/loopx/codex_app_thread_activity.py @@ -110,7 +110,7 @@ def _query_threads(uri: str, thread_ids: list[str]) -> dict[str, tuple[Any, Any] def _thread_rows(db: Path, thread_ids: list[str]) -> dict[str, tuple[Any, Any]]: # Configured homes may be relative; SQLite file URIs require an absolute path. - uri = db.resolve().as_uri() + uri = db.absolute().as_uri() try: return _query_threads(f"{uri}?mode=ro", thread_ids) except sqlite3.OperationalError: