Files
trex-core/docs/discovery/dcy-037_20260714_operator-broker-truth-and-managed-slot-divergence-hotfix.md
T

18 KiB

0. 문서 상태

  • 상태: confirmed
  • 문서 ID: dcy-037_20260714_operator-broker-truth-and-managed-slot-divergence-hotfix
  • 이슈명: HOTFIX — fetched credential 운영자 broker truth 정합 및 managed non-holding divergence 분리 (#39, #40, #41)
  • 작성 시각(KST): 2026-07-14 21:52:01 KST
  • 마지막 갱신 시각(KST): 2026-07-14 22:24:41 KST (draft-req)
  • 대체됨: 없음
  • 후속 Discovery 참조: 없음
  • 생성된 REQ 참조: REQ-119, REQ-120, REQ-121, REQ-122

1. 계획 상태 요약

  • 연계 문서 현황:
    • 관련 점수 파일: 미생성
    • 관련 TODO/메모: GitHub fourblocks2020/trex-core issue #39, #40, #41. 선행 Discovery dcy-034(차단형 reconcile/sync/verify) 및 dcy-036(fetched credential day-start fail-safe).
  • 해석:
    • credential_fetch_enabled=true인 genichin-longterm에서 daemon, operator CLI, broker token lifecycle이 같은 계좌 잔고 truth를 보장하지 못하여 sync/verify 결과를 운영 복구 판단에 사용할 수 없다.
    • 이 HOTFIX는 잘못된 broker truth를 빈 잔고로 해석하는 경로를 차단하고, 이미 관리되는 inactive/error 슬롯을 진짜 unmanaged position과 구분해 안전한 운영자 triage와 이후 sync를 가능하게 한다.

2. 요구사항 판정 결과

  • 판정: 신규
  • 근거
    • dcy-034/REQ-105·106은 sync/verify의 상태 동기화 의미론을 정의했지만, fetched credential 모드에서 CLI가 daemon과 동일한 credential source 및 token lifecycle을 사용해야 한다는 운영 불변식은 정의하지 않았다.
    • dcy-036/REQ-113·115는 day-start fetched credential과 fail-safe entry gate를 다루지만, direct-DB operator CLI의 credential 재해석·token throttle 및 inactive 슬롯의 reconcile 분류는 후속 운영 환류로 독립 추적하는 편이 명확하다.

3. 문제점의 요약

  • 현재 상태
    • issue #39 재현에서 daemon은 portfolio-hub fetched credential로 계좌 A를 사용하지만, sync/verify는 _build_broker(cfg)로 env/config 계좌 B를 생성했다. 동일 instance의 005930은 daemon broker truth에서 qty=5이나 CLI dry-run은 broker_qty_zero로 skip했다. 계좌 식별자는 문서·로그에서 안전하게 마스킹한다.
    • issue #40의 KISBroker.get_balance()는 token/auth HTTP 오류를 잡아 []로 반환한다. fresh fetched-credential broker가 KIS token throttle(EGW00133)을 받으면 실제 보유가 없는 것처럼 보이며, broker_qty_zero 같은 상태 판단으로 이어질 수 있다.
    • issue #41의 reconcile_core_holdings()는 list_holding_slots(instance_key)만 대조한다. 따라서 broker가 보유한 symbol에 대해 같은 instance의 managed slot이 inactive 또는 error로 존재해도 unmanaged position으로 로그를 남긴다.
  • 기대 상태
    • fetched credential 모드에서 daemon과 sync/verify는 동일한 portfolio-hub credential source와 명시적인 token/broker lifecycle 정책으로 broker truth를 조회하며, 인증·token 실패는 empty balance와 절대 동치가 아니다.
    • reconciliation은 slot 없음의 true unmanaged position과 slot 존재 + non-holding의 managed state divergence를 별도 code/log/운영 조치로 구분한다.
  • 이해관계자
    • 사장님: hotfix 범위·production 배포 및 실계좌 sync --live 최종 승인자
    • 팀장: Discovery/REQ/Batch 및 release gate 관리
    • trex-core-dev: broker 구성, reconciliation, CLI 및 회귀 테스트 delivery 담당
    • qc-reviewer: credential source, throttle fail-closed, divergence 분류의 독립 검증 담당
    • 운영자: dry-run 검증 후 승인된 live sync를 수행하는 주체
  • 제약사항
    • app key/app secret/token 및 raw credential payload는 로그, JSON 출력, 테스트 fixture에 기록하지 않는다.
    • sync --live는 외부 실계좌/DB 상태를 변경하므로 본 HOTFIX 배포 후에도 별도 운영 승인 없이는 실행하지 않는다.
    • 기존 credential_fetch_enabled=false env-only instance와 AMQP wire 계약은 회귀 없이 유지한다.

주요 사용자 시나리오

  • 운영자가 genichin-longterm의 inactive slot과 broker-held 005930을 확인한다. sync --dry-run은 fetched credential source로 broker qty=5를 확인하고, token 오류가 나면 0 보유로 판단하지 않고 비0 종료와 안전한 오류를 반환한다.
  • daemon reconcile이 inactive managed slot + broker qty>0을 발견하면 managed_non_holding_divergence로 구조화해 경고한다. slot이 전혀 없는 broker-held symbol만 기존 unmanaged_position 경로를 탄다.
  • hotfix QC와 release 검증 후 운영자가 dry-run 결과, portfolio-hub 장부, broker truth를 교차 확인하고 명시 승인된 단일 대상에만 sync --live --yes를 실행한다.

4. 이번에 정의할 변경

  • 변경 1 — daemon/CLI 공통 broker truth 해석: credential_fetch_enabled=true일 때 operator CLI도 portfolio-hub fetched credential을 사용하고, raw env/config credential은 명시적인 break-glass 정책 외에는 조용히 fallback하지 않는다.
  • 변경 2 — token/auth 실패 fail-loud: get_balance()와 operator 표면에서 token issuance/auth/transport 실패를 actual empty holdings와 분리하고, 반복 조회에 대한 broker/token 재사용 정책을 명확히 한다.
  • 변경 3 — managed non-holding divergence 분류: holding-only 수량 대조는 보존하되, 미매칭 broker symbol에 대해 전체 slot 존재를 확인해 unmanaged_position과 managed_non_holding_divergence를 구분한다.
  • 변경 4 — baseline/운영 문서 정합: credential source, error semantics, reconcile decision code, dry-run 및 live remediation gate를 docs/runtime-flows.md, docs/interface-contract.md, docs/operations.md에 실제 구현과 일치하게 반영한다.

영향 범위

  • 코드 영향:
    • trex_core/runtime/daemon.py, trex_core/runtime/cli.py, trex_core/application/snapshot_scheduler.py 또는 공통 broker factory: fetched credential source 및 CLI broker 생성 경로.
    • trex_core/adapters/broker/kis.py: token/auth failure와 actual empty balance의 구분, KIS token lifecycle.
    • trex_core/application/reconciliation.py, SlotRepository protocol 및 PostgreSQL/InMemory 구현: non-holding managed-slot existence check와 구조화 classification.
  • 문서 영향:
    • docs/runtime-flows.md, docs/interface-contract.md, docs/operations.md, 신규 REQ/Batch/verification 문서.
    • dcy-034, dcy-036: 본 HOTFIX follow-up 관계 기록.
  • 운영 영향:
    • 배포 전 dry-run에서 credential source·account reference masking·broker query 결과·분류 code를 확인한다.
    • 배포 후 live sync는 single portfolio/slot 대상, 사전 dry-run 및 사장님 승인으로 제한한다.

5. 요구사항 목록

기능 요구사항

  • FR-1: credential_fetch_enabled=true instance의 daemon, sync, verify 및 해당 operator broker-read 경로는 동일한 portfolio-hub fetched credential source를 해석해야 한다. fetch 실패 시 명시적 break-glass가 활성화되지 않은 한 env/config credential로 조용히 fallback하지 않는다.
  • FR-2: token issuance, auth, HTTP transport 또는 malformed broker response 실패는 get_balance()==[]로 변환하지 않는다. operator CLI는 안전하게 sanitize된 오류와 비0 종료로 중단하며, broker_qty_zero/sync skip/verify 결과를 만들지 않는다.
  • FR-3: repeated fetched-credential broker read는 구현된 token/broker lifecycle 정책에 따라 안정적으로 동작해야 하며, fresh object 생성으로 인한 token throttle을 실제 빈 계좌로 오판하지 않아야 한다.
  • FR-4: reconcile_core_holdings()는 broker-held symbol이 holding slot에 매칭되지 않을 때 (a) instance에 어떤 slot도 없는 unmanaged_position, (b) slot은 있으나 inactive/error 등 non-holding인 managed_non_holding_divergence를 구분해 구조화 로그/decision code를 남겨야 한다.
  • FR-5: managed_non_holding_divergence는 자동 slot 전이, 자동 warning 해제, 자동 매매 또는 자동 청산을 유발하지 않는다. 상태 수렴은 검증된 sync와 기존 운영 gate를 통해서만 수행한다.
  • FR-6: runtime/interface/operations baseline은 credential source 우선순위, safe error semantics, reconciliation 분류 및 operator remediation 절차를 구현과 동기화해야 한다.

비기능 요구사항

  • NFR-1 (안전성): credential, access token, app secret, raw account number 및 HTTP authorization payload는 로그·CLI JSON·테스트 artifact에 노출되지 않는다. account reference가 필요한 운영 식별은 마스킹 또는 비밀이 아닌 stable label을 사용한다.
  • NFR-2 (회귀안전): credential_fetch_enabled=false env-only instance, 정상 holding match, holding qty mismatch, slot 없는 unmanaged position, 기존 sync/verify live double-ack 의미론은 보존한다.
  • NFR-3 (관측성): source mode, safe failure code, reconciliation decision code, slot_id/portfolio_id/symbol/status가 구조화 로그로 추적 가능해야 한다.
  • NFR-4 (검증): unit/integration/CLI regression에 #39·#40·#41 재현 조건과 existing reconciliation/sync/verify 경로를 포함하고, independent QC가 실제 suite를 재실행한다.

범위 경계

  • In Scope
    • #39 fetched credential source mismatch, #40 token throttle/empty balance masking, #41 managed non-holding divergence 분류 및 이들의 tests/docs.
    • operator dry-run evidence 및 post-release single-target remediation runbook.
  • Out of Scope
    • portfolio-hub credential API 계약 또는 credential 저장 모델의 재설계; 필요한 계약 변화가 발견되면 cross-service gate로 중단한다.
    • sync --live의 자동 실행, 전역 bulk sync, unmanaged broker position 자동 slot 생성, automatic remediation/trading.
    • TVR 전략 로직 변경 또는 TVR 재배포. TVR은 core truth/slot 상태가 정합된 뒤 기존 inactive,holding read 의미론으로 재평가한다.

6. 리스크/가정 목록

리스크

  • R-1 (High): credential source 또는 account identity를 잘못 연결하면 다른 실계좌의 broker truth를 기준으로 sync 판단을 할 수 있다.
    • 완화 방안: fetched mode CLI source를 daemon과 공통화하고, dry-run에서 비밀 없는 source/account fingerprint 일치 assertion을 테스트한다. live sync는 사전 dry-run과 명시적 승인 없이는 실행하지 않는다.
  • R-2 (High): auth/token 오류를 empty balance로 취급하면 false zero 기반의 잘못된 상태 수렴이 발생할 수 있다.
    • 완화 방안: typed/sanitized failure를 도입하고 balance가 성공적으로 조회된 경우에만 qty=0 판단을 허용한다. throttling 재현 테스트를 추가한다.
  • R-3 (Medium): non-holding slot existence check가 holding 수량 mismatch 또는 true unmanaged warning 의미론을 바꿀 수 있다.
    • 완화 방안: primary holding compare를 유지한 뒤 unmatched broker symbols에만 second-pass existence lookup을 적용하고, 4-case regression matrix를 고정한다.
  • R-4 (Medium): HOTFIX 배포 직후 운영자가 빠르게 live sync를 실행하면서 장부/브로커/slot 증거 교차 확인을 건너뛸 수 있다.
    • 완화 방안: release runbook에 dry-run → broker/ledger cross-check → single-target explicit approval → live sync → re-reconcile 순서를 필수 gate로 기록한다.

가정

  • A-1: portfolio-hub fetched credential path는 daemon의 현재 정상 운행에서 authority source로 사용 가능하며, service-token 접근 계약은 변경하지 않는다.
  • A-2: sync는 REQ-105의 기존 원칙대로 managed slot만 브로커 truth에 수렴시키며, slot 없는 broker holdings를 자동 편입하지 않는다.
  • A-3: genichin-longterm의 005930 사례는 generic regression fixture로 추상화할 수 있고, 실제 계좌 식별값/credential은 test data에 포함하지 않는다.

오픈 질문

  • OQ-1: #39, #40, #41을 하나의 emergency delivery batch로 묶어 single release candidate로 배포할지, credential hotfix(#39/#40)와 telemetry 분류(#41)를 분리할지?
    • 상태: Answered (2026-07-14, 사장님)
    • 결정: #39·#40·#41을 하나의 emergency batch로 확정한다. #41은 자동 state write가 없는 additive classification으로 유지해 rollback blast radius를 제한한다.
    • 종료 근거: 사장님이 2026-07-14에 단일 emergency batch를 명시 승인했다.
  • OQ-2: operator CLI의 fetched credential broker/token lifecycle을 process-local reuse, shared token cache, 또는 다른 공통 provider 중 무엇으로 고정할지?
    • 상태: Deferred to REQ
    • 처리 방안: delivery-runner/trex-core-dev가 current KISBroker token cache·daemon reinitialization·CLI process lifetime을 분석해 secret-safe alternatives와 retry/throttle behavior를 REQ draft에 제시한다.
    • 인계 조건: REQ가 token lifecycle ownership, error type, retry/no-retry policy 및 regression test seam을 명시한다. 이는 구현 설계 선택이며 Discovery 범위·성공 기준을 바꾸지 않는다.
  • OQ-3: hotfix release 뒤 genichin-longterm의 live sync 실행 대상을 005930 단일 slot으로 확정할지?
    • 상태: Deferred to release gate
    • 처리 방안: hotfix deployment와 dry-run 결과 후 사장님이 broker/ledger/slot cross-check evidence를 보고 승인한다.
    • 인계 조건: release approval에 target portfolio/slot, executed command class, before/after evidence, rollback/hold plan이 기록된다. 본 Discovery는 live sync 실행을 승인하지 않는다.

7. 초기 성공 기준

  • S-1: fetched credential enabled fixture에서 daemon-equivalent path와 sync --dry-run/verify가 동일한 masked source identity와 동일 broker holding set을 사용한다.
  • S-2: KIS token throttle/auth failure fixture에서 CLI가 non-zero failure와 sanitized code를 반환하고, empty balance/broker_qty_zero/state write를 만들지 않는다.
  • S-3: 4-case reconciliation regression이 통과한다: slot 없음→unmanaged, inactive slot+broker holding→managed_non_holding_divergence, matching holding→ok, holding qty mismatch→기존 mismatch path.
  • S-4: 실제 deployed hotfix에서 approved dry-run이 genichin-longterm 대상 broker truth와 divergence classification을 확인하며, live sync 전에는 DB 상태가 변경되지 않는다.

측정 방식 및 데이터 출처

  • S-1 측정 방식: daemon/CLI 공통 provider와 mocked fetched credential/broker fixture의 account fingerprint·balance 결과 비교 테스트 / 데이터 출처: 신규 runtime CLI 및 broker factory tests.
  • S-2 측정 방식: token issuance error를 주입한 get_balance/CLI test에서 exit code, sanitized error, no-write assertion 확인 / 데이터 출처: KIS adapter 및 CLI tests.
  • S-3 측정 방식: reconcile_core_holdings application tests와 PostgreSQL/InMemory repository parity tests 실행 / 데이터 출처: tests/application/test_holding_divergence.py 및 추가 regression tests.
  • S-4 측정 방식: release runbook에 따른 production dry-run command output, broker/ledger/slot readback, audit logs 확인 / 데이터 출처: deployment evidence 및 운영자 기록. 실계좌 write는 승인 후 별도 증거로만 수행한다.

8. REQ로 넘기기 전 확인 체크

  • 범위(In Scope/Out of Scope) 확정
    • 근거: 사장님이 2026-07-14 #39/#40/#41 단일 emergency batch를 승인했고, cross-service 계약 변경은 발견 시 별도 gate로 중단한다.
  • 성공 지표의 측정 방식/데이터 출처 확정
    • 근거: S-1~S-4에 test seam, safe production dry-run evidence, independent QC 재실행 요구가 명시되었다. runtime 계측값은 REQ/release evidence로 수집한다.
  • High 영향 리스크 우선순위 및 해소 책임자 지정
    • 근거: R-1/R-2 delivery owner=delivery-runner/trex-core-dev, independent verifier=qc-reviewer, live operation approver=사장님으로 고정한다.
  • 모든 오픈 질문(OQ) Open 상태 없음 확인
    • 근거: OQ-1 Answered, OQ-2는 REQ 설계로 Deferred, OQ-3은 release approval gate로 Deferred 상태다.
  • Freeze 확정 담당자(CONFIRMED_BY) 지정
    • 근거: 사장님(genichin)이 2026-07-14 HOTFIX scope를 confirm-discovery에서 승인했다. live sync 실행은 별도 release approval 대상이다.

9. 파일 처리 결과

  • 처리 결과: 생성
  • 생성 경로: docs/discovery/dcy-037_20260714_operator-broker-truth-and-managed-slot-divergence-hotfix.md
  • 참조 문서:
    • docs/discovery/dcy-034_20260625_slot-account-blocking-reconcile-sync-coldstart.md
    • docs/discovery/dcy-036_20260628_daystart-credential-fetch-failsafe-trading-load.md
    • GitHub issues #39, #40, #41; trex_core/runtime/daemon.py, trex_core/runtime/cli.py, trex_core/adapters/broker/kis.py, trex_core/application/reconciliation.py
    • .stage-pilot/templates/runtime-flows.md, .stage-pilot/templates/project-structure.md

10. 사용자 결정 필요 항목 요약

확정된 결정

  • HOTFIX delivery boundary: #39·#40·#41을 하나의 emergency batch로 구현·검증해 single release candidate로 관리한다 (2026-07-14, 사장님 승인).
  • Production safety gate: hotfix 배포 뒤 sync --live는 dry-run 및 broker/ledger/slot cross-check evidence가 준비된 경우에도 사장님 명시 release approval이 있어야만 실행한다.
  • Post-deploy evidence: target instance의 masked credential source, broker holdings, slot 상태, portfolio-hub ledger readback은 REQ verification 및 release gate에서 수집한다. 현재는 구현 전이므로 실제 운영 데이터는 없다.

11. Discovery Freeze

섹션 참조

Freeze 플래그

  • Handoff Decision: 승인
  • Handoff Rationale: 사장님이 #39/#40/#41 단일 emergency batch scope를 승인했다. OQ-2는 REQ에서 구현 설계로 구체화하며, OQ-3은 배포 후 별도 release approval gate이므로 REQ drafting을 막지 않는다.
  • Ready for REQ Drafting: true
  • Confirmed By: genichin
  • Confirmed At (KST): 2026-07-14 22:19:29 KST