Merge remote-tracking branch 'origin/main' into copilot/feat-reconcile-unmanaged-position-durable-lifecycl
# Conflicts: # .proofline/criteria/ac-0029.md # .proofline/identities.json # tests/adapters/persistence/test_migration_and_drift.py Co-authored-by: genichin <4282227+genichin@users.noreply.github.com>
This commit is contained in:
@@ -742,7 +742,7 @@ def sync_slot_to_broker(
|
||||
Broker qty == 0 for slot → skipped with WARN (slot-side A-case guard).
|
||||
Broker-only (no slot) → NOT handled here; caller filters (FR-3).
|
||||
|
||||
After update, if slot.qty == broker_qty: clear_warned(slot_id).
|
||||
After update, if slot.qty == broker_qty: clear_warned(slot_id, case_code="B").
|
||||
"""
|
||||
from uuid import UUID
|
||||
|
||||
@@ -983,14 +983,15 @@ def sync_slot_to_broker(
|
||||
after_avg = float(refreshed.avg_price) if (refreshed and refreshed.avg_price is not None) else None
|
||||
after = {"qty": after_qty, "status": after_status, "avg_price": after_avg}
|
||||
|
||||
# Clear warning when qty now matches broker
|
||||
# Clear B-case warning when qty now matches broker
|
||||
warning_cleared = False
|
||||
if after_qty is not None and int(after_qty) == broker_qty:
|
||||
try:
|
||||
warn_store.clear_warned(slot_id)
|
||||
warn_store.clear_warned(slot_id, case_code="B")
|
||||
warning_cleared = True
|
||||
_log.info(
|
||||
"sync: reconcile-warning cleared slot=%s symbol=%s broker_qty=%d actor=sync",
|
||||
"sync: reconcile-warning cleared slot=%s symbol=%s case_code=B "
|
||||
"broker_qty=%d actor=sync",
|
||||
slot_id, sym, broker_qty,
|
||||
)
|
||||
except Exception: # noqa: BLE001
|
||||
@@ -1075,7 +1076,7 @@ def verify_slot_reconcile(
|
||||
"reason": "no active reconcile-warning (already cleared or never set)",
|
||||
}
|
||||
|
||||
# Check case_code — verify is C-only (REQ-106 OQ-C).
|
||||
# Check case set — verify is C-only (REQ-106 OQ-C).
|
||||
info = warn_store.get_warning_info(slot_id_str)
|
||||
if info is None:
|
||||
return {
|
||||
@@ -1087,8 +1088,13 @@ def verify_slot_reconcile(
|
||||
"reason": "warning exists but get_warning_info returned None (unexpected)",
|
||||
}
|
||||
|
||||
case_code = info.get("case_code", "")
|
||||
if case_code != "C":
|
||||
case_codes_raw = info.get("case_codes")
|
||||
if isinstance(case_codes_raw, list):
|
||||
case_codes = {str(code) for code in case_codes_raw if code}
|
||||
else:
|
||||
case_code = str(info.get("case_code", ""))
|
||||
case_codes = {case_code} if case_code else set()
|
||||
if "C" not in case_codes:
|
||||
return {
|
||||
"portfolio_id": portfolio_id,
|
||||
"slot_id": slot_id_str,
|
||||
@@ -1096,7 +1102,7 @@ def verify_slot_reconcile(
|
||||
"hub_qty": None,
|
||||
"broker_qty": None,
|
||||
"reason": (
|
||||
f"warning case_code='{case_code}' is not 'C' — "
|
||||
f"warning case_codes={sorted(case_codes)} do not include 'C' — "
|
||||
"use 'trex-core sync' to resolve B-case (slot↔broker qty mismatch)"
|
||||
),
|
||||
}
|
||||
@@ -1127,9 +1133,17 @@ def verify_slot_reconcile(
|
||||
continue
|
||||
sid_str = str(s.slot_id)
|
||||
warn_info = warn_store.get_warning_info(sid_str)
|
||||
if warn_info is None or warn_info.get("case_code") != "C":
|
||||
if warn_info is None:
|
||||
continue
|
||||
warn_store.clear_warned(sid_str)
|
||||
warned_codes_raw = warn_info.get("case_codes")
|
||||
if isinstance(warned_codes_raw, list):
|
||||
warned_codes = {str(code) for code in warned_codes_raw if code}
|
||||
else:
|
||||
case_code = str(warn_info.get("case_code", ""))
|
||||
warned_codes = {case_code} if case_code else set()
|
||||
if "C" not in warned_codes:
|
||||
continue
|
||||
warn_store.clear_warned(sid_str, case_code="C")
|
||||
store.set_warning(s.portfolio_id, False)
|
||||
cleared_slots.append(sid_str)
|
||||
_log.info(
|
||||
|
||||
Reference in New Issue
Block a user