fix: close broker uncertainty audit gaps

This commit is contained in:
jordan
2026-07-29 17:53:39 +09:00
parent de1e3eb44c
commit 611805dcec
19 changed files with 236 additions and 42 deletions
+22 -12
View File
@@ -30,6 +30,7 @@ from uuid import UUID
from trex_core.application.ports import IntentStore
from trex_core.domain.slot import (
SlotAdmissionRejectedError,
SlotRepository,
SlotService,
SlotState,
@@ -275,20 +276,24 @@ class IntentService:
symbol = _resolve(slot)
if not symbol:
return IntentDecision(intent.intent_id, "rejected", None, "unknown_portfolio")
if self._buy_guard_fn is not None:
def _admission_precondition(_locked_slot) -> Optional[str]:
# Runs inside SlotService's instance-scoped advisory lock. This
# makes capacity and cached-cash reservation checks atomic with
# the PENDING_BUY transition across same-instance daemons.
if (
not is_increase
and self._max_active_slots is not None
and self._repo.count_active(self._instance_key)
>= self._max_active_slots
):
return "capacity_full"
if self._buy_guard_fn is None:
return None
try:
guard_reason = self._buy_guard_fn(intent)
return self._buy_guard_fn(intent)
except Exception: # noqa: BLE001 - buy guard fails closed
guard_reason = "account_snapshot_unavailable"
if guard_reason is not None:
return IntentDecision(
intent.intent_id, "rejected", None, guard_reason
)
# M cap applies only to NEW activations (initial buy). An increase on
# an already-active asset does not change the active asset count.
if (not is_increase and self._max_active_slots is not None
and self._repo.count_active(self._instance_key) >= self._max_active_slots):
return IntentDecision(intent.intent_id, "rejected", None, "capacity_full")
return "account_snapshot_unavailable"
# dcy-022 / REQ-068: resolve effective timeout (per-intent > config default).
effective_timeout = (
intent.timeout_sec
@@ -317,6 +322,11 @@ class IntentService:
slippage_pct=intent.slippage_pct,
venue=intent.venue,
extra={"intent_id": intent.intent_id, "symbol": symbol},
precondition=_admission_precondition,
)
except SlotAdmissionRejectedError as exc:
return IntentDecision(
intent.intent_id, "rejected", None, exc.reason
)
except SlotStateMismatchError:
continue # slot changed under us; re-fetch and re-evaluate