fix(reconcile): symbol을 슬롯 생성 시 함께 저장 + NULL 슬롯 backfill

reconcile_universe 시그니처를 set[str] → dict[str, str] (portfolio_id→symbol)로 변경.
기존 symbol=NULL 슬롯은 다음 reconcile 실행 시 자동 backfill.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-11 21:08:46 +09:00
co-authored by Claude Sonnet 4.6
parent 221a16342e
commit 24a6017457
29 changed files with 92 additions and 68 deletions
@@ -84,7 +84,7 @@ class InMemorySlotRepository:
]
def reconcile_universe(
self, instance_key: str, universe_portfolio_ids: set[str],
self, instance_key: str, universe: dict[str, str],
*, allow_retire: bool = True,
) -> dict[str, int]:
existing = {
@@ -93,13 +93,18 @@ class InMemorySlotRepository:
}
now = datetime.now(tz=timezone.utc)
created = retired = tombstoned = 0
# backfill symbol on slots that were created before this field was populated
for s in self._slots.values():
if (s.instance_key == instance_key and s.portfolio_id in universe
and s.symbol is None):
self._slots[s.slot_id] = replace(s, symbol=universe[s.portfolio_id], updated_at=now)
# create asset slots for newly-appeared universe assets (idempotent)
for pid in universe_portfolio_ids:
for pid, symbol in universe.items():
if pid not in existing:
sid = uuid4()
self._slots[sid] = Slot(
slot_id=sid, status=SlotState.INACTIVE,
instance_key=instance_key, portfolio_id=pid,
instance_key=instance_key, portfolio_id=pid, symbol=symbol,
created_at=now, updated_at=now,
)
created += 1
@@ -107,7 +112,7 @@ class InMemorySlotRepository:
# mass-disappearance guard, dcy-020/OQ-2 D3 — caller passes allow_retire=False)
if allow_retire:
for pid, s in existing.items():
if pid in universe_portfolio_ids:
if pid in universe:
continue
if s.status in self._ACTIVE:
tombstoned += 1 # holding/pending — keep (safety-sell), no retire
+3 -2
View File
@@ -66,13 +66,14 @@ class SlotRepository(Protocol):
...
def reconcile_universe(
self, instance_key: str, universe_portfolio_ids: set[str],
self, instance_key: str, universe: dict[str, str],
*, allow_retire: bool = True,
) -> dict[str, int]:
"""Reconcile the slot set to the portfolio-hub universe (dcy-020/REQ-058).
universe: {portfolio_id -> symbol} mapping from PortfolioItem.
- new portfolio_id (no slot for instance) -> create an inactive asset slot
(idempotent; keyed by (instance_key, portfolio_id), permanent).
with symbol set (idempotent; keyed by (instance_key, portfolio_id), permanent).
- slot whose portfolio_id left the universe AND is inactive AND not yet
retired -> soft-retire (set retired_at).
- slot that left the universe but is holding/pending -> tombstone (left