fix(cli): BAT-022 slots 명령이 universe 대신 실제 슬롯 상태 표시 (dcy-016 후속)

`trex-core slots` 가 snapshot RPC 의 universe `assets`(portfolio)를 슬롯 상태로
오인 표시하던 갭 정정. snapshot envelope 이 슬롯 상태를 운반하지 않아 발생
(10개 슬롯인데 universe 1개·status 없음 — BAT-007 HTTP 슬롯 read 제거 후
슬롯 상태 read 표면 부재가 근인).

- REQ-028 (CHG-20260608-01): snapshot envelope 에 read-only per-slot `slots[]`
  additive 추가(AC-4). strategy `assets[]`(universe, no-price) 불변.
- REQ-032 (CHG-20260608-03): `_cmd_slots` 가 envelope `slots[]`(실제 슬롯 상태)
  표시 + `--status` 를 슬롯 status 기준 client-side 필터 + watchdog 결합.
  `universe` 는 `assets[]` 유지. `universe --json` 도 universe 데이터로 scope.
- canonical `portfolio.slot_to_dict` 단일화 — BAT-007 stub / BAT-008 envelope
  두 경로가 동일 슬롯 스키마(외부명 `entry_price` 유지, dcy-012 호환).
- interface-contract 에 `slots[]` 계약 + `assets[]` vs `slots[]` 구분 반영.

검증: 단위 338 passed + ruff clean + 신규 코드 데몬으로 실 PostgreSQL/RabbitMQ
운영 재현 PASS(slots=10·status·DB 일치·universe 분리·비침습). REQ-028/032
Implemented, BAT-022 release-candidate. pytest 인프라 게이트는 Human Approval
Memo 수용(운영 재현이 실 경로 커버).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-08 19:54:42 +09:00
co-authored by Claude Opus 4.8
parent a0a149114d
commit 63da87c9b3
16 changed files with 574 additions and 46 deletions
+20 -2
View File
@@ -161,7 +161,25 @@ sell payload 는 `{"intent_type":"sell", "intent_id":..., "slot_id":..., "reason
"message": str | null # sanitized — no credentials/raw exceptions
}
```
**No `price` field on items** (REQ-028 AC-3 / REQ-027 AC-1).
**No `price` field on `assets[]` items** (REQ-028 AC-3 / REQ-027 AC-1) — strategy-facing universe.
- 슬롯 상태 (REQ-028 AC-4, BAT-022): envelope 은 strategy `assets[]`(universe) 와 **별도로** 데몬의 per-slot 상태를 운반한다. 운영자용(`trex-core slots`)이며 universe 와 명확히 구분된다.
```
"slots": [ # always a list (REQ-028 AC-4, never null)
{ "slot_id": str,
"trading_date": "YYYY-MM-DD",
"status": "idle"|"pending_buy"|"holding"|"pending_sell"|"closed",
"symbol": str | null,
"qty": number | null,
"avg_price": number | null, # 매수 체결가/평단
"entry_price": number | null, # pending 주문 지정가 (dcy-012 외부 호환명; 내부 limit_price)
"allocated_capital": number | null,
"strategy_id": str | null, "sell_strategy_id": str | null,
"portfolio_id": str | null, # == asset_id
"forced_close_at": "ISO-8601" | null }
]
```
- BAT-007 stub 경로와 BAT-008 envelope 경로가 **동일 `slots[]` 스키마**(`portfolio.slot_to_dict`)를 낸다. `assets[]`(portfolio 단위, universe, no-price) 와 독립이며 strategy 소비자는 무시 가능(additive, 하위호환). `slots[]` 는 operator-facing 이라 `entry_price`/`avg_price` 가격을 노출한다(no-price 규칙은 `assets[]` 한정).
- 워치독 상태 (dcy-017/REQ-034, 선택 필드): 응답에 holding 슬롯의 워치독 상태를 read-only 로 동봉한다.
```
@@ -182,7 +200,7 @@ sell payload 는 `{"intent_type":"sell", "intent_id":..., "slot_id":..., "reason
| CLI | RPC 큐 | payload | 비고 |
|---|---|---|---|
| `universe` | `trex.snapshot.req[.{instance}]` | `{view:"all", actor:"operator", touch_watchdog:false}` | 자산 목록 조회(워치독 억제) |
| `slots [--status held\|buyable\|all]` | `trex.snapshot.req[.{instance}]` | `{view, actor:"operator", touch_watchdog:false}` | `holding`→`held` view |
| `slots [--status all\|holding\|buyable\|pending]` | `trex.snapshot.req[.{instance}]` | `{view:"all", actor:"operator", touch_watchdog:false}` | envelope `slots[]`(실제 슬롯 상태) 표시(REQ-028 AC-4/REQ-032, BAT-022) — universe `assets[]` 아님. `--status` 는 슬롯 status 기준 client-side 필터 |
| `enter <portfolio_id> [--capital/--qty/...]` | `trex.intent.req[.{instance}]` | buy intent(`portfolio_id` 필수, symbol 서버 resolve) | 안전장치 필수 |
| `exit <slot_id> [--price]` | `trex.intent.req[.{instance}]` | sell intent(`slot_id`, optional `limit_price`) | 가격 미지정 시 시장가 |
| `pause/resume/paused` | (RPC 아님) | — | direct-DB 유지(OQ-3) |