From 10c60ed8d1ba42877975ae7b264baeaa706719f3 Mon Sep 17 00:00:00 2001 From: huangruiteng <14976749+huangruiteng@users.noreply.github.com> Date: Wed, 16 Sep 2026 22:49:50 +0800 Subject: [PATCH] test(chat): patch the refresh-state rules where they now live #4521 moved the refresh-state parser, rules and dispatch into `loopx/cli_commands/project_lifecycle_refresh_state.py` and deliberately left no re-export behind, so `project_lifecycle` no longer carries `refresh_state_run`, `sync_explore_graph_after_material_refresh`, `sync_human_gate_after_refresh`, `read_heartbeat_settlement`, `settlement_result_payload` or `resolve_runtime_root`. The nine goal-channel tests still patched those names on the dispatching module, so they failed with AttributeError on main even though the behaviour under test was intact. Each patch now targets the owning module and the dispatch entry (`handle_project_lifecycle_command`) is unchanged, so the tests keep asserting the same contract: the delivery postcondition decides ok/exit, external sinks are suppressed end to end, a disabled post-writeback hook performs zero projection calls, the sidecar dispatch replays once, exception detail stays redacted, and NaN/Infinity usage JSON never reaches a refresh. No assertion, fixture or production module changed. Verified: pytest tests/cli_commands tests/test_project_lifecycle_refresh_state_ownership.py tests/canary -q -> 123 passed. Signed-off-by: huangruiteng <14976749+huangruiteng@users.noreply.github.com> --- .../test_project_lifecycle_goal_channel.py | 45 ++++++++++--------- 1 file changed, 25 insertions(+), 20 deletions(-) diff --git a/tests/cli_commands/test_project_lifecycle_goal_channel.py b/tests/cli_commands/test_project_lifecycle_goal_channel.py index 21a3de412c..9170ea3c29 100644 --- a/tests/cli_commands/test_project_lifecycle_goal_channel.py +++ b/tests/cli_commands/test_project_lifecycle_goal_channel.py @@ -9,7 +9,12 @@ import pytest -from loopx.cli_commands import project_lifecycle +# The refresh-state rules live in their own module (project_lifecycle only +# dispatches to them), so patches must target the owning module. +from loopx.cli_commands import ( + project_lifecycle, + project_lifecycle_refresh_state as refresh_state_command, +) from loopx.control_plane.capability_hooks import ( POST_WRITEBACK_HOOK_RESULT_SCHEMA_VERSION, PostWritebackHookRegistration, @@ -98,7 +103,7 @@ def test_refresh_state_applies_goal_channel_delivery_postcondition( ) -> None: captured: dict[str, Any] = {} monkeypatch.setattr( - project_lifecycle, + refresh_state_command, "refresh_state_run", lambda **kwargs: { "ok": True, @@ -108,7 +113,7 @@ def test_refresh_state_applies_goal_channel_delivery_postcondition( }, ) monkeypatch.setattr( - project_lifecycle, + refresh_state_command, "sync_explore_graph_after_material_refresh", lambda **kwargs: { "enabled": False, @@ -119,7 +124,7 @@ def test_refresh_state_applies_goal_channel_delivery_postcondition( }, ) monkeypatch.setattr( - project_lifecycle, + refresh_state_command, "sync_human_gate_after_refresh", lambda **kwargs: gate_sync, ) @@ -147,7 +152,7 @@ def test_refresh_state_forwards_external_sink_suppression( ) -> None: captured_kwargs: dict[str, Any] = {} monkeypatch.setattr( - project_lifecycle, + refresh_state_command, "refresh_state_run", lambda **kwargs: { "ok": True, @@ -157,7 +162,7 @@ def test_refresh_state_forwards_external_sink_suppression( }, ) monkeypatch.setattr( - project_lifecycle, + refresh_state_command, "sync_explore_graph_after_material_refresh", lambda **kwargs: { "enabled": False, @@ -181,7 +186,7 @@ def sync_gate(**kwargs: Any) -> dict[str, Any]: } monkeypatch.setattr( - project_lifecycle, + refresh_state_command, "sync_human_gate_after_refresh", sync_gate, ) @@ -214,7 +219,7 @@ def _record_refresh(**kwargs: Any) -> dict[str, Any]: refresh_calls.append(kwargs) return {"ok": True, "appended": True, "dry_run": False} - monkeypatch.setattr(project_lifecycle, "refresh_state_run", _record_refresh) + monkeypatch.setattr(refresh_state_command, "refresh_state_run", _record_refresh) args = _args() args.usage_json = ( @@ -262,7 +267,7 @@ def test_refresh_state_redacts_goal_channel_exception_details( ) ) monkeypatch.setattr( - project_lifecycle, + refresh_state_command, "refresh_state_run", lambda **kwargs: { "ok": True, @@ -272,7 +277,7 @@ def test_refresh_state_redacts_goal_channel_exception_details( }, ) monkeypatch.setattr( - project_lifecycle, + refresh_state_command, "sync_explore_graph_after_material_refresh", lambda **kwargs: { "enabled": False, @@ -290,7 +295,7 @@ def fail_with_private_details(**kwargs: Any) -> dict[str, Any]: ) monkeypatch.setattr( - project_lifecycle, + refresh_state_command, "sync_human_gate_after_refresh", fail_with_private_details, ) @@ -355,7 +360,7 @@ def producer(value: Mapping[str, Any]) -> dict[str, object]: producer=producer, ) monkeypatch.setattr( - project_lifecycle, + refresh_state_command, "refresh_state_run", lambda **kwargs: { "ok": True, @@ -372,29 +377,29 @@ def producer(value: Mapping[str, Any]) -> dict[str, object]: }, ) monkeypatch.setattr( - project_lifecycle, + refresh_state_command, "read_heartbeat_settlement", lambda *args, **kwargs: SimpleNamespace( delivery=SimpleNamespace(failure=None) ), ) monkeypatch.setattr( - project_lifecycle, + refresh_state_command, "settlement_result_payload", lambda result: {"status": "settled"}, ) monkeypatch.setattr( - project_lifecycle, + refresh_state_command, "resolve_runtime_root", lambda *args, **kwargs: runtime_root, ) monkeypatch.setattr( - project_lifecycle, + refresh_state_command, "sync_explore_graph_after_material_refresh", lambda **kwargs: {"enabled": False}, ) monkeypatch.setattr( - project_lifecycle, + refresh_state_command, "sync_human_gate_after_refresh", lambda **kwargs: {"enabled": False}, ) @@ -438,7 +443,7 @@ def projection(**kwargs: Any) -> dict[str, object]: return {} monkeypatch.setattr( - project_lifecycle, + refresh_state_command, "refresh_state_run", lambda **kwargs: { "ok": True, @@ -448,12 +453,12 @@ def projection(**kwargs: Any) -> dict[str, object]: }, ) monkeypatch.setattr( - project_lifecycle, + refresh_state_command, "sync_explore_graph_after_material_refresh", lambda **kwargs: {"enabled": False}, ) monkeypatch.setattr( - project_lifecycle, + refresh_state_command, "sync_human_gate_after_refresh", lambda **kwargs: {"enabled": False}, )