Files
trex-core/docs/runtime-flows.md
T
genichinandClaude Opus 4.8 a30efa8f3b feat(safety-sell): dcy-007 core 자율 안전 매도 — BAT-010 기반 + BAT-011 트리거
dcy-007(core 자율 안전 매도) 전체 delivery. BAT-010 기반 인프라가 미커밋
상태였고 BAT-011 트리거 레이어와 공유 파일에서 섞여 있어 한 커밋으로 묶음.

BAT-010 (REQ-034/035/036/041, release-candidate):
- WatchdogStore(application/watchdog.py) + snapshot RPC 워치독 갱신
- Slot stop_loss_price/take_profit_price 필드 + 004_add_price_thresholds.sql
- SlotService.sell() 자율 매도 내부 경로 + idempotency
- SafetySellConfig env 5종 파싱

BAT-011 (REQ-037/038/039/040/042, release-candidate):
- SafetySellService(application/safety_sell.py): forced_close 만기 / stop-loss·
  take-profit KIS polling / 장 마감 강제 청산 + trex-safety-sell 7번째 스레드
- BrokerPort.get_current_price + KIS inquire-price(TTTC8397R/VTTC8397R, stck_prpr)
  + paper stub
- SlotService.update_thresholds() + intent RPC update_thresholds 액션
  (부분 갱신/null 해제, 워치독 last_seen_at 리셋)
- baseline 3문서 정합(interface-contract/runtime-flows/project-structure)

거버넌스: REQ-034~042 Implemented 동기화, BAT-010/011 release-candidate,
dcy-007 discovery 포함.

검증: 신규 38 테스트 green, 단위 236 passed/23 skipped,
인프라 게이트(PG+RabbitMQ) 259 passed(마이그레이션 001~004 round-trip 포함),
ruff clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-07 08:32:30 +09:00

