From 1897e143e5d78faa0acd75696245c240263f6871 Mon Sep 17 00:00:00 2001 From: "duanjialing.777" Date: Tue, 8 Sep 2026 13:30:03 +0800 Subject: [PATCH] fix(quota): preserve monitor-poll fence rejection Signed-off-by: duanjialing.777 --- loopx/cli_commands/quota.py | 11 ++++ .../test_split_root_todo_writeback_fence.py | 55 +++++++++++++++++++ 2 files changed, 66 insertions(+) diff --git a/loopx/cli_commands/quota.py b/loopx/cli_commands/quota.py index f1fe81d0fd..0a2f0fb234 100644 --- a/loopx/cli_commands/quota.py +++ b/loopx/cli_commands/quota.py @@ -42,6 +42,9 @@ render_existing_heartbeat_receipt_payload, ) from ..control_plane.quota.turn_envelope import build_turn_envelope +from ..control_plane.coordination.legacy_writer_fence import ( + LegacyCoordinationWriterFenced, +) from ..control_plane.effect_runtime import EffectRuntimeRejected from ..control_plane.scheduler.execution_context import ( GUIDED_START_TURN_RUNTIME_PROFILES, @@ -238,6 +241,14 @@ def _quota_failure_payload( ) if error.agent_id is not None: payload["agent_id"] = error.agent_id + elif isinstance(error, LegacyCoordinationWriterFenced): + payload.update( + { + "error_code": error.code, + "reason": str(error), + **error.payload, + } + ) if lock_timeout_fields: payload["recommended_action"] = "inspect the lock holder before retrying" if command == "monitor-poll": diff --git a/tests/control_plane/test_split_root_todo_writeback_fence.py b/tests/control_plane/test_split_root_todo_writeback_fence.py index 57f5215869..269f1049b3 100644 --- a/tests/control_plane/test_split_root_todo_writeback_fence.py +++ b/tests/control_plane/test_split_root_todo_writeback_fence.py @@ -12,6 +12,7 @@ import pytest +from loopx.cli import main from loopx.cli_commands.turn_todo_writeback import ( write_turn_repair_update, write_turn_validated_completion, @@ -239,6 +240,60 @@ def native(_method: str, request: dict[str, Any]) -> dict[str, Any]: assert OVERRIDE_POLL_HASH not in state.read_text(encoding="utf-8") +def test_quota_monitor_poll_cli_preserves_fence_rejection( + monkeypatch: pytest.MonkeyPatch, + tmp_path: Path, + capsys: pytest.CaptureFixture[str], +) -> None: + registry, state, _runtime_registry, runtime_override = ( + _write_split_root_goal(tmp_path) + ) + _engage_fence_at(runtime_override) + _fence_check_blocks(monkeypatch) + before = state.read_bytes() + + exit_code = main( + [ + "--registry", + str(registry), + "--runtime-root", + str(runtime_override), + "--format", + "json", + "quota", + "monitor-poll", + "--goal-id", + GOAL_ID, + "--agent-id", + AGENT_ID, + "--todo-id", + MONITOR_ID, + "--target-key", + "splitroot-review", + "--result-hash", + OVERRIDE_POLL_HASH, + "--next-due-at", + "2026-09-04T02:00:00+00:00", + "--execute", + ] + ) + + assert exit_code == 1 + payload = json.loads(capsys.readouterr().out) + assert payload["error_code"] == "legacy_coordination_writer_fenced" + assert payload["reason"] == ( + "legacy coordination writer is fenced; use the promoted canonical " + f"authority (file_v0) for goal {GOAL_ID}; fence unknown; " + "the primary record was not changed" + ) + assert payload["write_check"] == { + "status": "blocked", + "reason_code": "legacy_coordination_writer_fenced", + "authority_mode": "file_v0", + } + assert state.read_bytes() == before + + def test_turn_repair_update_blocked_when_override_root_is_fenced( monkeypatch: pytest.MonkeyPatch, tmp_path: Path,