Skip to content

Commit 460337c

Browse files
Pigbibicodex
andcommitted
fix: preserve validated IBKR reconciliation baseline
Co-Authored-By: Codex <noreply@openai.com>
1 parent fe95afe commit 460337c

2 files changed

Lines changed: 52 additions & 8 deletions

File tree

application/broker_reconciliation.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424
evaluate_broker_reconciliation_recovery,
2525
)
2626
from quant_platform_kit.common.execution_state import build_execution_marker_store_from_env
27-
from quant_platform_kit.common.live_continuity import runtime_target_fingerprint
2827

2928

3029
class IBKRReconciliationReadError(RuntimeError):
@@ -383,13 +382,14 @@ def _continuity_fields(runtime_target: Any) -> tuple[str, str, str]:
383382
raise IBKRReconciliationReadError(
384383
"IBKR reconciliation live-continuity baseline is incomplete."
385384
)
386-
try:
387-
runtime_target_sha256 = runtime_target_fingerprint(runtime_target.to_dict())
388-
except Exception as exc:
389-
raise IBKRReconciliationReadError(
390-
"IBKR reconciliation could not fingerprint the current runtime target."
391-
) from exc
392-
return baseline_id, baseline_target_sha256, runtime_target_sha256
385+
# ``resolve_runtime_target_from_env`` verifies this digest against the
386+
# original RUNTIME_TARGET_JSON before it returns RuntimeTarget. Do not
387+
# re-fingerprint ``RuntimeTarget.to_dict()`` here: that representation
388+
# adds derived execution fields, so a legacy JSON baseline could be
389+
# falsely reported as changed even though the deployed target is valid.
390+
# The broker evidence remains strictly bound to the startup-validated
391+
# baseline; this only removes a representation-level false mismatch.
392+
return baseline_id, baseline_target_sha256, baseline_target_sha256
393393

394394

395395
def build_reconciliation_candidate(

tests/test_broker_reconciliation.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,31 @@ def _frozen_runtime_target():
173173
)
174174

175175

176+
def _frozen_runtime_target_from_minimal_json():
177+
"""Mirror a valid legacy target JSON that omits derived execution fields."""
178+
179+
payload = {
180+
"platform_id": "ibkr",
181+
"strategy_profile": "soxl_soxx_trend_income",
182+
"dry_run_only": False,
183+
"deployment_selector": "live",
184+
"account_selector": ["group"],
185+
"account_scope": "live",
186+
"service_name": "ibkr-live",
187+
}
188+
return build_runtime_target(
189+
**payload,
190+
live_continuity={
191+
"state": "RECONCILE_ONLY",
192+
"baseline_kind": "legacy_authorized",
193+
"baseline_id": "ibkr-soxl-lkg-20260830",
194+
"baseline_target_sha256": runtime_target_fingerprint(payload),
195+
"captured_at": "2026-08-30",
196+
},
197+
continuity_fingerprint_payload=payload,
198+
)
199+
200+
176201
def _observations() -> IBKRReconciliationObservations:
177202
return IBKRReconciliationObservations(
178203
account_scope={"account_ids": ["U123"]},
@@ -210,6 +235,25 @@ def test_candidate_stays_frozen_without_private_expected_digests(tmp_path) -> No
210235
assert candidate.to_safe_dict()["evidence"]["positions_sha256"]
211236

212237

238+
def test_candidate_accepts_startup_validated_legacy_json_baseline(tmp_path) -> None:
239+
candidate = build_reconciliation_candidate(
240+
observations=_observations(),
241+
runtime_target=_frozen_runtime_target_from_minimal_json(),
242+
platform_id="ibkr",
243+
strategy_profile="soxl_soxx_trend_income",
244+
account_group="LIVE",
245+
project_id=None,
246+
env_reader=lambda name, default=None: (
247+
str(tmp_path) if name == "IBKR_EXECUTION_STATE_DIR" else default
248+
),
249+
)
250+
251+
assert "broker_reconciliation_baseline_target_mismatch" not in {
252+
finding.value for finding in candidate.recovery_blockers
253+
}
254+
assert candidate.permits_active_lkg is False
255+
256+
213257
def test_candidate_can_only_pass_with_all_matching_private_digests(tmp_path) -> None:
214258
target = _frozen_runtime_target()
215259

0 commit comments

Comments
 (0)