238 lines
20 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Trex Core Runtime Flows
- Baseline Mode: mixed
- Baseline Seed: .stage-pilot/bootstrap/baseline.yaml
- Status: draft
- Owner: project maintainers
- Last Updated (KST): 2026-06-07 (BAT-011)
- Source Discovery / Batch: bootstrap-baseline (pre-Discovery); 데몬/HTTP 흐름 = dcy-002 / BAT-002
- Primary Runtime: daemon (HTTP API) + library (slot 도메인)
## Project Summary
- trex 제품군의 공용 베이스로, 첫 도메인으로 trex-orbit 의 일일 거래 슬롯 생명주기(slot) 순수 도메인을 storage-agnostic 하게 이식한 코어다.
## Purpose
- This document is the current approved execution-flow baseline for the project.
- It is a cross-cutting reference document for Discovery, REQ, Batch Design, Batch Verification, and implementation updates.
## Scope
- Covers representative domain-service flows (library 형태이므로 외부 명령이 아니라 in-process API 호출 흐름).
- Does not duplicate full acceptance criteria or low-level implementation details.
## Covered Entry Points
- `trex-core-daemon` :: 슬롯 코어 데몬 (lifecycle + RabbitMQ 통신, BAT-002/BAT-007)
- RabbitMQ `trex.snapshot.req` :: 전략 모듈의 스냅샷 RPC (request-reply, BAT-007)
- RabbitMQ `trex.intent.req` :: 전략 모듈의 매수/매도 admission RPC (request-reply, BAT-007)
- RabbitMQ `trex.events` :: 체결/슬롯 상태 이벤트 topic exchange (pub/sub, BAT-007)
- SlotService :: 슬롯 생명주기 도메인 서비스 (preallocate / transit / capacity / entry-count)
- SlotRepository (Protocol) :: 저장소 구현체 주입 경계
- MessagingTransport (Protocol) :: 메시지 transport 추상화 (RabbitMQ / InMemory stub)
## Flow: `데몬 lifecycle` (BAT-002, BAT-007)
1. `trex-core-daemon` 기동 → `DaemonConfig.from_env()`(TREX_POSTGRESQL_DSN, `TREX_RABBITMQ_URL` 등).
2. setup: `apply_migrations(dsn)`(멱등) → 거래일 슬롯 `preallocate_daily_slots`(멱등) → `execution.reconcile()`(REQ-014) → 이벤트 publisher 의 `declare_topology()`.
3. start: 7개 백그라운드 스레드 동시 기동
- `trex-execution` :: 브로커 dispatch/poll 루프 (REQ-013/014)
- `trex-ledger` :: ledger outbox 드레인 (REQ-019)
- `trex-intent-rpc` :: `trex.intent.req` 소비자 (prefetch=1, FIFO, dedup via intent_log; `update_thresholds` 액션 포함 REQ-040)
- `trex-snapshot-rpc` :: `trex.snapshot.req` 소비자 (응답 holding 슬롯마다 워치독 `last_seen_at` 갱신 REQ-034)
- `trex-pause-rpc` :: `trex.pause.req` 소비자 (REQ-032)
- `trex-snapshot-scheduler` :: 일일 portfolio-hub 스냅샷 refresh (REQ-030)
- `trex-safety-sell` :: 자율 안전 매도 트리거 루프 (forced_close / stop-loss·take-profit / 장 마감, REQ-037/038/039)
4. shutdown: `SIGINT`/`SIGTERM` → `stop.set()` → 모든 스레드 join + 모든 transport `close()`. HTTP 표면 0, 별도 포트 바인딩 없음.
## Flow: `스냅샷 RPC` (RabbitMQ `trex.snapshot.req`, BAT-007)
1. 전략 모듈이 RPC 요청을 `trex.snapshot.req` 큐로 publish — payload 에 `view`/`strategy_id`/`trading_date`, properties 에 `correlation_id` + `reply_to`(direct reply-to).
2. `trex-snapshot-rpc` 스레드가 메시지 수신 → `make_snapshot_rpc_handler(slot_repo, slot_service)` 로 슬롯 목록 + 가용성 계산.
3. 응답을 `reply_to` 큐로 publish (`correlation_id` 일치) → manual ack.
4. read-only 이므로 dedup 불요; 재요청 시 동일 응답을 다시 생성한다.
## Flow: `매수/매도 intent admission RPC` (RabbitMQ `trex.intent.req`, BAT-007)
1. 전략 모듈이 RPC 요청을 `trex.intent.req` 큐로 publish — payload `{intent_type, intent_id, ...}`, properties `correlation_id` = intent 멱등키, `reply_to`.
2. `trex-intent-rpc` 스레드(단일, prefetch=1)가 메시지 수신 → `IntentService.submit_buy/sell` 호출.
3. `intent_log.try_insert(intent_id, ...)` 가 dedup 게이트 — 중복 메시지는 기존 decision 재반환(슬롯 무전이).
4. 신규면 admission: `find_idle_slot` → `SlotService.transit_slot(idle→pending_buy)` → Postgres durable commit.
5. admission 응답(`status, slot_id?, reason?`)을 `reply_to` 큐로 publish → manual ack.
6. ack-after-commit 시퀀스: durable commit 이 ack 보다 먼저이므로 ack 전 크래시 시 재전달돼도 (3) 의 dedup 으로 재실행되지 않는다.
7. 체결/종료는 응답이 아니라 후속 이벤트(아래)로 전달 — admission 동기 + fill 비동기 2단계 시맨틱.
## Flow: `슬롯 이벤트 발행/구독` (RabbitMQ `trex.events`, BAT-007)
1. 체결: execution 루프가 `pending_buy→holding`(또는 `pending_sell→closed`)을 commit + ledger outbox enqueue (REQ-018/019).
2. 같은 스레드에서 `event_publisher.publish_event(rk, payload)` 호출.
- routing key: `{event_type}.{strategy_id}.{symbol}` (예: `slot_filled_buy.A.005930`).
- payload: 현재 상태 + `sequence_at`(슬롯 `updated_at` iso) 운반 → 수신자는 last-wins 적용 가능 (순서 보증 불요).
- publisher confirms 활성, 발행 실패는 로그 + DLQ 라우팅(`trex.events.dlq`). publish 실패가 슬롯 전이를 롤백하지 않는다.
3. 전략별 durable queue(`trex.events.strategy.{strategy_id}`)는 `*.{strategy_id}.#` 패턴으로 바인딩 → broker 단 필터로 자기 자산 이벤트만 수신.
4. 전략 down 중 적체된 메시지는 catch-up 으로 복구; TTL(1h) / max-length(10k) 초과는 `drop-head` + 후속 스냅샷 RPC reconcile.
## Flow: `일일 스냅샷 refresh` (BAT-008 / REQ-030)
데몬 lifespan 의 `trex-snapshot-scheduler` 스레드가 portfolio-hub universe 를 fetch 해 in-memory 스냅샷을 atomic-swap 으로 교체하고, 같은 패스에서 정합 검증을 수행한다.
1. boot: `SnapshotScheduler.run_once()` 한 번 호출 → 첫 universe 로딩(또는 degraded 마킹).
2. tick(`TREX_SNAPSHOT_TICK_SEC`, 기본 60s): 현재 KST 시각이 `TREX_SNAPSHOT_SCHEDULE_KST`(기본 07:30) 이상이고 오늘 아직 안 돌았으면 fire.
3. fire 단계: `PortfolioHubReadPort.whoami()` 로 daemon_id 도출(1회 캐시) → `get_portfolios(daemon_id)` 로 universe 페치 → `SnapshotStore.swap(items)` atomic 교체 → `reconcile_positions(store, broker)` 정합 검증.
4. 실패 분기: `PortfolioHubUnavailableError` → `degraded = portfolio_hub_unavailable`; 응답 파싱 실패 → `snapshot_fetch_failed`. 둘 다 다음 RPC 응답 envelope 에 즉시 노출(REQ-028 AC-2).
## Flow: `포지션 정합 검증` (BAT-008 / REQ-031)
`reconcile_positions` 는 portfolio-hub 보유 수량을 symbol 별로 합산해 broker `get_balance()` 결과와 비교한다.
1. `BrokerPort.get_balance() → list[BalanceItem(symbol, hldg_qty)]` 조회. KIS 어댑터는 `TTTC8434R`/`VTTC8434R` `inquire-balance`, paper 는 결정론 fill aggregate. **0수량 row 는 양쪽 모두 drop**(D-2 leftover 처리, Decision 7).
2. 자산을 `symbol` 로 그룹화해 `hub_total = sum(asset_quantity)` 계산 → broker 의 같은 symbol `hldg_qty` 와 비교.
3. 불일치: 해당 symbol 의 모든 portfolio_id 에 `SnapshotStore.set_warning(pid, True)` → 다음 envelope 부터 응답에서 마스킹. 첫 raise 시 한 번 로그.
4. 일치: 이전에 `warning` 이었던 자산은 자동 해제 → 다음 envelope 부터 다시 포함. resume 과 달리 자동·임시 overlay(영속 X).
## Flow: `pause/resume 제어` (BAT-008 / REQ-032)
운영자가 자산을 매매에서 제외/복귀시키는 두 경로(CLI + RPC)는 동일한 `snapshot_pause` Postgres 테이블을 가리킨다.
1. CLI: `trex-core pause <portfolio_id>` / `trex-core resume <portfolio_id>` → `PostgreSQLPauseStore.pause/resume` 호출(`portfolio_id` PK upsert/delete).
2. RPC: 전략 모듈/운영자가 `trex.pause.req` 큐에 `{action: pause|resume|list, portfolio_id, actor}` 발행 → `make_pause_rpc_handler` 가 같은 store 호출 → `{status, paused[], action}` 응답.
3. 응답 산출: `build_snapshot_envelope` 가 `pause_store.list_paused()` 를 매 요청마다 읽음 → `mask(pending | warning | pause)` 합성. resume 후 **다음 요청부터 즉시 복귀**(별도 reload 불필요).
4. 영속: daemon 재시작 + 일일 refresh 후에도 `snapshot_pause` row 유지(universe swap 은 pause 를 만지지 않음).
## Flow: `스냅샷 RPC 응답 산출` (BAT-008 / REQ-028, REQ-029)
`trex.snapshot.req` 메시지 처리.
1. 요청 payload 디코드: `{view ∈ {all, buyable, held}, strategy_key?, trading_date?}`.
2. `slot_service.compute_slot_capacity(today, total_slots)` → `available_slots = idle_count`.
3. `build_snapshot_envelope(store, pause_store, slot_repo, view, strategy_key, available_slots, trading_date)`:
- base 집합: ALL = `universe ∪ holding`, BUYABLE = `universe − (pending ∪ holding)`(`available_slots=0` 이면 `[]`), HELD = `holding 슬롯만`.
- 마스킹: pending(slot) OR warning(REQ-031) OR pause(REQ-032) — 어느 하나라도면 제외. slot overlay > portfolio-hub 보유 여부(불변식 A).
- `strategy_key` 일치(`==`) 직교 필터.
- degraded 상태가 있으면 `degraded=true + reason + sanitized message` 동봉.
4. envelope: `{assets[], view, available_slots, degraded, reason, message}` — `assets` 는 항상 배열, null 금지. 항목 필드는 `symbol/asset_id/cash_balance/asset_quantity/active_strategy_id/strategy_params`, **price 필드는 없음**.
## Flow: `per-slot 워치독 갱신` (BAT-010/011 / REQ-034, REQ-040)
전략 모듈의 생존 신호를 추적해 자율 안전 매도가 정상 운용 슬롯에 개입하지 않게 한다.
1. `trex-snapshot-rpc` 가 `trex.snapshot.req` 를 처리할 때 응답에 포함된 모든 `holding` 슬롯에 대해 `WatchdogStore.update(slot_id)`(`last_seen_at = now`)를 호출한다. 전략 모듈이 스냅샷을 폴링하는 한 그 슬롯은 "활성"으로 판정된다.
2. `update_thresholds` intent(REQ-040)도 수신 시 해당 슬롯 `last_seen_at` 을 리셋한다(임계값 갱신 = 생존 신호).
3. `WatchdogStore.is_expired(slot_id, TREX_WATCHDOG_TIMEOUT)` 가 True 이고 grace period(`TREX_WATCHDOG_GRACE_PERIOD`, 데몬 부팅 직후) 밖이면 그 슬롯은 워치독 만료 → forced_close/가격 트리거 대상이 된다.
4. in-memory store 라 데몬 재시작 시 초기화되며 grace period 가 자동 재시작된다(부팅 직후 오탐 방지).
## Flow: `forced_close_at 만기 자동 매도` (BAT-011 / REQ-037)
`trex-safety-sell` worker 가 `TREX_FORCED_CLOSE_CHECK_INTERVAL` 주기로 수행.
1. 오늘 `holding` 슬롯 중 워치독 만료(grace 밖)이고 `forced_close_at <= now(UTC)` 인 슬롯을 선별.
2. 대상 슬롯에 `SlotService.sell(actor="trex-safety-sell", reason="forced_close_at")` 호출 → `holding→pending_sell`(REQ-036). 이후 체결은 집행 루프가 처리.
3. 워치독 활성 슬롯·`forced_close_at=None` 슬롯은 제외. 이미 `pending_sell` 이면 idempotent(중복 매도 없음). KIS 호출 없음(DB + in-memory 비교만).
## Flow: `stop-loss·take-profit 가격 체크` (BAT-011 / REQ-038)
`trex-safety-sell` worker 가 `TREX_STOP_LOSS_POLL_INTERVAL` 주기로 수행.
1. `holding` + 워치독 만료 + (`stop_loss_price` 또는 `take_profit_price` 설정) 슬롯만 대상(워치독 활성 슬롯은 KIS 호출조차 안 함).
2. `BrokerPort.get_current_price(symbol)` → KIS `inquire-price`(`TTTC8397R` 실전 / `VTTC8397R` 모의)의 `stck_prpr`. paper 는 프로그램된 가격(미설정 시 None).
3. `현재가 <= stop_loss_price` → 손절(`reason="stop_loss"`), `현재가 >= take_profit_price` → 익절(`reason="take_profit"`)으로 `SlotService.sell()`. 가격 미수신(None) 시 skip(오탐 방지, 다음 tick 재시도).
4. 쿼터 가드: 시간당 호출 상한 `N × (3600 / interval)` 추정이 임계 초과 시 경고 로그(REQ-038 AC-8).
## Flow: `장 마감 강제 청산` (BAT-011 / REQ-039)
`TREX_MARKET_CLOSE_LIQUIDATION_TIME`(HH:MM KST, optional) 설정 시에만 동작.
1. `trex-safety-sell` worker 가 매 tick 현재 KST 시각을 평가 → 설정 시각 도달 + 같은 거래일 미발동(`last_liquidation_date` 가드)이면 발동.
2. 발동 시 **워치독·grace 무관** 모든 `holding` 슬롯에 `SlotService.sell(reason="market_close_liquidation")`. 이미 `pending_sell` 은 idempotent.
3. 미설정이면 동작하지 않음. in-memory 가드라 재시작 시 같은 날 재발동 가능(설계상 허용 — REQ-039 Notes).
## Flow: `broker degrade 분기` (BAT-007 / REQ-024)
1. transport connection drop → 어댑터가 `_mark_disconnected()` + 백오프(1s→2s→4s→8s→16s→30s cap) 재연결.
2. application 레이어는 `transport.health()` 가 `disconnected/reconnecting` 인 동안 신규 발행/RPC 가 `TransportUnavailableError` 로 빠르게 실패한다 → 전략은 매매 보류.
3. core 자율 안전 매도(stop-loss·take-profit / forced_close / 장 마감 청산)는 transport 와 무관하게 `trex-safety-sell` worker 가 수행한다(아래 safety-sell 흐름; dcy-007 REQ-037~041 구현, BAT-010/011). 이전 "A-5 미구현" 항목은 dcy-007 로 해소됨.
4. 로컬 버퍼링 없음 — broker 복구 후 옛 intent 가 자동 재생되지 않는다(전략이 신선 상태로 재판단).
## Flow: `주문 집행 루프` (백그라운드, BAT-004)
데몬 lifecycle 에서 시작되는 단일 백그라운드 execution 루프가 주기적으로(설정 간격) 수행:
1. dispatch: `find_pending_for_dispatch`(status pending_* AND `broker_order_id IS NULL`, `FOR UPDATE SKIP LOCKED`) → `BrokerPort.submit_order` → `mark_dispatched(broker_order_id)`(멱등 가드). 거부 시 `pending_buy→idle` / `pending_sell→holding` 롤백.
2. poll: `list_dispatched_pending`(broker_order_id NOT NULL) → `BrokerPort.poll_fill` → 체결 시 `pending_buy→holding`(avg_price/qty) / `pending_sell→closed`(+재진입), 거부 시 롤백. 전이 후 `clear_dispatch`.
3. reconcile(재기동): 서빙 전 `list_dispatched_pending` 재폴링으로 크래시 후 복구(중복 주문 0). 미디스패치(broker_order_id NULL) 슬롯은 다음 dispatch 가 1회만 송신.
- 모든 슬롯 쓰기는 SlotService(advisory lock) 단일 경로(불변식 A). 브로커 주문/취소는 BrokerPort 단일 세션만(불변식 B).
- 어댑터: paper(결정적 async 더블) / KIS(모의투자 검증, 실계좌 게이팅).
## Flow: `체결 → ledger outbox` (BAT-006)
집행 루프 fill 전이 직후 `ExecutionService` 가 ledger 기록을 outbox 에 적재해 슬롯 전이 critical path 와 ledger write 를 분리한다(원장 기록 단일 주체 = 집행 경로, NFR-3).
1. fill 감지: `BrokerPort.poll_fill` 이 `state="filled"` 와 결정적 `execution_id`(paper: `paper:{broker_order_id}:{filled_qty}` / KIS: `kis-daily-ccld:{ord_dt}:{ord_gno_brno}:{odno}:{tot_ccld_qty}`), 계산된 `fee`(BrokerPort `calculate_fee(side, amount)`), `tax=0` 을 운반.
2. 슬롯 전이: `pending_buy→holding`(avg_price/qty) 또는 `pending_sell→closed` 를 advisory lock 안에서 먼저 commit. 이 단계가 critical path.
3. ledger enqueue: 슬롯의 `portfolio_id`(== ledger `asset_id`, OQ-1 결정) 가 있으면 `LedgerTrade(asset_id, order_id, execution_id, side, qty, price, fee, tax, executed_at)` 을 `LedgerOutbox.enqueue` 로 적재. 행 식별자는 `correlation_id = {asset_id}:{order_id}:{execution_id}`(로컬 dedup). `portfolio_id` 부재면 ledger 기록 skip 후 슬롯 전이는 정상 유지.
4. `clear_dispatch` 로 `broker_order_id` 해제.
## Flow: `ledger writer` (백그라운드, BAT-006)
데몬 lifespan 에서 별도 백그라운드 워커가 ledger outbox 를 drain 한다. 슬롯 전이와 분리되어 eventually-consistent.
1. `LedgerOutbox.list_pending(limit)` 으로 pending work item 을 FIFO 로 가져온다.
2. row 의 현재 상태가 이미 `completed` 면 (e.g. 동시/재처리) `duplicate_completed` 로 마킹하고 어댑터를 호출하지 않는다.
3. `LedgerPort.record_trade(trade)` 호출.
- `LedgerWriteResult.NEW` → outcome `completed`, row status `completed`.
- `LedgerWriteResult.DUPLICATE_OK`(어댑터 server-side dedup) → outcome `duplicate_completed`, row status `completed`, ledger 중복 0.
- `LedgerConflictError`(portfolio-hub 409) → outcome `conflict_blocked`, row status `conflict_blocked` (자동 재시도 중단, 운영자 해소 대상).
- `LedgerRetryableError`(timeout/5xx/network) → outcome `retryable_failure`, row status `pending` 유지(다음 tick 재시도).
4. portfolio-hub 장기 장애에도 outbox 가 적재되어 무유실. 재시작 시 PostgreSQL `ledger_outbox.status='pending'` 행을 그대로 이어 처리한다.
## Shared Components
- SlotRepository Protocol :: 저장소 추상화. 구현체(InMemorySlotRepository, 향후 PostgreSQLSlotRepository)를 주입.
- SLOT_ADVISORY_LOCK_ID :: 모든 상태 변경을 직렬화하는 공유 advisory lock 식별자.
- transactional_advisory_lock :: 단일 트랜잭션 + advisory lock 컨텍스트(외부 I/O 금지).
## Flow: `slot 사전 할당 (preallocate_daily_slots)`
1. 호출자가 `preallocate_daily_slots(trading_date, count)` 를 호출한다(count > 0 검증).
2. SlotService 가 `transactional_advisory_lock(SLOT_ADVISORY_LOCK_ID)` 를 획득한다.
3. 저장소가 해당 trading_date 에 슬롯이 이미 있으면 0 을 반환(멱등), 없으면 `count` 개 idle 슬롯을 삽입한다.
4. 삽입된 슬롯 수를 반환한다.
## Flow: `slot 상태 전이 (transit_slot)`
1. 호출자가 `transit_slot(slot_id, from_state, to_state, reason, actor, **fields)` 를 호출한다.
2. (from_state, to_state) 가 8 전이 패밀리에 속하는지 먼저 검증한다(아니면 SlotTransitionError).
3. advisory lock 획득 후 슬롯을 fetch 한다(없으면 SlotNotFoundError).
4. 현재 status == from_state 인지 확인한다(아니면 SlotStateMismatchError — 레이스 감지).
5. to_state 의미에 맞춰 점유 필드를 계산하고 슬롯 행을 update 한다. (closed/pending_buy -> idle 은 점유 초기화)
6. closed -> idle 복구면 (trading_date, symbol) 재진입 카운트를 +1 한다.
7. slot_transition_log 에 전이 기록 1행을 append 하고 갱신된 슬롯을 반환한다.
8. 외부 I/O(브로커 등)는 lock 밖에서 호출자가 수행한다(lock 보유 중 금지).
## Flow Constraints
- 모든 상태 변경 API 는 advisory-locked 단일 트랜잭션 안에서 수행한다.
- lock 보유 중에는 외부 I/O 를 수행하지 않는다.
- slot_transition_log 는 append-only 다(UPDATE/DELETE 금지).
- 상태 전이는 read(현재 상태)-검증-write 가 동일 트랜잭션에서 원자적으로 이뤄져 레이스를 차단한다.
## Current Gaps / Planned Changes
- 실제 영속화(PostgreSQL) 흐름은 미구현 — 현재는 in-memory repository 로만 구동.
- CLI/데몬 런타임 흐름 없음(library). 상위 애플리케이션 통합 흐름은 영속화 단계 이후 정의.
- 강제 청산/EOD 같은 상위 운영 흐름은 이번 순수 도메인 범위 밖.
## Update Triggers
- Update this document when a command entry point changes.
- Update this document when a runtime orchestration path changes.
- Update this document when a batch changes the approved flow baseline.
## Change Log
- 2026-06-03 :: bootstrap-baseline 으로 초기 흐름 baseline 생성(slot 도메인 in-process API 흐름 기준).
- 2026-06-03 :: BAT-001 이식 완료 반영 — preallocate/transit 흐름이 `trex_core.domain.slot` 실제 코드로 구현됨(REQ-004). 영속화/CLI 흐름은 여전히 미구현(범위 밖).
- 2026-06-03 :: BAT-002 — 데몬 lifecycle + 매수/매도 intent HTTP 흐름 추가, Primary Runtime=daemon+library(REQ-005~009). 브로커 집행/safety floor 흐름은 후속 Discovery.
- 2026-06-04 :: BAT-004 — 브로커 집행 레이어 반영(BrokerPort/paper·KIS 어댑터, execution 루프, broker_order_id 실행큐·멱등, reconciliation) (REQ-011~015).
- 2026-06-04 :: BAT-006 — ledger 통합 반영(체결→ledger outbox enqueue, LedgerWriter retry/dedup/409 흐름, FillStatus.execution_id/fee 운반, slot 전이와 ledger write 분리) (REQ-017~020).
- 2026-06-05 :: BAT-007 — transport 전환(HTTP `/v1` API 제거, RabbitMQ 메시징 도입: 스냅샷/intent RPC + 이벤트 topic, sync+thread daemon, intent_log 멱등 ack-after-commit, publisher confirms + DLQ, per-strategy durable queue + TTL/max-length, broker degrade 분기) (REQ-021~026).
- 2026-06-06 :: BAT-008 — portfolio-hub 스냅샷 read 레이어 추가(`PortfolioHubReadPort` + hub_read/stub_read 어댑터, `SnapshotStore` in-memory + `snapshot_pause` Postgres, `SnapshotScheduler` 일일 refresh, `reconcile_positions` 정합 검증 + `BrokerPort.get_balance()` 확장, `trex.snapshot.req` envelope 응답 + degraded 신호, `trex.pause.req` 제어 RPC, `trex-core` CLI) (REQ-027~033; 옵션 D AC-2/AC-3 은 portfolio-hub#8 배포 후 별도 구현).