From 24eaa0551d442754a7b1b8baba5bf985af9589dc Mon Sep 17 00:00:00 2001 From: Pigbibi <20649888+Pigbibi@users.noreply.github.com> Date: Tue, 8 Sep 2026 22:49:08 +0800 Subject: [PATCH] fix: prefer scoped heartbeat targets over legacy inventory Co-Authored-By: Codex --- scripts/execution_report_heartbeat.py | 3 +- scripts/runtime_heartbeat_policy.py | 6 +++- tests/test_execution_report_heartbeat.py | 4 +++ tests/test_runtime_heartbeat_policy.py | 46 ++++++++++++++++++++++++ 4 files changed, 57 insertions(+), 2 deletions(-) diff --git a/scripts/execution_report_heartbeat.py b/scripts/execution_report_heartbeat.py index 11913dd..80f0cea 100644 --- a/scripts/execution_report_heartbeat.py +++ b/scripts/execution_report_heartbeat.py @@ -1187,7 +1187,8 @@ def main(now: dt.datetime | None = None) -> int: f"Execution report heartbeat skipped for {name}: " "no enabled runtime target matches this heartbeat" ) - _notify_normal_heartbeat(name, "No enabled runtime target matches this heartbeat; no order was submitted.") + # No report was checked, so this branch cannot claim normal execution + # or the absence of submitted orders. return 0 lookback_hours = float(os.environ.get("RUNTIME_HEARTBEAT_LOOKBACK_HOURS") or "36") max_reports = int(os.environ.get("RUNTIME_HEARTBEAT_MAX_REPORTS_TO_READ") or "20") diff --git a/scripts/runtime_heartbeat_policy.py b/scripts/runtime_heartbeat_policy.py index d2c1703..76e35c6 100644 --- a/scripts/runtime_heartbeat_policy.py +++ b/scripts/runtime_heartbeat_policy.py @@ -249,7 +249,12 @@ def _normalize_target( def _load_runtime_target_items( environ: Mapping[str, str], ) -> tuple[list[Mapping[str, Any]], Mapping[str, Any]]: + raw_runtime_target = str(environ.get("RUNTIME_TARGET_JSON") or "").strip() raw_targets = str(environ.get("CLOUD_RUN_SERVICE_TARGETS_JSON") or "").strip() + if raw_runtime_target and str(environ.get("RUNTIME_HEARTBEAT_ACCOUNT_SCOPE") or "").strip(): + # A scoped environment's current target takes precedence over the + # repository's legacy multi-target inventory. + raw_targets = "" items: list[Mapping[str, Any]] = [] defaults: Mapping[str, Any] = {} if raw_targets: @@ -270,7 +275,6 @@ def _load_runtime_target_items( ) if not items: - raw_runtime_target = str(environ.get("RUNTIME_TARGET_JSON") or "").strip() if raw_runtime_target: try: runtime_target = json.loads(raw_runtime_target) diff --git a/tests/test_execution_report_heartbeat.py b/tests/test_execution_report_heartbeat.py index 70a7463..ed5458d 100644 --- a/tests/test_execution_report_heartbeat.py +++ b/tests/test_execution_report_heartbeat.py @@ -742,6 +742,9 @@ def test_target_profile_uses_deployed_canonical_runtime_target(monkeypatch): def test_main_skips_when_all_configured_targets_are_disabled(monkeypatch, capsys): _clear_runtime_env(monkeypatch) monkeypatch.setenv("RUNTIME_HEARTBEAT_NAME", "LongBridge disabled targets") + monkeypatch.setenv("RUNTIME_HEARTBEAT_NOTIFY_ON_SUCCESS", "true") + messages = [] + monkeypatch.setattr(heartbeat, "_send_telegram", lambda message: messages.append(message)) monkeypatch.setenv( "CLOUD_RUN_SERVICE_TARGETS_JSON", json.dumps( @@ -761,3 +764,4 @@ def test_main_skips_when_all_configured_targets_are_disabled(monkeypatch, capsys now=dt.datetime(2026, 6, 20, 23, 10, tzinfo=dt.timezone.utc) ) == 0 assert "no enabled runtime target matches this heartbeat" in capsys.readouterr().out + assert messages == [] diff --git a/tests/test_runtime_heartbeat_policy.py b/tests/test_runtime_heartbeat_policy.py index 1e93239..0503d0c 100644 --- a/tests/test_runtime_heartbeat_policy.py +++ b/tests/test_runtime_heartbeat_policy.py @@ -3,17 +3,63 @@ import datetime as dt import json +import pytest + from scripts.runtime_heartbeat_policy import ( filter_due_targets, load_runtime_targets, match_payload_target, runtime_target_configuration_present, + runtime_target_configuration_has_enabled_targets, target_key, target_latest_due_at, ) from scripts import execution_report_heartbeat as heartbeat +@pytest.mark.parametrize("legacy_inventory", [ + [{"service": "old-sg", "account_scope": "SG", "runtime_target_enabled": False}], + [{"service": "old-paper", "account_scope": "PAPER"}], +]) +def test_scoped_current_target_precedes_legacy_inventory(legacy_inventory): + environ = { + "RUNTIME_HEARTBEAT_ACCOUNT_SCOPE": "SG", + "RUNTIME_TARGET_JSON": json.dumps({ + "service_name": "current-sg", + "strategy_profile": "soxl_soxx_trend_income", + "account_scope": "SG", + "live_continuity": {"state": "ACTIVE_LKG"}, + }), + "CLOUD_RUN_SERVICE_TARGETS_JSON": json.dumps(legacy_inventory), + } + + assert runtime_target_configuration_has_enabled_targets(environ) + targets = load_runtime_targets(environ) + assert [target["service"] for target in targets] == ["current-sg"] + assert targets[0]["strategy_profile"] == "soxl_soxx_trend_income" + + +def test_unscoped_heartbeat_keeps_multi_target_inventory(): + environ = { + "RUNTIME_TARGET_JSON": json.dumps({"service_name": "single"}), + "CLOUD_RUN_SERVICE_TARGETS_JSON": json.dumps([ + {"service": "first"}, {"service": "second"}, + ]), + } + assert [target["service"] for target in load_runtime_targets(environ)] == [ + "first", "second", + ] + + +def test_invalid_scoped_target_does_not_fall_back_to_legacy_inventory(): + with pytest.raises(ValueError, match="RUNTIME_TARGET_JSON"): + load_runtime_targets({ + "RUNTIME_HEARTBEAT_ACCOUNT_SCOPE": "SG", + "RUNTIME_TARGET_JSON": "invalid", + "CLOUD_RUN_SERVICE_TARGETS_JSON": json.dumps([{"service": "old-sg"}]), + }) + + def _target( *, service: str,