Skip to content

Commit 23a82d0

Browse files
Pigbibicodex
andcommitted
feat: separate live baseline continuity from candidate gate
Co-Authored-By: Codex <noreply@openai.com>
1 parent c075dd3 commit 23a82d0

14 files changed

Lines changed: 286 additions & 8 deletions

platform-config.json

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -426,6 +426,14 @@
426426
"dry_run"
427427
],
428428
"blocked_live_reason": "missing_current_promotion_evidence_and_preauthorized_autonomy_policy",
429+
"live_continuity": {
430+
"eligible": true,
431+
"allowed_platforms": [
432+
"longbridge",
433+
"ibkr",
434+
"schwab"
435+
]
436+
},
429437
"features": {
430438
"income_layer": true,
431439
"option_overlay": true,
@@ -493,6 +501,12 @@
493501
"dry_run"
494502
],
495503
"blocked_live_reason": "missing_current_promotion_evidence_and_preauthorized_autonomy_policy",
504+
"live_continuity": {
505+
"eligible": true,
506+
"allowed_platforms": [
507+
"firstrade"
508+
]
509+
},
496510
"features": {
497511
"income_layer": false,
498512
"option_overlay": false,
@@ -937,6 +951,12 @@
937951
"dry_run"
938952
],
939953
"blocked_live_reason": "missing_current_promotion_evidence_and_preauthorized_autonomy_policy",
954+
"live_continuity": {
955+
"eligible": true,
956+
"allowed_platforms": [
957+
"binance"
958+
]
959+
},
940960
"features": {
941961
"income_layer": false,
942962
"option_overlay": false,

python/scripts/build_config.py

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,9 @@
3737
"can_switch_live",
3838
"allowed_execution_modes",
3939
"blocked_live_reason",
40+
"live_continuity",
4041
}
42+
LIVE_CONTINUITY_POLICY_FIELDS = {"eligible", "allowed_platforms"}
4143
SCHEDULER_FIELDS = {"timezone", "main_time", "probe_time", "precheck_time"}
4244
MARKET_FIELDS = {"market", "market_calendar", "market_timezone"}
4345
FEATURE_SNAPSHOT_FIELDS = {"required", "path", "manifest_path", "max_age_days"}
@@ -94,6 +96,10 @@ def validate(config: dict) -> list[str]:
9496
errors: list[str] = []
9597
validate_runtime_authority_status(config, errors)
9698
validate_notification_references(config, errors)
99+
platforms = config.get("platforms", {})
100+
if not isinstance(platforms, dict):
101+
errors.append("platforms must be an object")
102+
platforms = {}
97103
scheduling = config.get("scheduling")
98104
scheduler_profiles = scheduling.get("profiles") if isinstance(scheduling, dict) else None
99105
if not isinstance(scheduler_profiles, dict) or not scheduler_profiles:
@@ -126,7 +132,7 @@ def validate(config: dict) -> list[str]:
126132
errors.append(
127133
f"scheduler profile {profile}: {field} must be Mon-Fri cron with day-of-month '*'"
128134
)
129-
for pid, pdata in config.get("platforms", {}).items():
135+
for pid, pdata in platforms.items():
130136
if "capabilities" not in pdata:
131137
errors.append(f"platform {pid}: missing capabilities")
132138
if "default_account" not in pdata:
@@ -225,6 +231,7 @@ def validate(config: dict) -> list[str]:
225231
f"must match market_timezone {market_timezone!r}"
226232
)
227233
for sid, sdata in config.get("strategies", {}).items():
234+
validate_live_continuity_policy(sid, sdata, platforms, errors)
228235
if "domain" not in sdata:
229236
errors.append(f"strategy {sid}: missing domain")
230237
continue
@@ -370,6 +377,41 @@ def validate(config: dict) -> list[str]:
370377
return errors
371378

372379

