Commit Graph
58 Commits
Author SHA1 Message Date
Hermes Agent a2be76b199 feat(ledger): quiet idle outbox polling 2026-09-20 21:57:30 +09:00
Hermes Agent c66acf6e47 fix: preserve sell event routing owner 2026-09-20 20:34:00 +09:00
Hermes Agent c70ac0fb24 feat: auto-reconcile stale holding mirrors
Closes #94
2026-09-14 12:55:04 +09:00
Hermes Agent adf131f19c chore(release): prepare v0.7.0 2026-09-13 17:01:20 +09:00
copilot-swe-agent[bot]andgenichin 1f728f4d7d fix(broker): restore main wiring and add production retry cadence + status surface
Co-authored-by: genichin <4282227+genichin@users.noreply.github.com>
2026-09-13 06:25:28 +00:00
copilot-swe-agent[bot]andgenichin c73e382b70 Merge remote-tracking branch 'origin/main' into copilot/feat-reconcile-unmanaged-position-durable-lifecycl
# Conflicts:
#	.proofline/criteria/ac-0029.md
#	.proofline/identities.json
#	tests/adapters/persistence/test_migration_and_drift.py

Co-authored-by: genichin <4282227+genichin@users.noreply.github.com>
2026-09-13 02:53:22 +00:00
copilot-swe-agent[bot]andgenichin 591db7adb7 Fix unmanaged reconciliation review blockers
Co-authored-by: genichin <4282227+genichin@users.noreply.github.com>
2026-09-13 02:21:37 +00:00
genichin 3cb0fdc303 Merge pull request #90 from fourblocks2020/copilot/bug-ledger-shared-outbox-consumption
Isolate ledger outbox delivery by instance and add lease-based claims
2026-09-13 10:48:57 +09:00
copilot-swe-agent[bot]andgenichin f2b825e720 test: cover unmanaged position lifecycle flow
Co-authored-by: genichin <4282227+genichin@users.noreply.github.com>
2026-09-12 13:27:12 +00:00
copilot-swe-agent[bot]andgenichin 09b1b4f4c3 fix: isolate ledger outbox delivery by instance
Co-authored-by: genichin <4282227+genichin@users.noreply.github.com>
2026-09-12 13:24:58 +00:00
copilot-swe-agent[bot]andgenichin fe51d211d9 feat: persist unmanaged position lifecycle state
Co-authored-by: genichin <4282227+genichin@users.noreply.github.com>
2026-09-12 13:24:51 +00:00
copilot-swe-agent[bot]andgenichin 45a5e4157d fix: preserve multi-cause reconcile warnings and align slot blocking
Co-authored-by: genichin <4282227+genichin@users.noreply.github.com>
2026-09-12 13:22:40 +00:00
copilot-swe-agent[bot]andgenichin 65df10542b fix(execution): KIS child cancel identity binding, sanitized logging, PG cancel SQL tests
Co-authored-by: genichin <4282227+genichin@users.noreply.github.com>
2026-09-01 12:06:45 +00:00
copilot-swe-agent[bot]andgenichin f0ac909958 fix(execution): cancel SQL nullable type error, KIS child cancel detection, worker exception logging
Co-authored-by: genichin <4282227+genichin@users.noreply.github.com>
2026-09-01 03:33:38 +00:00
Hermes Agent 3854aabd0d fix strategy state persistence corrections 2026-08-29 16:11:02 +09:00
copilot-swe-agent[bot]andgenichin a9e88f9770 feat(intents): emit warn alarm on first reconcile_warned rejection
Co-authored-by: genichin <4282227+genichin@users.noreply.github.com>
2026-08-26 04:19:14 +00:00
copilot-swe-agent[bot]andgenichin f1864a6aa8 fix(ac-0024): address 4 blockers: NUL byte in C identity, instance_key isolation, clear_warned gate, production wiring
Co-authored-by: genichin <4282227+genichin@users.noreply.github.com>
2026-08-26 00:20:09 +00:00
copilot-swe-agent[bot]andgenichin 30bcbe1d12 feat: use broker-authoritative qty in slots RPC holdings
Co-authored-by: genichin <4282227+genichin@users.noreply.github.com>
2026-08-24 14:15:43 +00:00
copilot-swe-agent[bot]andgenichin 48dfc3e76f fix: harden skipped ledger resolution paths
Co-authored-by: genichin <4282227+genichin@users.noreply.github.com>
2026-08-24 13:38:04 +00:00
copilot-swe-agent[bot]andgenichin 76cde2edd0 feat: persist skipped ledger fills
Co-authored-by: genichin <4282227+genichin@users.noreply.github.com>
2026-08-24 13:23:06 +00:00
jordan 0266992238 feat: add bounded KIS account read recovery 2026-07-29 17:40:01 +09:00
Hermes Agent 216e462df4 fix(broker): enforce operator broker truth boundary 2026-07-15 08:07:21 +09:00
genichin 9e787c81a2 Merge pull request #35 from fourblocks2020/feat/dcy-036-kis-credential-inject
feat(dcy-036/bat-054): KISBroker 양의 경로 — 데몬-스코프 KIS 인증정보 실거래 주입
2026-06-30 13:54:49 +09:00
genichinandClaude Opus 4.8 de97bc842d fix(slot-filled): 매도 체결 이벤트 NO_ROUTE 해소 — strategy_id 전이 전 캡처
INACTIVE 전이가 strategy_id를 None 초기화(REQ-057) → 라우팅키 slot_filled_sell._.{sym}
→ orb/tsl 바인딩 불일치 NO_ROUTE/DLQ. _on_filled/_on_partial_cancel에서 전이 직전
strategy_id 캡처해 emitter에 전달. wire 계약 불변(포맷 유지·값 교정). QC opus PASS, 747 passed/회귀 0.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VHtM5Ws1HKCKQ4JGi6GRb5
2026-06-29 09:38:45 +09:00
genichinandClaude Sonnet 4.6 6b066e19cb feat(bat-054): 그날-시작 KIS 인증정보 양의 경로 주입 — KISBroker reinitialize (REQ-113 AC-2/AC-4, REQ-115 AC-1)
BAT-053 스캐폴드에서 연기된 양의 경로 완성:
- KISBroker.reinitialize(credential): app_key/app_secret/account_ref 교체 + L1 token 무효화
- SnapshotScheduler에 kis_reinit_callback 주입 지점 추가; 그날-시작 성공 시 callback 호출
- daemon.py에 _build_kis_reinit_callback() 헬퍼 + SnapshotScheduler 조립 수정
- 신규 테스트 12개 추가: 757 passed / 47 skipped (기존 745 대비 +12, 회귀 0)

