feat(safety-sell): dcy-017 워치독 소유권 가시화 — snapshot 상태 + 발동 로깅 (BAT-021)

전략 이탈로 슬롯이 내부 청산 루틴(워치독) 소유로 넘어간 상태가 무성이라
운영자가 인지할 수 없던 문제(issue #12)를 가시화한다.

거버넌스: 전부 change-req (신규 REQ 없음, dcy-017 OQ-3; Redis 미채택).
- REQ-034/021: WatchdogStore.last_seen + snapshot RPC 응답에 holding 슬롯
  워치독 상태(expired/last_seen) read-only 동봉(best-effort)
- REQ-032: trex-core slots 가 expired 슬롯을 WATCHDOG-OWNED(전략 이탈) 표시
- REQ-037/038/039: safety-sell 워치독 소유 진입(WARN)/이탈(INFO) + 발동
  (forced_close·stop_loss=WARN, take_profit·market_close=INFO) 로그 +
  in-process 카운터(SafetySellService.counters), 구조화 필드(slot_id/
  portfolio_id/reason/phase/instance_key). per-tick 스킵은 카운터만
- REQ-042: runtime-flows/interface-contract 정합

best-effort 격리(NFR-1): 모든 가시화는 예외 격리 — 워치독 판정·청산 불변.
dcy-016 억제 읽기로 관측이 워치독을 리셋하지 않음(비침습).

검증: 단위 335 passed/34 skipped, 인프라 게이트(PG+RabbitMQ) 369 passed, ruff clean.
신규 test_safety_sell_visibility(6) + snapshot watchdog(1) + CLI(1). BAT-021 rc.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-08 17:10:26 +09:00
co-authored by Claude Opus 4.8
parent d09b93195f
commit 6d7bca1460
26 changed files with 1022 additions and 10 deletions
@@ -149,3 +149,41 @@ dcy-007에서 정의된 자율 안전 매도 기능(REQ-034~041)의 구현 완
- Existing Implementation Invalidated: no
- Status Recommendation: keep-current (Implemented)
- Follow-up Action: draft-release 로 BAT-011 배포 편성.
### CHG-20260608-01
- Date: 2026-06-08
- Author: Justin Ko
#### Change Summary
- (change-req, dcy-017) baseline 문서를 워치독 가시화에 맞춰 정합: `docs/runtime-flows.md`(워치독 상태 노출 + safety-sell 가시화 flow), `docs/interface-contract.md`(snapshot 응답 `watchdog` 필드). BAT-021 정합 완료 → Implemented 유지.
#### Intent
- 코드(watchdog/messaging_handlers/safety_sell/cli) 변경과 baseline 문서를 일치시켜 운영자 참조 정합 유지.
#### Acceptance Criteria Delta
- Updated:
- runtime-flows/interface-contract 의 워치독 가시화·safety-sell 발동 가시화 기술 정합.
#### Impacted Area Delta
- Docs: docs/runtime-flows.md, docs/interface-contract.md
#### Delivery Trace Delta
- Batches: BAT-021
- Releases: none
#### Revalidation Impact
- Reverification Needed: no (문서 정합)
- Existing Implementation Invalidated: no
- Status Recommendation: Implemented (change-req → BAT-021 정합)
- Follow-up Action: confirm-req-implemented(BAT-021).
#### Validation Plan
- BAT-021: 코드-문서 diff 대조.
@@ -222,3 +222,43 @@
#### Validation Plan
- BAT-020: 기본 요청 touch / operator 요청 suppress 단위(caplog/recorder).
### CHG-20260608-03
- Date: 2026-06-08
- Author: Justin Ko
#### Change Summary
- (change-req, dcy-017/REQ-034 연계) snapshot RPC 응답에 `watchdog` 필드(holding 슬롯 `{slot_id, portfolio_id, symbol, expired, last_seen}` 배열) 추가(best-effort, 실패 시 생략). 메시지 계약 확장. BAT-021 재구현·재검증 완료 → Implemented 유지.
#### Intent
- 워치독 소유 상태를 운영자 CLI 가 관측할 수 있도록 스냅샷 응답 계약에 read-only 필드 정식화.
#### Acceptance Criteria Delta
- Added:
- snapshot 응답 optional `watchdog[]` 필드(read-only).
#### Impacted Area Delta
- Modules: `trex_core/application/messaging_handlers.py`(_attach_watchdog)
- Docs: `docs/interface-contract.md`
- Tests: tests/application/test_messaging_snapshot_consumer.py
#### Delivery Trace Delta
- Batches: BAT-021
- Releases: none
#### Revalidation Impact
- Reverification Needed: yes (BAT-021 에서 완료)
- Existing Implementation Invalidated: no (additive 필드)
- Status Recommendation: Implemented (change-req → BAT-021 재검증)
- Follow-up Action: confirm-req-implemented(BAT-021).
#### Validation Plan
- BAT-021: snapshot 응답 watchdog 필드 shape 단위.
@@ -148,3 +148,42 @@
- Existing Implementation Invalidated: no
- Status Recommendation: keep-current (Implemented)
- Follow-up Action: draft-release 로 BAT-011 배포 편성.
### CHG-20260608-01
- Date: 2026-06-08
- Author: Justin Ko
#### Change Summary
- (change-req, dcy-017/issue #12) safety-sell 가시화: 워치독 소유 진입(전략 이탈)=WARN/이탈(복귀)=INFO(`_update_ownership`, run_once 매 tick), forced_close 발동=WARN(`_log_fire`) + in-process 카운터(`SafetySellService.counters`). 구조화 필드(slot_id/portfolio_id/reason/phase/instance_key). best-effort(가시화 실패가 청산을 막지 않음). 트리거 판정 동작 불변. BAT-021 재구현·재검증 완료 → Implemented 유지.
#### Intent
- 전략 이탈→안전망 소유, forced_close 발동을 무성에서 가시화(OQ-2 차등 매트릭스).
#### Acceptance Criteria Delta
- Added:
- 워치독 소유 진입/이탈 + forced_close 발동 로그/카운터. per-tick 스킵은 카운터만.
#### Impacted Area Delta
- Modules: `trex_core/application/safety_sell.py`(_update_ownership/_log_fire/counters/instance_key), `trex_core/runtime/daemon.py`(instance_key 배선)
- Tests: tests/application/test_safety_sell_visibility.py
#### Delivery Trace Delta
- Batches: BAT-021
- Releases: none
#### Revalidation Impact
- Reverification Needed: yes (BAT-021 에서 완료)
- Existing Implementation Invalidated: no (관측만 추가, 트리거 불변)
- Status Recommendation: Implemented (change-req → BAT-021 재검증)
- Follow-up Action: confirm-req-implemented(BAT-021).
#### Validation Plan
- BAT-021: 소유 진입 WARN/이탈 INFO/forced_close WARN + 카운터 단위(caplog).
@@ -235,3 +235,42 @@
- Existing Implementation Invalidated: no
- Status Recommendation: keep-current (Implemented)
- Follow-up Action: REL-003 재확인 후 배포.
### CHG-20260608-01
- Date: 2026-06-08
- Author: Justin Ko
#### Change Summary
- (change-req, dcy-017/issue #12) SL/TP 발동 가시화: `stop_loss` 발동=WARN, `take_profit` 발동=INFO(`_log_fire`) + 카운터(`fire:stop_loss`/`fire:take_profit`). 구조화 필드(slot_id/portfolio_id/reason/phase/instance_key). 판정·KIS polling 동작 불변(관측만). BAT-021 재구현·재검증 완료 → Implemented 유지.
#### Intent
- 손절/익절 발동을 무성에서 가시화(OQ-2: stop_loss=WARN 주의사건, take_profit=INFO).
#### Acceptance Criteria Delta
- Added:
- stop_loss(WARN)/take_profit(INFO) 발동 로그 + 카운터.
#### Impacted Area Delta
- Modules: `trex_core/application/safety_sell.py`(check_prices 발동 가시화)
- Tests: tests/application/test_safety_sell_visibility.py::test_take_profit_fire_logs_info
#### Delivery Trace Delta
- Batches: BAT-021
- Releases: none
#### Revalidation Impact
- Reverification Needed: yes (BAT-021 에서 완료)
- Existing Implementation Invalidated: no (관측만 추가)
- Status Recommendation: Implemented (change-req → BAT-021 재검증)
- Follow-up Action: confirm-req-implemented(BAT-021).
#### Validation Plan
- BAT-021: take_profit 발동 INFO + 카운터 단위.
@@ -145,3 +145,42 @@
- Existing Implementation Invalidated: no
- Status Recommendation: keep-current (Implemented)
- Follow-up Action: draft-release 로 BAT-011 배포 편성.
### CHG-20260608-01
- Date: 2026-06-08
- Author: Justin Ko
#### Change Summary
- (change-req, dcy-017/issue #12) market_close 청산 발동 가시화: `market_close_liquidation` 발동=INFO(`_log_fire`) + 카운터. 구조화 필드 포함. 청산 동작(워치독/grace 무관, 1일 1회) 불변. BAT-021 재구현·재검증 완료 → Implemented 유지.
#### Intent
- 장 마감 청산 발동을 가시화(OQ-2: market_close=INFO).
#### Acceptance Criteria Delta
- Added:
- market_close 발동 INFO 로그 + 카운터.
#### Impacted Area Delta
- Modules: `trex_core/application/safety_sell.py`(check_market_close 발동 가시화)
- Tests: tests/application/test_safety_sell_visibility.py(가시화 전반) + 기존 market_close 회귀
#### Delivery Trace Delta
- Batches: BAT-021
- Releases: none
#### Revalidation Impact
- Reverification Needed: yes (BAT-021 에서 완료)
- Existing Implementation Invalidated: no (관측만 추가)
- Status Recommendation: Implemented (change-req → BAT-021 재검증)
- Follow-up Action: confirm-req-implemented(BAT-021).
#### Validation Plan
- BAT-021: market_close 발동 시 INFO + 카운터(기존 청산 회귀 동반).
@@ -174,3 +174,42 @@
#### Validation Plan
- BAT-020: CLI universe/slots/enter/exit RPC payload·안전장치·라우팅·--json 단위.
### CHG-20260608-02
- Date: 2026-06-08
- Author: Justin Ko
#### Change Summary
- (change-req, dcy-017) `trex-core slots` 가 snapshot 응답의 `watchdog` 필드를 읽어 `expired=true` 슬롯을 **WATCHDOG-OWNED(전략 이탈)** 로 표시(+`watchdog_owned` 카운트). dcy-016 `slots` 확장. BAT-021 재구현·재검증 완료 → Implemented 유지.
#### Intent
- 운영자가 CLI 로 워치독 소유(전략 이탈) 슬롯을 즉시 인지.
#### Acceptance Criteria Delta
- Updated:
- `slots` 출력에 워치독 소유 표시(snapshot watchdog 필드 기반).
#### Impacted Area Delta
- Modules: `trex_core/runtime/cli.py`(_cmd_slots watchdog 표시)
- Tests: tests/runtime/test_cli_operator.py::test_slots_marks_watchdog_owned
#### Delivery Trace Delta
- Batches: BAT-021
- Releases: none
#### Revalidation Impact
- Reverification Needed: yes (BAT-021 에서 완료)
- Existing Implementation Invalidated: no (표시 추가)
- Status Recommendation: Implemented (change-req → BAT-021 재검증)
- Follow-up Action: confirm-req-implemented(BAT-021).
#### Validation Plan
- BAT-021: slots watchdog-owned 표시 단위.
@@ -152,3 +152,42 @@
#### Validation Plan
- BAT-020: operator 요청 시 watchdog.update 미호출, 기본 요청 시 호출 단위.
### CHG-20260608-02
- Date: 2026-06-08
- Author: Justin Ko
#### Change Summary
- (change-req, dcy-017/issue #12) 워치독 상태 가시화: `WatchdogStore.last_seen(slot_id)` read-only getter 추가, snapshot RPC 응답에 holding 슬롯 워치독 상태(`expired`/`last_seen`) read-only 동봉(best-effort). 판정 로직·timeout 불변(관측만 추가). BAT-021 재구현·재검증 완료 → Implemented 유지.
#### Intent
- 전략 이탈로 슬롯이 안전망(워치독) 소유로 넘어간 상태를 운영자가 인지 가능하게(무성 → 가시화).
#### Acceptance Criteria Delta
- Added:
- snapshot 응답 워치독 상태 노출 + `last_seen` getter. 판정 동작 불변.
#### Impacted Area Delta
- Modules: `trex_core/application/watchdog.py`(last_seen), `trex_core/application/messaging_handlers.py`(_attach_watchdog)
- Tests: tests/application/test_messaging_snapshot_consumer.py::test_snapshot_exposes_watchdog_state_for_holding_slots
#### Delivery Trace Delta
- Batches: BAT-021
- Releases: none
#### Revalidation Impact
- Reverification Needed: yes (BAT-021 에서 완료)
- Existing Implementation Invalidated: no (관측만 추가, 판정 불변)
- Status Recommendation: Implemented (change-req → BAT-021 재검증)
- Follow-up Action: confirm-req-implemented(BAT-021).
#### Validation Plan
- BAT-021: 워치독 만료 holding 슬롯의 expired/last_seen 노출 단위.
+1 -1
View File
@@ -139,8 +139,8 @@
## Recent Change Log Summary
- 2026-06-08 :: REQ-034/021/037/038/039/032/042 change-req (dcy-017, BAT-021): 전략 이탈로 슬롯이 워치독(안전망) 소유로 넘어간 상태를 가시화(issue #12). snapshot RPC 응답에 holding 슬롯 워치독 상태(expired/last_seen) read-only 동봉(best-effort) + `WatchdogStore.last_seen`(REQ-034/021), `trex-core slots` WATCHDOG-OWNED 표시(REQ-032), safety-sell 워치독 소유 진입(WARN)/이탈(INFO)·발동(forced_close·stop_loss=WARN, take_profit·market_close=INFO) 로그+in-process 카운터(REQ-037/038/039), baseline 2문서 정합(REQ-042). best-effort(청산 무차단), Redis 미채택. 전부 change-req(신규 REQ 없음, OQ-3). 단위 335 passed, 인프라 게이트 369 passed, ruff clean. (change-req/confirm-req-implemented)
- 2026-06-08 :: REQ-032/022/021/034/026 change-req (dcy-016, BAT-020): 운영자 CLI 확장 — `universe`/`slots`(snapshot RPC)·`enter`(buy intent RPC)·`exit`(sell intent RPC) 추가(전부 데몬 RPC 통일, 단일 writer 보존, DB 직접 쓰기 없음)(REQ-032), CLI request-reply RPC 클라이언트 재사용(REQ-022), snapshot 요청 워치독 억제 플래그(`touch_watchdog`/`actor="operator"`)로 운영자 조회가 전략 생존 신호로 오인되지 않게(REQ-021/034, R-4), enter/exit 안전장치(확인 프롬프트/`--yes`/live 이중 ack/`--dry-run`)+`--json`+인스턴스 라우팅, baseline 2문서 정합(REQ-026). pause/resume 는 direct-DB 유지. 전부 change-req(신규 REQ 없음, OQ-3). 단위 327 passed, 인프라 게이트 361 passed, ruff clean. (change-req/confirm-req-implemented)
- 2026-06-08 :: REQ-017/019/018/020 change-req (dcy-015, BAT-019): portfolio-hub 404(미등록 포트폴리오)가 retryable 로 분류되어 백오프·상한 없이 영구 재시도되던 버그(issue #13) 정정. 404/400/422→`LedgerPermanentError`(REQ-017), outbox 영속→터미널 `delivery_failed` 즉시 + retryable 지수 백오프+jitter+`next_retry_at`+max_attempts→dead-letter + `list_pending` 백오프 스킵 + 마이그레이션 007(status enum/컬럼) + 백오프 env(REQ-019), 터미널 WARN+카운터 가시화(REQ-018), baseline 3문서 정합(REQ-020). 전부 change-req(신규 REQ 없음, OQ-3). 단위 311 passed, 인프라 게이트 345 passed, ruff clean. (change-req/confirm-req-implemented)
- 2026-06-08 :: REQ-005/048/009 change-req (dcy-014, BAT-018): 당일 슬롯 생성 기준을 시스템 UTC 날짜에서 설정 타임존(기본 KST) 기준 `trading_date` 로 분리(timestamp 는 UTC 유지) + 데몬 1회 preallocation 을 `DailyPreallocationScheduler`(TZ 일자당 1회 멱등 fire, 재시작 없이 다음날 자동 생성)로 대체(REQ-005), TOML `trading_timezone`/`daily_preallocation_time` 키(REQ-048), runtime-flows/data-model 정합(REQ-009). 전부 change-req(신규 REQ 없음, OQ-3; 스케줄러는 REQ-030 패턴 준용). 단위 302 passed, 인프라 게이트 334 passed, ruff clean. (change-req/confirm-req-implemented)
- 2026-06-08 :: REQ-012/013/018/031/014/015 change-req (dcy-013, BAT-017): KIS 외부 주문 취소 시 슬롯이 pending_sell 에 영구 갇히던 버그(issue #9) 정정. KIS `poll_fill` 이 `cncl_yn` 파싱해 외부 취소 감지 — 체결 0 취소→`cancelled`(롤백 자동 복구), 부분 체결 후 취소→`is_partial_cancel`(체결분 원장 1회+qty 차감+holding 복귀, OQ-1 A안)(REQ-012/013/018), 잔여 포지션 정합 연계(REQ-031), pending stale 워치독 Alert-only(`TREX_PENDING_STALE_THRESHOLD`, WARN+카운터, 상태 미변경)(REQ-014), baseline 2문서 정합(REQ-015). 전부 change-req(신규 REQ 없음, OQ-3). 단위 294 passed, 인프라 게이트 326 passed, ruff clean. (change-req/confirm-req-implemented)
- 2026-06-08 :: REQ-013/007/036/002/006/003/015 change-req (dcy-012, BAT-016): 매도 주문이 매수가(entry_price) 지정가로 나가던 버그(issue #10) 정정. 슬롯 `entry_price`→`limit_price`(side 무관 pending 지정가) rename + pending 이탈 clear(REQ-002), 컬럼 rename 마이그레이션 006(REQ-006), `_dispatch` 가 slot.limit_price 단일 필드로 가격/주문유형 결정(REQ-013), SellIntent/sell payload optional limit_price·_admit_sell 운반(REQ-007), SlotService.sell optional 가격→자율 매도 시장가(REQ-036), 컬럼 계약 가드(REQ-003), baseline 3문서 정합(REQ-015). 전부 change-req(신규 REQ 없음, OQ-3). 단위 286 passed, 인프라 게이트 318 passed, ruff clean. (change-req/confirm-req-implemented)