380+
def validate_live_continuity_policy(
381+
strategy_id: str,
382+
strategy: object,
383+
platforms: dict,
384+
errors: list[str],
385+
) -> None:
386+
"""Validate optional incumbent-continuity metadata independently of P0--P6."""
387+
388+
if not isinstance(strategy, dict):
389+
return
390+
continuity = strategy.get("live_continuity")
391+
if continuity is None:
392+
return
393+
if not isinstance(continuity, dict):
394+
errors.append(f"strategy {strategy_id}: live_continuity must be an object")
395+
return
396+
unsupported = sorted(set(continuity) - LIVE_CONTINUITY_POLICY_FIELDS)
397+
if unsupported:
398+
errors.append(f"strategy {strategy_id}: unsupported live_continuity fields {unsupported}")
399+
eligible = continuity.get("eligible")
400+
if not isinstance(eligible, bool):
401+
errors.append(f"strategy {strategy_id}: live_continuity.eligible must be boolean")
402+
allowed_platforms = continuity.get("allowed_platforms")
403+
if not isinstance(allowed_platforms, list) or not all(
404+
isinstance(platform, str) and platform in platforms for platform in allowed_platforms
405+
):
406+
errors.append(
407+
f"strategy {strategy_id}: live_continuity.allowed_platforms must contain configured platforms"
408+
)
409+
elif len(set(allowed_platforms)) != len(allowed_platforms):
410+
errors.append(f"strategy {strategy_id}: live_continuity.allowed_platforms must not contain duplicates")
411+
elif eligible is True and not allowed_platforms:
412+
errors.append(f"strategy {strategy_id}: live_continuity.eligible requires allowed_platforms")
413+
414+
373415
def build_strategy_platform_dry_run_coverage(config: dict | None = None) -> dict[str, object]:
374416
"""Report declared and default-buildable no-order platform routes.
375417
@@ -698,6 +740,7 @@ def _automation_policy_for_strategy(profile: str, strategy: dict) -> dict[str, o
698740
runtime_enabled = strategy.get("runtime_enabled") is True
699741
blocked_reason = str(strategy.get("blocked_live_reason") or "").strip()
700742
features = strategy.get("features") if isinstance(strategy.get("features"), dict) else {}
743+
continuity = strategy.get("live_continuity") if isinstance(strategy.get("live_continuity"), dict) else {}
701744
if runtime_enabled and can_switch_live and lifecycle_stage == "runtime_enabled":
702745
lane = "live_equivalent_optimization"
703746
triggers = ["health_degradation", "parameter_drift", "scheduled_retest", "market_regime_shift"]
@@ -737,6 +780,10 @@ def _automation_policy_for_strategy(profile: str, strategy: dict) -> dict[str, o
737780
"operating_policy_status": operating_policy_status,
738781
"can_switch_live": can_switch_live,
739782
"blocked_live_reason": blocked_reason,
783+
"live_continuity": {
784+
"eligible": continuity.get("eligible") is True,
785+
"allowed_platforms": list(continuity.get("allowed_platforms") or []),
786+
},
740787
"triggers": triggers,
741788
"evidence_required": evidence_required,
742789
"position_control_sensitive": bool(features.get("combo") or features.get("option_overlay")),
@@ -1005,11 +1052,16 @@ def _strategy_profile_gate_fields(sdata: dict) -> dict[str, object]:
10051052
)
10061053
if blocked_live_reason is None and not can_switch_live:
10071054
blocked_live_reason = lifecycle_stage or "not_runtime_enabled"
1055+
continuity = sdata.get("live_continuity") if isinstance(sdata.get("live_continuity"), dict) else {}
10081056
return {
10091057
"lifecycle_stage": lifecycle_stage,
10101058
"can_switch_live": can_switch_live,
10111059
"allowed_execution_modes": _normalize_allowed_execution_modes(sdata.get("allowed_execution_modes")),
10121060
"blocked_live_reason": "" if blocked_live_reason is None else str(blocked_live_reason).strip(),
1061+
"live_continuity": {
1062+
"eligible": continuity.get("eligible") is True,
1063+
"allowed_platforms": list(continuity.get("allowed_platforms") or []),
1064+
},
10131065
}
10141066

10151067

python/scripts/build_platform_config.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -293,11 +293,16 @@ def _strategy_profile_gate_fields(sdata: dict) -> dict[str, object]:
293293
)
294294
if blocked_live_reason is None and not can_switch_live:
295295
blocked_live_reason = lifecycle_stage or "not_runtime_enabled"
296+
continuity = sdata.get("live_continuity") if isinstance(sdata.get("live_continuity"), dict) else {}
296297
return {
297298
"lifecycle_stage": lifecycle_stage,
298299
"can_switch_live": can_switch_live,
299300
"allowed_execution_modes": _normalize_allowed_execution_modes(sdata.get("allowed_execution_modes")),
300301
"blocked_live_reason": "" if blocked_live_reason is None else str(blocked_live_reason).strip(),
302+
"live_continuity": {
303+
"eligible": continuity.get("eligible") is True,
304+
"allowed_platforms": list(continuity.get("allowed_platforms") or []),
305+
},
301306
}
302307

303308

python/scripts/inject_platform_config.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,7 @@ def _strategy_profile_entry(sid: str, sdata: dict) -> dict:
158158
blocked_live_reason = sdata.get("blocked_live_reason")
159159
if blocked_live_reason is None and not can_switch_live:
160160
blocked_live_reason = lifecycle_stage or "not_runtime_enabled"
161+
continuity = sdata.get("live_continuity") if isinstance(sdata.get("live_continuity"), dict) else {}
161162
entry = {
162163
"profile": sid,
163164
"label": sdata.get("label", sid),
@@ -169,6 +170,10 @@ def _strategy_profile_entry(sid: str, sdata: dict) -> dict:
169170
"can_switch_live": can_switch_live,
170171
"allowed_execution_modes": _normalize_allowed_execution_modes(sdata.get("allowed_execution_modes")),
171172
"blocked_live_reason": "" if blocked_live_reason is None else str(blocked_live_reason).strip(),
173+
"live_continuity": {
174+
"eligible": continuity.get("eligible") is True,
175+
"allowed_platforms": list(continuity.get("allowed_platforms") or []),
176+
},
172177
"income_layer_enabled": feat.get("income_layer", False),
173178
"option_overlay_enabled": feat.get("option_overlay", False),
174179
"combo_enabled": feat.get("combo", False),

python/scripts/runtime_settings.py

Lines changed: 103 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from __future__ import annotations
55

66
import argparse
7+
import hashlib
78
import json
89
import os
910
import re
@@ -75,6 +76,27 @@
7576
)
7677
STRATEGY_RELEASE_ID_PATTERN = re.compile(r"^[A-Za-z0-9][A-Za-z0-9._-]{2,127}$")
7778
SHA256_PATTERN = re.compile(r"^(?:sha256:)?[0-9a-fA-F]{64}$")
79+
LIVE_CONTINUITY_STATES = frozenset(
80+
{
81+
"ACTIVE_LKG",
82+
"ACTIVE_REDUCED",
83+
"RECONCILE_ONLY",
84+
"RISK_REDUCTION_ONLY",
85+
"PAUSED",
86+
"ROLLBACK_LKG",
87+
}
88+
)
89+
LIVE_CONTINUITY_BASELINE_KINDS = frozenset({"legacy_authorized", "release_attested"})
90+
LIVE_CONTINUITY_FIELDS = frozenset(
91+
{
92+
"state",
93+
"baseline_kind",
94+
"baseline_id",
95+
"baseline_target_sha256",
96+
"captured_at",
97+
}
98+
)
99+
LIVE_CONTINUITY_BASELINE_ID_PATTERN = re.compile(r"^[A-Za-z0-9][A-Za-z0-9._-]{2,127}$")
78100
GENERATED_VARIABLES = {"RUNTIME_TARGET_JSON", "STRATEGY_PROFILE"}
79101
SECRET_MARKERS = ("PASSWORD", "PRIVATE_KEY", "TOKEN", "API_KEY", "ACCESS_KEY", "CLIENT_SECRET", "SECRET")
80102
LEGACY_INCOME_LAYER_VARIABLES = frozenset(
@@ -187,6 +209,15 @@ def compact_json(value: Any) -> str:
187209
return json.dumps(value, ensure_ascii=False, separators=(",", ":"))
188210

189211

212+
def runtime_target_fingerprint(runtime_target: dict[str, Any]) -> str:
213+
"""Fingerprint a frozen target excluding only its current continuity state."""
214+
215+
payload = dict(runtime_target)
216+
payload.pop("live_continuity", None)
217+
encoded = json.dumps(payload, ensure_ascii=True, sort_keys=True, separators=(",", ":"))
218+
return hashlib.sha256(encoded.encode("utf-8")).hexdigest()
219+
220+
190221
def env_string(value: Any) -> str:
191222
if isinstance(value, str):
192223
return value
@@ -453,6 +484,7 @@ def validate_runtime_target(target: dict[str, Any], errors: list[str]) -> None:
453484
except (ZoneInfoNotFoundError, ValueError):
454485
errors.append(f"runtime_target.market_timezone is invalid: {market_timezone!r}")
455486
validate_strategy_release(runtime_target, errors)
487+
validate_live_continuity(runtime_target, errors)
456488

457489

458490
def validate_strategy_release(runtime_target: dict[str, Any], errors: list[str]) -> None:
@@ -493,6 +525,62 @@ def validate_strategy_release(runtime_target: dict[str, Any], errors: list[str])
493525
)
494526

495527

528+
def validate_live_continuity(runtime_target: dict[str, Any], errors: list[str]) -> None:
529+
"""Validate a frozen incumbent baseline independently of candidate policy."""
530+
531+
continuity = runtime_target.get("live_continuity")
532+
if continuity is None:
533+
return
534+
if not isinstance(continuity, dict):
535+
errors.append("runtime_target.live_continuity must be an object when present")
536+
return
537+
unsupported = sorted(set(continuity) - LIVE_CONTINUITY_FIELDS)
538+
if unsupported:
539+
errors.append(
540+
"runtime_target.live_continuity contains unsupported fields: " + ", ".join(unsupported)
541+
)
542+
for field in sorted(LIVE_CONTINUITY_FIELDS):
543+
value = continuity.get(field)
544+
if not isinstance(value, str) or not value.strip():
545+
errors.append(f"runtime_target.live_continuity.{field} is required")
546+
547+
state = str(continuity.get("state") or "").strip().upper()
548+
if state not in LIVE_CONTINUITY_STATES:
549+
errors.append(
550+
"runtime_target.live_continuity.state must be one of "
551+
+ ", ".join(sorted(LIVE_CONTINUITY_STATES))
552+
)
553+
baseline_kind = str(continuity.get("baseline_kind") or "").strip()
554+
if baseline_kind not in LIVE_CONTINUITY_BASELINE_KINDS:
555+
errors.append(
556+
"runtime_target.live_continuity.baseline_kind must be one of "
557+
+ ", ".join(sorted(LIVE_CONTINUITY_BASELINE_KINDS))
558+
)
559+
baseline_id = str(continuity.get("baseline_id") or "").strip()
560+
if baseline_id and not LIVE_CONTINUITY_BASELINE_ID_PATTERN.fullmatch(baseline_id):
561+
errors.append("runtime_target.live_continuity.baseline_id has invalid characters")
562+
digest = str(continuity.get("baseline_target_sha256") or "").strip().lower()
563+
digest = digest.removeprefix("sha256:")
564+
if not SHA256_PATTERN.fullmatch(digest):
565+
errors.append("runtime_target.live_continuity.baseline_target_sha256 must be a SHA-256 digest")
566+
elif digest != runtime_target_fingerprint(runtime_target):
567+
errors.append(
568+
"runtime_target.live_continuity.baseline_target_sha256 does not match the runtime target"
569+
)
570+
captured_at = str(continuity.get("captured_at") or "").strip()
571+
if captured_at:
572+
try:
573+
date.fromisoformat(captured_at)
574+
except ValueError:
575+
errors.append("runtime_target.live_continuity.captured_at must be an ISO-8601 date")
576+
if baseline_kind == "release_attested" and not isinstance(runtime_target.get("strategy_release"), dict):
577+
errors.append("release_attested live_continuity requires runtime_target.strategy_release")
578+
579+
580+
def is_live_continuity_target(runtime_target: dict[str, Any]) -> bool:
581+
return isinstance(runtime_target.get("live_continuity"), dict)
582+
583+
496584
def validate_live_ibkr_us_scheduler(
497585
runtime_target: dict[str, Any],
498586
scheduler: dict[str, Any],
@@ -596,12 +684,26 @@ def validate_runtime_target_strategy_policy(runtime_target: dict[str, Any], erro
596684
errors.append(
597685
f"platform {platform_id} does not support {execution_mode} control execution"
598686
)
687+
continuity_target = is_live_continuity_target(runtime_target)
599688
allowed_modes = normalize_allowed_execution_modes(strategy.get("allowed_execution_modes"))
600-
if allowed_modes and execution_mode not in allowed_modes:
689+
if allowed_modes and execution_mode not in allowed_modes and not continuity_target:
601690
errors.append(f"runtime_target.strategy_profile {profile} does not allow {execution_mode} execution")
602691

603692
if execution_mode != "live":
604693
return
694+
if continuity_target:
695+
continuity_policy = strategy.get("live_continuity")
696+
if not isinstance(continuity_policy, dict) or continuity_policy.get("eligible") is not True:
697+
errors.append(
698+
f"runtime_target.strategy_profile {profile} is not eligible for live continuity"
699+
)
700+
return
701+
allowed_platforms = continuity_policy.get("allowed_platforms")
702+
if not isinstance(allowed_platforms, list) or platform_id not in allowed_platforms:
703+
errors.append(
704+
f"runtime_target.strategy_profile {profile} live continuity is not allowed on {platform_id}"
705+
)
706+
return
605707
lifecycle_stage = str(strategy.get("lifecycle_stage") or "").strip()
606708
if strategy.get("runtime_enabled") is not True:
607709
errors.append(f"runtime_target.strategy_profile {profile} is not runtime_enabled")

python/scripts/sync_strategy_switch_page_asset.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,15 @@ def _enrich_profiles_from_config(profiles: list[dict]) -> list[dict]:
8181
"blocked_live_reason",
8282
"" if blocked_live_reason is None else str(blocked_live_reason).strip(),
8383
)
84+
continuity = config_fields.get("live_continuity")
85+
if isinstance(continuity, dict):
86+
item.setdefault(
87+
"live_continuity",
88+
{
89+
"eligible": continuity.get("eligible") is True,
90+
"allowed_platforms": list(continuity.get("allowed_platforms") or []),
91+
},
92+
)
8493
item.setdefault("runtime_enabled", runtime_enabled)
8594
enriched.append(item)
8695
return enriched

0 commit comments

Comments
 (0)