안전 불변식 유지:
- credential_fetch_enabled=False(기본) 경로 byte-for-byte 보존
- fail-closed 불변식 미파괴 (reinit 실패 시 entry_allowed 유지)
- AMQP wire 계약 변경 없음

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VHtM5Ws1HKCKQ4JGi6GRb5
2026-06-28 19:51:48 +09:00
genichinandClaude Sonnet 4.6 e4e5c70422 feat(bat-053): 그날-시작 KIS 인증정보 fetch + fail-safe 로드 (REQ-112~115)
REQ-112: BrokerCredentialPayload/TradingContext 데이터클래스 + PortfolioHubReadPort.fetch_broker_credential() 포트 메서드 + hub_read 어댑터(_fetch_user_info 헬퍼 추출) + stub_read 결정론 stub
REQ-113: DaemonInstanceConfig/DaemonConfig.credential_fetch_enabled opt-in(기본 False) + TOML 파싱 + break-glass(TREX_KIS_BROKER_BREAK_GLASS) 감지
REQ-114: SnapshotScheduler._maybe_fetch_credential() — fail-closed 3조건(unavailable/broker-null/trading_disabled) + CRITICAL 알람 DAY_START_LOAD_FAILED + IntentService entry_allowed_fn 게이트(BuyIntent 차단, SellIntent 통과)
REQ-115: 그날 고정 캐시(_cached_credential/_credential_date) — 동일 날짜 재fetch 방지 + app_secret 메모리 한정(Redis 미기록)

스텁 기반 구현: 실 portfolio-hub/네트워크 없이 AC 전부 단위 테스트.
회귀: credential_fetch_enabled=False(기본) 경로 byte-for-byte 보존.
745 passed, 47 skipped (0 failed).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VHtM5Ws1HKCKQ4JGi6GRb5
2026-06-28 11:00:18 +09:00
genichinandClaude Sonnet 4.6 c193388198 feat(core-A): reconcile-warning durable store + blocking gate (BAT-050, REQ-102/103/104/107/108)
B/C 불일치 시 슬롯을 durable 차단(ReconcileWarningStore)하고 전략 intent 경로에
pause∨reconcile_warned 게이트를 추가한다. 안전망(safety-sell/kill-switch/watchdog)
경로는 SlotService 직접 호출로 게이트를 구조적으로 우회(OQ-B 확정).
SlotState enum/전이표/wire 계약 필드는 불변, reconcile_warned 필드만 additive 추가.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VHtM5Ws1HKCKQ4JGi6GRb5
2026-06-26 00:43:04 +09:00
genichinandClaude Opus 4.8 d4c5fcbba1 feat(alarm): trex-core 알람 emitter + trex.alerts exchange (dcy-033 BAT-049, Implemented)
AlarmEmitter(application, best-effort 비차단)+trex.alerts topic exchange 가산·격리+8 CRITICAL code 배선. 608 passed, opus QC PASS, 팀장 e2e(텔레그램 HTTP200) 통과. REQ-098~101 Implemented.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VHtM5Ws1HKCKQ4JGi6GRb5
2026-06-21 19:25:55 +09:00
genichinandClaude Opus 4.8 d182464b7a feat(messaging): OPS-005 이벤트 발행 연결 신뢰성 (BAT-047)
발행 전용 연결의 idle heartbeat 사망 후 첫 발행 유실 결함 제거.
- REQ-091 발행 재시도 + 강제 재연결(_force_reconnect, is_open 불신)
- REQ-092 idle keepalive(execution-loop 단일스레드 틱, process_data_events time_limit=0)
- REQ-093 DLQ 가시성 + emit 실패 ERROR 경보(구조화 로그, dcy-029 재사용)
- REQ-094 baseline 문서 정합(runtime-flows/operations/project-structure)

독립 opus QC PASS-WITH-NOTES → 시정 2건(ruff F401, AC-3 프로덕션 경로) 완료.
554 passed/47 skipped/회귀 0/ruff clean. NFR-1 wire 무회귀(interface-contract diff 0).
BAT-047 release-candidate, REQ-091~094 Implemented. dcy-031. M2 트랙B.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VHtM5Ws1HKCKQ4JGi6GRb5
2026-06-19 22:39:43 +09:00
genichinandClaude Opus 4.8 d8d9021260 feat(execution,obs): 호가단위 스냅(dcy-030/#28) + 운영 관측성(dcy-029/#29) + REL-007 환류
dcy-030 (issue #28, P0 체결 복구):
- REQ-083 KRX 호가단위(tick) 순수 유틸 (domain/pricing.py)
- REQ-070 CHG: 슬리피지 실효가 호가단위 스냅 — 상한 준수(매수 floor/매도 ceil)
  + band<1tick 하한/상한 보장 + 부동소수 epsilon 가드. 147700→148400/54500→54700.
- BAT-044 release-candidate

dcy-029 (issue #29, 운영 관측성):
- REQ-084 구조화 로깅 기반 (runtime/logging_setup.py): ISO8601 KST·레벨·Loki JSON
  ·correlation id(slot_id)
- REQ-085 주문 결과 로깅 (event=order_submit|fill|reject)
- REQ-087 거부 ERROR 가시성 (조용한 롤백 제거)
- REQ-086 청산 reason+actor 동반 노출 (안전망/전략 구분, 신규 필드 없음)
- REQ-088 운영 조회 CLI report (application/operator_report.py, 보유·오늘 체결
  ·실현 PnL net·대기, direct-DB)
- REQ-089 운영자 런북 docs/operations.md
- REQ-090 baseline 정합 (runtime-flows·project-structure·interface-contract)
- BAT-045(P0)·BAT-046(P1) release-candidate

REL-007: confirmed → feedback-captured (운영 인스턴스 라이브 검증 + #28/#29 환류
정규화). BAT-037~041 released.

검증: 537 passed, 47 skipped, 회귀 0, ruff clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-16 13:19:31 +09:00
genichinandClaude Opus 4.8 e0d4e71304 feat(slot,intent): slot strategy-state 저장소 (dcy-028) + sell retryable 플래그 (issue #27)
BAT-042 (dcy-028 / issue #26 C-1/C-2/C-2b/C-3, REQ-079~082 + REQ-073/074 CHG):
- slot_strategy_state 별도 테이블 + StrategyStateStore(in-memory+PG) + occupancy_seq·version CAS + opaque UTF-8 data + 2단 size cap (REQ-079)
- trex.slots.strategy_state.set write RPC — reject reason 집합, 멱등 (REQ-080)
- Slot.occupancy_seq 필드 + inactive→pending_buy bump + →INACTIVE close 삭제 훅 → 점유 전환 ABA 구조적 차단 (REQ-081)
- slots.req 응답 행에 strategy_state 맵(batched 조립)·occupancy_seq additive (REQ-073 AC-7/REQ-074 AC-6)
- 마이그레이션 014(occupancy_seq)/015(slot_strategy_state), baseline 문서 정합 (REQ-082)

BAT-043 (issue #27 / REQ-007 CHG):
- rejected SellIntent 응답에 additive retryable: bool (core 소유 reason→retryable 매핑, 미인식 기본 true, bad_request* false) — 손절매 재시도 안전 경로 decoupling

검증: 487 passed, 47 skipped (신규 41: store CAS 6+ABA, occupancy bump/close 4경로, set RPC 9, slots read 5, retryable 8, PG 게이트), ruff clean.
REQ-079·080·081·082·073·074·007 → Implemented. issue #27 close. PG 경로 실 DB 검증은 release 위임.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 19:27:57 +09:00
genichinandClaude Opus 4.8 9871008bb6 fix(paper): BAT-039 — paper order seq Redis 영속화 (재시작 멱등 키 충돌 방지, REQ-011/issue #4)
REQ-011 change-req(CHG-20260612-01)→구현→confirm-req-implemented(CHG-20260612-02)
전체 delivery. paper 한정 결정성 버그(프로덕션 무관).

- seq_store.py 신규: SeqStore 포트 + RedisSeqStore(원자 INCR, 재시작 단조·영속)
  + InMemorySeqStore(폴백/테스트). REQ-016 token_cache 패턴 재사용.
- paper.py: optional seq_store 주입. 미주입 시 기존 in-memory 동작 byte-for-byte 유지.
- config.py/daemon.py: TREX_PAPER_REDIS_URL → RedisSeqStore wiring
  (key=trex:broker:paper:{instance_key}:order-seq).
- 런타임 Redis 오류는 전파(silent in-memory 폴백 금지 — 버그 재현 방지).
- tests: 재시작 단조성/correlation_id 비충돌/in-memory 폴백/fake redis INCR (5).
  434 passed (+5), ruff clean.

REQ-011 Approved→Implemented, BAT-039 release-candidate. issue #4 close.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-12 18:16:11 +09:00
genichinandClaude Sonnet 4.6 6c24bd4c2c feat(slots-rpc): available_slots capacity hint 추가 (BAT-037 / REQ-073 AC-6)
slots RPC 응답에 available_slots: int|null 포함.
max_active_slots 미설정 시 null, 설정 시 max(0, M-active).
16 tests passed (AC-6 신규 3개 포함), 423 전체 통과.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 10:28:29 +09:00
genichinandClaude Sonnet 4.6 002340e5fd docs(rel-006): BAT-035/036 포함 + 배포 근거 보완 — confirmed
- BAT-035/036 batch index: confirmed → release-candidate
- rel-006: BAT-035/036 추가, status confirmed, SHA 명시, smoke 한계·외부 소비자 분석 기술
- daemon.py: stale 주석 trex.snapshot.req → trex.slots.req

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-11 19:01:59 +09:00
genichinandClaude Opus 4.8 f0ad37747b feat(rel-007): dcy-025/024 + BAT-035/036 — slots.req 즉시 교체 + safety_sell_enabled
BAT-035 (dcy-025): trex.snapshot.req → trex.slots.req 즉시 교체
- topology: SNAPSHOT_REQ_QUEUE 제거, SLOTS_REQ_QUEUE = "trex.slots.req" 추가
- portfolio.py: View/build_snapshot_envelope/SnapshotEnvelope/_asset_to_dict 제거;
  slot_to_dict 시그니처 확장(snapshot_store/watchdog_store/pause_store/watchdog_timeout_sec)
  — asset_quantity(hub)/cash_balance/cash_estimated/warned/watchdog{}/paused 병합 (REQ-074)
- messaging_handlers.py: make_snapshot_rpc_handler 제거, make_slots_rpc_handler 추가
  — filter(all/inactive/pending/holding/error) + spy(bool) 처리; strategy 필드 제외 (REQ-073/074)
- daemon.py: _snapshot_rpc_loop → _slots_rpc_loop, make_slots_rpc_handler 연결
- cli.py: universe/slots → slots_queue + spy=True + server-side filter; buyable→inactive 매핑 (REQ-063)
- rabbitmq.py: snapshot_queue → slots_queue
- docs: interface-contract.md + runtime-flows.md slots.req 계약으로 업데이트 (REQ-075)
- REQ-028: Implemented → Deprecated (코드 삭제 완료)
- 테스트: test_snapshot_handler/selection/rpc_watchdog/messaging_snapshot_consumer 삭제;
  test_slots_handler.py(신규, 13 cases), test_killswitch_masking_resume 재작성,
  test_ledger_option_d._apply_cash_estimate → slots handler 경유, test_topology/cli_operator 갱신

BAT-036 (dcy-024): per-instance safety_sell_enabled 플래그
- config.py: DaemonInstanceConfig + DaemonConfig 에 safety_sell_enabled: bool = True 추가;
  from_toml 파싱, from_config 전파
- daemon.py: start() workers 조건부 — safety_sell_enabled=false 시 trex-safety-sell 미기동 + WARNING
- tests: test_instance_config.py 에 safety_sell_enabled 파싱 3개 케이스 추가

REQ: 073/074/075/021/034/063/048/005 Approved → Implemented

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-11 14:45:45 +09:00
genichinandClaude Sonnet 4.6 fb05a73130 feat(rel-006): dcy-022/023 + BAT-032~034 — 실행 정책·qty 검증·allocated_capital 제거 통합
BAT-032 (dcy-022): timeout_sec/slippage_pct 실행 정책, 미체결 자동 취소,
보호 지정가, `trex-core cancel` 백스톱. 마이그레이션 012.
BAT-033: `trex-core enter --qty` 필수 검증 — 미지정 시 RPC 전 즉시 오류.
BAT-034 (dcy-023): allocated_capital 죽은 필드 전 경로 제거 (domain·intent·
persistence·snapshot·CLI). 마이그레이션 013(비가역 DROP). 외부 발신자
unknown key 무시 수신 의미론 적용.
REL-006 confirmed: BAT-026~034 통합 릴리즈, pre-release smoke 증거 포함.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-11 07:42:03 +09:00
genichinandClaude Opus 4.8 7d727824dc feat(slot): BAT-030 — 자산 중심 cutover 완성 (dcy-020 BAT-δ, REQ-056·057·062·064·065)
M1 literal inactive 전면화: SlotState 5-state(idle/closed 별칭 제거),
마이그레이션 010(CHECK 교체+행 rewrite), ops_checks 를 transition_log 기반 재작성.
M2 집행 매도 수량 브로커 정본(D3): dispatch 매도 qty=broker hldg_qty,
미러 fallback 없음, 브로커 0 → skip+WARN, 리스트 기반 dispatch(head-block 방지).
M3 컬럼 drop + legacy pool 제거: 마이그레이션 011(trading_date·entry_count DROP),
repo 계약 instance 스코프 개편(집행 큐 포함), preallocation 폐기(데몬 7스레드),
admission 자산 중심 단일 경로(slot_id 정본), capacity 재정의(available=M−active),
daily_symbol_entries 코드 경로 제거.
M4 cutover 도구(D1·D4): trex-core-cutover(전체 rebuild, preflight/브로커 carry-over/
--allow-unmapped/--promote-unique/dry-run) + PG 리허설 테스트 8건.
M5 baseline 마감: data-model/runtime-flows/project-structure/interface-contract
자산 중심 최종 갱신 + run-trex-core-daemon 스킬 정합.

검증: 단위 367 passed + ruff clean; PG-gated 29 passed(010/011 round-trip·드리프트
가드·cutover 리허설); RabbitMQ 21 passed; run-the-app e2e(test DB+실 MQ, M=1):
reconcile 슬롯 생성→slot_id 매수→dedup→capacity_full→fill→already_active→
매도→inactive(자산 바인딩 보존)→영구 슬롯 재활성 전 구간 green.
실 cutover 는 운영자 실행(REL 단계, D4).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-10 15:16:15 +09:00
genichinandClaude Opus 4.8 78a743a2ef feat(slot): BAT-029 M2(a)/M3 — holding 불일치 reconcile + slots 단일 뷰 (dcy-020/REQ-062·063)
- REQ-062 divergence: list_holding_slots(protocol+in_memory+PG) +
  reconcile_core_holdings(A=코어 holding/broker0→error, C=수량 불일치→warning,
  B=broker 보유/코어 없음→warning; 자동 청산 없음 issue #15). snapshot_scheduler
  자산 중심 모드에서 best-effort 호출, daemon slot_service 배선.
- REQ-063: slot_to_dict 에 universe_detached(retired_at) 추가 — slots 단일 뷰 tombstone 표시.
- 신규 테스트: test_holding_divergence(4).

M2(b) 청산 수량 브로커 정본(execution dispatch)은 라이브 집행 변경 → BAT-δ carry-over.

검증: 단위 393 passed / 36 skipped, PG-gated 18 passed, ruff clean. legacy 무파손.
REQ-061/062/063 Approved 유지(γ 경로 도입 검증; default 전환·M2(b) 는 BAT-δ).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-10 12:18:56 +09:00
genichinandClaude Opus 4.8 8a377e6fb3 feat(slot): BAT-028 M2/M3 — universe reconcile + 자산 중심 admission (dcy-020/REQ-058·059)
config 게이트 이중 경로(max_active_slots 설정 시 자산 중심, 미설정 시 legacy pool).
운영 데몬은 cutover(BAT-δ) 전까지 pool 무중단.

- repository: find_slot_by_portfolio·count_active(holding+pending_buy+pending_sell)·
  reconcile_universe(생성 멱등/soft-retire/tombstone, allow_retire 가드) — protocol+in_memory+PG
  (PG: ON CONFLICT (instance_key, portfolio_id) WHERE portfolio_id IS NOT NULL)
- snapshot_scheduler: run_once swap 직후 _reconcile_slots(빈 universe skip, ≥50%↓ retire 보류)
- intents: _admit_buy 모드 분기 + _admit_buy_asset_centric(활성화/already_active/
  capacity_full/unknown_portfolio/over-cap, 인스턴스 lock)
- daemon: instance_key·max_active_slots·reconcile_enabled·trading_date_fn 배선
- 신규 테스트: test_universe_reconcile(6), test_asset_centric_admission(6), PG reconcile(1)

검증: 단위 387 passed / 35 skipped, PG-gated 17 passed, ruff clean. legacy 무파손.
REQ-058/059/060 은 Approved 유지(자산 중심 경로 도입 검증; default 전환·cutover 는 BAT-δ).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-10 11:14:50 +09:00
genichinandClaude Opus 4.8 017b8185e0 feat(slot): BAT-027 M2/M3 — 상태머신 inactive 재정의 + 인스턴스 스코프 advisory lock (dcy-020/REQ-057)
안 B 점진의 2/3단계. runtime 무변경(idle/closed 별칭 흡수)으로 cascade 통제.

- state.py: SlotState.INACTIVE 추가 + idle/closed 별칭 normalize_state(); 전이 집합
  inactive 기준 재정의(inactive↔pending_buy↔holding↔pending_sell), closed→idle 제거;
  is_allowed_transition 양쪽 정규화; slot_advisory_lock_id(instance_key) 헬퍼
- service.py: SlotService(instance_key) + advisory lock 인스턴스 스코프(self._lock_id)
- __init__: normalize_state/slot_advisory_lock_id export; daemon: instance_key 전달
- 테스트 갱신: test_state(전이/멤버7/락/size12), test_service(closed→idle 제거→reallocation),
  test_slot_entry_count, test_postgresql_repository(closed→idle 거부+reallocation)

검증: 단위 375 passed / 35 skipped, PG-gated(trex_core_test) 8 passed
(마이그레이션 009 드리프트 + lifecycle/reallocation), ruff clean.

REQ-056/057 은 토대만 충족 → Approved 유지(entry_count/trading_date drop·literal
inactive 전면 전환은 BAT-δ; confirm-req-implemented 는 dcy-020 후속 배치 완료 후).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-10 10:53:39 +09:00
genichinandClaude Opus 4.8 8b2c26c04c feat(config): config 디렉터리 기반 설정 관리 (--config-path/--env-file, trex-orb-v1 정합, BAT-026)
REQ-048/010 change-req. `--config` 를 이름으로 해석 + `--config-path <dir>`
(기본 ./config) + resolve_config_toml(슬래시 포함 값은 하위호환 명시 경로).
`.env` 를 config-dir 결정적 해석(--env-file 우선, 없으면 <config_dir>/.env,
CWD 무관) + config/.env.example + .gitignore config/* 허용목록. 운영자 CLI 도
--config-path/--env-file 동일 적용. from_env() legacy 루트 .env 폴백 유지로
완전 하위호환.

단위 23 + 전체 375 passed, ruff clean, 라이브 스모크(name·path 동일).
REQ-048/010 → Implemented.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-09 23:45:08 +09:00
genichinandClaude Opus 4.8 2bfecd59a8 feat(ledger): dcy-018 option D cash/position write-back + 추정 현금 (BAT-023)
ledger write 응답의 portfolio-hub 권위 cash/quantity 를 스냅샷에 write-back 해
매수/매도 직후 "수량 live·현금 stale" 비대칭 제거(option D 활성화). portfolio-hub#8
(*_after 응답) 동작 중 확인으로 그간 deferred 됐던 REQ-030 을 닫음.

거버넌스: 전부 change-req (신규 REQ 없음, dcy-018 OQ-3 a).
- REQ-017: record_trade → LedgerWriteOutcome{result, cash_balance_after,
  asset_quantity_after}. 어댑터가 응답 root flat *_after 파싱(new+duplicate, 누락 None)
- REQ-019: LedgerWriter 에 SnapshotStore 주입 → new+duplicate write-back
  (best-effort, *_after 절대값이라 멱등). LedgerOutbox.pending_cash_delta()
- REQ-029: 추정 현금 오버레이(_apply_cash_estimate, cash_estimated 라벨, OQ-4 i)
  + 슬롯 오버레이 유지(OQ-1 B). 미정산 outbox delta 기반, write-back 수렴
- REQ-030: option D 활성화 → Approved→Implemented

best-effort 격리(NFR-1): write-back/추정 실패가 ledger 멱등·청산 무차단.
배치 번호: 동시 커밋된 BAT-022(slot-state, dcy-016)와 충돌해 BAT-023 으로 배정.

검증: 단위 350 passed/35 skipped, 인프라 게이트(PG+RabbitMQ) 385 passed, ruff clean.
신규 test_ledger_option_d + adapter/PG 테스트. BAT-023 release-candidate.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-08 21:50:56 +09:00
genichinandClaude Opus 4.8 a570ac659e fix(daemon): buy 심볼 resolver 결함 수정 + REL-004 draft (운영자 run 증거)
REL-004(미릴리즈 BAT-014~021 통합) draft-release 작성. confirm-release 전
운영자 프로그램 run-the-app 검증(실 데몬 + trex-core CLI e2e)을 수행했고,
369 passed 가 놓친 실 프로덕션 버그를 검출·수정했다.

- fix(REQ-007 CHG-20260608-03): Daemon._resolve_symbol 이 SnapshotAsset.symbol
  (없는 속성)을 접근 → 데몬 buy admission 매 요청 AttributeError → intent RPC
  타임아웃(실 데몬 매수 전면 불능). asset.item.symbol 로 수정. 단위 테스트가
  stub resolver 를 주입해 실 배선을 한 번도 실행하지 않아 미검출이었음
- test: tests/runtime/test_daemon_resolve_symbol.py — 실 Daemon._resolve_symbol
  × 실 SnapshotStore 회귀 가드(수정 전 실패하는 테스트)
- docs(release): rel-004 draft(app-service, BAT-014~021) + Pre-Release Run
  Evidence(데몬 기동 + universe/slots/enter/exit e2e + ledger completed +
  버그 검출·수정 + LIVE 미검증 한계) + releases/index REL-004 행

검증: 실 데몬 재기동 후 buy/sell e2e admitted→체결→closed + ledger completed,
단위 337 passed, ruff clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-08 17:38:34 +09:00
genichinandClaude Opus 4.8 6d7bca1460 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>
2026-06-08 17:10:26 +09:00
genichinandClaude Opus 4.8 b851b927ee fix(ledger): dcy-015 outbox 영속 오류 분류 + 백오프/dead-letter (BAT-019)
portfolio-hub 404(미등록 포트폴리오)가 retryable 로 분류되어 백오프·상한 없이
초당 영구 재시도되던 버그(issue #13, 실측 930회) 정정.

거버넌스: 전부 change-req (신규 REQ 없음, dcy-015 OQ-3; 백오프 env 는 REQ-019 흡수).
- REQ-017: 404/400/422 → LedgerPermanentError 분류 (409 conflict / 408·429·5xx·
  network retryable 불변). LedgerPort 에 permanent 타입 추가
- REQ-019: 영속 → 터미널 delivery_failed 즉시(재시도 0). retryable 지수 백오프
  +jitter+next_retry_at, max_attempts 초과 시 dead-letter delivery_failed.
  list_pending 이 백오프 행 스킵(head-of-line 보호). 마이그레이션 007(status enum
  +next_retry_at). 백오프 env(base/cap/max-attempts)
- REQ-018: 터미널/dead-letter WARN + WriterStats.delivery_failed 카운터 가시화
- REQ-020: runtime-flows/data-model/interface-contract 정합

단일 터미널 delivery_failed(permanent/dead-letter, last_outcome 로 사유 구분) —
conflict_blocked 패턴과 동형. dead-letter 는 운영자 재처리 가능 상태.

검증: 단위 311 passed/34 skipped, 인프라 게이트(PG+RabbitMQ) 345 passed, ruff clean.
신규 test_ledger_outbox_backoff + adapter/PG 테스트, 기존 retryable 백오프 정합.
BAT-019 release-candidate.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-08 16:12:17 +09:00
genichinandClaude Opus 4.8 0b7db66463 feat(runtime): dcy-014 trading_date KST 타임존 분리 + 일일 슬롯 preallocation (BAT-018)
데몬이 시스템 UTC 날짜로 슬롯을 1회만 preallocate 해 trading_date 가 KST 캘린더와
어긋나고 재시작 없이는 다음날 슬롯이 안 생기던 문제(issue #14) 정정.

거버넌스: 전부 change-req (신규 REQ 없음, dcy-014 OQ-3; 스케줄러는 REQ-030 패턴 준용).
- REQ-005: trading_date 를 설정 타임존(기본 Asia/Seoul) 기준 계산(timestamp 는 UTC
  유지). DailyPreallocationScheduler 신규 — TZ 일자당 1회 멱등 fire, 재시작 없이
  다음날 슬롯 자동 생성. setup() 배선 + trex-preallocation 스레드
- REQ-048: TOML trading_timezone/daily_preallocation_time 키 + from_config 전파
- REQ-009: runtime-flows/data-model 정합 + instance.example.toml

OQ-1 기본 00:00(TZ 자정, TOML 설정 가능) / OQ-2 매 캘린더일 생성(거래일 캘린더 후속).
멱등 preallocate 로 중복 tick·다중 인스턴스 중복 생성 0.

검증: 단위 302 passed/32 skipped, 인프라 게이트(PG+RabbitMQ) 334 passed, ruff clean.
신규 test_preallocation_scheduler(고정 clock) + test_instance_config 확장.
BAT-018 release-candidate.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-08 15:39:04 +09:00
genichinandClaude Opus 4.8 4746f74aae fix(broker): dcy-013 KIS 외부 취소 감지 + 부분 체결 회계 + stale 워치독 (BAT-017)
KIS 계좌에서 직접 매도 주문을 취소하면 poll_fill 이 cncl_yn 을 파싱하지 않아
슬롯이 pending_sell 에 영구히 갇히고 죽은 주문을 무한 폴링하던 버그(issue #9)
정정.

거버넌스: 전부 change-req (신규 REQ 없음, dcy-013 OQ-3).
- REQ-012: KIS poll_fill cncl_yn 파싱 → 외부 취소 감지. FillStatus.cancelled
  /is_partial_cancel 추가. 체결 0 취소→cancelled, 부분 체결 후 취소→partial+cancelled
- REQ-013: 집행 _on_partial_cancel — 체결분 원장 1회 + qty 차감(매도 잔여/매수 체결분)
  + holding 복귀 (OQ-1 A안). 전량 취소는 기존 롤백 경로 재사용
- REQ-018: 부분 체결분 원장 1회 기록 (_record_ledger 재사용)
- REQ-031: 잔여 포지션 정합 연계 (slot.qty ↔ 실 잔고, 과매도 방지)
- REQ-014: pending stale 워치독 Alert-only (TREX_PENDING_STALE_THRESHOLD,
  WARN+카운터, 상태 미변경) — 취소 감지 누락 backstop
- REQ-015: interface-contract/runtime-flows 정합

전량 취소는 holding 자동 복구(limit_price clear→재매도 시장가, dcy-012 연계).
KIS cncl_yn 실 규약은 운영 검증(미지 값 보수 처리 + 워치독 backstop).

검증: 단위 294 passed/32 skipped, 인프라 게이트(PG+RabbitMQ) 326 passed, ruff clean.
신규 test_kis_cancel(httpx stub) + test_execution_cancel(programmable broker).
BAT-017 release-candidate.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-08 14:11:49 +09:00
genichinandClaude Opus 4.8 aee3512383 feat(intents): dcy-011 buy intent portfolio_id 필수 계약 + ledger skip 가시화 (BAT-015)
dcy-011 전 주기 delivery. issue #6(buy 식별자 정정) + issue #11(ledger silent skip) 흡수.
거버넌스: 기존 Implemented REQ는 change-req, 신규 기능만 신규 REQ(OQ-3).

- REQ-007 (change-req): buy admission portfolio_id 필수화 + SnapshotStore symbol
  resolve + unknown_portfolio 거부 (hard cutover, OQ-1/OQ-2)
- REQ-021 (change-req): buy 메시지 payload symbol 제거
- REQ-018 (change-req): _record_ledger skip 가시화 (WARN + ledger_skipped 카운터)
- REQ-055 (신규): closed↔ledger_outbox anti-join 운영 점검 find_ledger_gap_slots

코드: intents.py(BuyIntent portfolio_id 필수 + resolve_symbol 주입 + _admit_buy
reject 분기), messaging_handlers.py, runtime/daemon.py(_resolve_symbol 배선),
execution.py(skip WARN 로그), adapters/persistence/ops_checks.py(신규 anti-join).

검증: 단위 282 passed/32 skipped, 인프라 게이트(PG+RabbitMQ) 314 passed, ruff clean.
BAT-015(standard) release-candidate. srs/index Next ID → REQ-056.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-08 12:57:51 +09:00
genichinandClaude Opus 4.8 9f83c64a40 feat(multi-instance): dcy-009 BAT-013 — RabbitMQ 다중 인스턴스 토폴로지 (REQ-048~051)
dcy-009(다중 인스턴스 RabbitMQ 토폴로지) 전체 SDLC: draft-req → confirm-req →
draft-batch(BAT-013) → run-batch-delivery → confirm-req-implemented.

- REQ-048 (Configuration): DaemonInstanceConfig.from_toml + DaemonConfig.from_config
  (env 병합, portfolio_hub_token 주입) + 데몬 --config 필수 argparse + .gitignore 인스턴스
  TOML + config/instance.example.toml
- REQ-049 (Interface): topology 동적 큐명 빌더 intent/snapshot/control_queue(instance_key)
  — default=리터럴 큐명(하위호환), named=`{base}.{instance_key}` + daemon per-instance 구독
- REQ-050 (Integration): trex.direct(topic)/trex.broadcast(fanout) exchange +
  declare_topology(instance_key) per-instance binding + control dual binding + direct_routing_key
- REQ-051 (Documentation): interface-contract/runtime-flows/project-structure 정합

하위호환: instance_key="default" → 기존 리터럴 큐명·RPC 경로 무회귀(FR-5).
split-brain 방지(NFR-3): 인스턴스 전용 큐. 민감값(NFR-4): TOML + gitignore.

검증: 신규 9 단위 + RabbitMQ 라우팅 2, 단위 271 passed,
인프라 게이트 301 passed(directed 격리 S-2 + broadcast dual S-3 + default 무회귀 S-4),
ruff clean. REQ-048~051 Implemented, BAT-013 release-candidate.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-07 15:10:36 +09:00
genichin 7e23648489 feat: add kill-switch control flow 2026-06-07 14:00:20 +09:00