docs(governance): remove remaining legacy lifecycle artifacts

This commit is contained in:
Hermes Agent
2026-08-08 21:41:22 +09:00
parent 2e5f59e09c
commit 67bd41c085
34 changed files with 0 additions and 2093 deletions
@@ -1,40 +0,0 @@
# BAT-058 Design
- Status: in-progress
- Batch: BAT-058
## 핵심 설계 결정
### 1) cost-basis 응답 파싱 규칙
- `portfolio-hub` 응답에서 `cost_basis_per_unit`(canonical)을 최우선으로 사용
- canonical key가 존재하면 `avg_cost/wac/cost_basis`로 fallback 금지
- canonical이 없을 때만 legacy chain(`avg_cost`, `wac`, `cost_basis`)를 순차적으로 시도
- 유효성: `None` 금지, 숫자 변환 가능, 0보다 큰 값만 허용
### 2) 진단값 분리
- adapter 수준에서 기존 `get_cost_basis()`는 값만 반환
- 새 진단 API `get_cost_basis_with_diagnostics()` 추가:
- `(value, source, reason)`
- public `cost_basis_source`는 `canonical` / `legacy` / `none`만 허용한다. adapter의 `avg_cost` / `wac` / `cost_basis` alias는 `legacy`로 정규화하며, 진단 port의 미승인/비문자 source는 `none`으로 정규화한다.
- public `cost_basis_reason`는 `missing`, `invalid`, `http_status_<3-digit>`, `transport`, `invalid_json`, `invalid_payload`, `not_configured`, `diagnostic_exception`, `diagnostic_source_invalid` 또는 유효한 값이 seed되었을 때의 `null`만 허용한다. `http_status_<3-digit>`만 동적 형식이며 provider field name, parser detail, 임의 오류 텍스트는 공개 값으로 허용하지 않는다.
- adapter parser의 field-level detail은 private implementation detail이다. sync 결과와 `sync --dry-run/--live` 출력에는 위 source/reason taxonomy만 노출한다.
### 3) sync fail-closed 정책
- `slot.status == INACTIVE`, `broker_qty > 0`, 동기화 결과 `avg_price` 미확보 시:
- `dry_run=True`: write 없음 preview 반환 (`after`는 before와 동일), `skipped=True`, `skip_reason=cost_basis_unavailable`
- `dry_run=False`: no-write fail-closed. `error='cost_basis_unavailable'`, `skipped=True`, `after=before`
- 기존 `HOLDING` 및 `PENDING_*`, `ERROR` 경로는 기존 규칙 유지
### 4) 로그/출력
- sync 결과 객체에 아래 필드 추가: `seeded_avg_price`, `cost_basis_source`, `cost_basis_reason`
- CLI 출력에서 위 값 표시
- 실패 시 reason을 노출해 운영자가 어떤 fallback/오류가 발생했는지 즉시 확인 가능
## 비(非)설계 대상
- `portfolio-hub` API 스키마 변경/인증 변경
- 실제 live sync 수행이나 운영 배포 실행
## 검증 전략
- adapter 단위 테스트: canonical 우선/legacy fallback/invalid canonical non-fallback/이유 분리
- sync application 테스트: inactive fail-closed와 dry-run preview, holding 기본 동기화 동작 회귀 보존
- 최소 통합: 기존 테스트셋에서 관련 케이스가 모두 통과되는지 확인
@@ -1,33 +0,0 @@
# BAT-058 Implementation
- Status: in-progress
- Batch: BAT-058
## 변경 대상
1. `trex_core/adapters/portfolio/hub_read.py`
- `get_cost_basis_with_diagnostics()` 새 메서드 추가
- canonical(`cost_basis_per_unit`) 우선 파싱 + legacy fallback 체인 적용
- invalid/parse 오류 reason 문자열 반환
2. `trex_core/application/reconciliation.py`
- `sync_slot_to_broker()`에서 cost-basis 진단 메서드 사용
- inactive onboarding & no cost basis 시 dry-run/no-write + live fail-closed 구현
- sync 결과에 `seeded_avg_price`, `cost_basis_source`, `cost_basis_reason` 추가
3. `trex_core/runtime/cli.py`
- sync 결과 출력에 `cost_basis.seeded`, `cost_basis.source`, `cost_basis.reason`, `error` 표시
4. `tests/adapters/portfolio/test_hub_read.py`
- get_cost_basis canonical/legacy/invalid/오류 reason 검증 추가
5. `tests/application/test_sync.py`
- inactive no-cost_basis live/dry-run 동작
- hold/warning scenarios 회귀 보존
## 상태
- planning: completed
- design: completed
- implementation: completed
- draft-batch-verification: completed
@@ -1,29 +0,0 @@
# BAT-058: cost-basis canonical parse 및 안전 sync seed hotfix
- Status: release-candidate (lead `confirm-batch-verification`, 2026-07-17; Release gate/approval 미실행)
- Profile: standard
- Source Discovery: dcy-038_20260716_cost-basis-response-contract-and-safe-sync-seeding
- Source REQ: REQ-123
- Included REQ: REQ-123
- Excluded REQ: 없음
## Scope
### In Scope
- REQ-123(제안된 cost-basis canonical parse 및 WAC unavailable fail-closed seed)
- portfolio-hub 응답 `cost_basis_per_unit` canonical 우선 파싱 고정
- legacy alias fallback/invalid 처리 규칙 구현
- dry-run 진단 및 live no-write fail-closed semantics
- adapter/application regression test 확장 및 baseline 문서 정합
### Out of Scope
- portfolio-hub API/schema/인증/토큰/설정 변경
- DB migration / 데이터 삭제 / 실서버 직접 수정
- `sync --live`, 배포/운영 재시작 실행은 별도 release 승인 단계
## Notes
- 이 batch는 `suggest-batch-reqs` 다음 단계로 생성한 lead-승인 배치입니다.
- implementation 전환 직전까지는 설계 산출물을 대체하지 않고, 해당 scope만 구현/검증합니다.
@@ -1,43 +0,0 @@
# BAT-058 Planning
- Status: in-progress
- Batch: BAT-058
- Profile: standard
## Included REQ
- REQ-123 (Approved): cost-basis canonical 우선 파싱 + safe sync seeding
## Delivery Plan
1. `trex_core.adapters.portfolio.hub_read`에서 `cost-basis` 응답 파싱 순서 정립
- 우선순위: `cost_basis_per_unit`(canonical) -> `avg_cost` -> `wac` -> `cost_basis`
- canonical가 present이면 legacy fallback를 금지하고 이유를 기록
- 유효성 검사(형변환/양수) 실패 시 source/reason 분리
2. `sync_slot_to_broker`에 WAC 사용 경로를 cost-basis 진단 결과와 함께 통합
- INACTIVE + broker_qty>0 + 가격 미확보 시 dry-run은 preview만
- live에서는 no-write fail-closed + exit fail
3. `tests/adapters/portfolio/test_hub_read.py`와 `tests/application/test_sync.py`를 AC-기반으로 확장
- canonical 우선 규칙, invalid canonical non-fallback, unavailable reason 보존
- inactive onboarding no-write dry-run / live fail-closed
4. `docs/interface-contract.md`, `docs/runtime-flows.md`, `docs/operations.md` baseline 동기화
5. `docs/batches/bat-058...` 산출물(implementation + verification) 작성 후 리드 검토 제출
## Dependencies
- modules: `trex_core/adapters/portfolio/hub_read.py`, `trex_core/application/reconciliation.py`, `trex_core/runtime/cli.py`
- tests: `tests/adapters/portfolio/test_hub_read.py`, `tests/application/test_sync.py`
- docs: `docs/interface-contract.md`, `docs/runtime-flows.md`, `docs/operations.md`
## Design Gate
- Design Required Before Implementation: yes
- Reason: 계약 precedence 및 fail-closed semantics이 배포 안전성에 직결되어, 미리 수용 기준(재기록/no-write/오류 로그)을 고정해야 함.
## Milestones
1. planning 확정 (현재)
2. design 작성
3. implementation 완료
4. draft-batch-verification 작성 및 테스트 실행
5. QC handoff
## Risks
- R1 High: canonical 값 parse 실패 시 legacy fallback를 허용하면 REQ-123 위반 가능성(의도치 않은 source 사용)
- R2 High: inactive onboarding에서 가격 미확보일 때 live write가 진행될 경우 거래원장·slot 상태 오염
- R3 Medium: 비용/시간 과다로 기존 `cost_basis` 회귀 테스트 간과 시 interface contract drift
@@ -1,75 +0,0 @@
# BAT-058 Verification
- Status: release-candidate (lead `confirm-batch-verification`, 2026-07-17)
- Batch: BAT-058
- Verified By: delivery-runner (implementation evidence) + independent `dev-qc` QC-7
- Confirmed By: trex-dev-lead (QC-7 `PASS-WITH-NOTES` 수용; 사용자 지시: “QC-7 PASS-WITH-NOTES로 batch verification을 진행해”)
## Verification 범위
- Scope: REQ-123 및 `dcy-038` 기반 hotfix 범위
- `trex_core/adapters/portfolio/hub_read.py`
- `trex_core/application/reconciliation.py`
- `trex_core/runtime/cli.py`
- `tests/adapters/portfolio/test_hub_read.py`
- `tests/application/test_sync.py`
## AC 점검 결과
| AC | 내용 | 결과 | 근거 |
|---|---|---|---|
| AC-1 | canonical `cost_basis_per_unit` 우선 파싱 + 유효성 검증 | ✅ PASS | `test_get_cost_basis_with_diagnostics_prefers_canonical_when_valid`, `test_get_cost_basis_with_diagnostics_canonical_null_blocks_legacy`, `test_get_cost_basis_with_diagnostics_canonical_non_numeric_blocks_legacy`, `test_get_cost_basis_with_diagnostics_canonical_negative_blocks_legacy` |
| AC-2 | canonical invalid 시 legacy masking 금지, canonical 부재 시 legacy fallback 순차 적용 | ✅ PASS | `test_get_cost_basis_with_diagnostics_canonical_invalid_blocks_legacy`, `test_get_cost_basis_with_diagnostics_legacy_chain_fallback_order`, `test_get_cost_basis_with_diagnostics_legacy_skips_invalid_values` |
| AC-3 | 성공/실패 이유 분류(reason) | ✅ PASS | `test_get_cost_basis_with_diagnostics_canonical_null_blocks_legacy`, `test_get_cost_basis_with_diagnostics_canonical_non_numeric_blocks_legacy`, `test_get_cost_basis_with_diagnostics_canonical_negative_blocks_legacy`, `test_get_cost_basis_with_diagnostics_missing_field_returns_missing_reason`, `test_get_cost_basis_with_diagnostics_all_legacy_invalid_returns_last_reason` |
| AC-4 | non-2xx/transport 실패 사유 분류 | ✅ PASS | `test_get_cost_basis_with_diagnostics_non_2xx_or_transport_keeps_diagnostic` |
| AC-5 | dry-run no-write preview (seeded=true/false 반영) | ✅ PASS | `test_sync_dry_run_no_write`, `test_sync_dry_run_inactive_valid_canonical_seed_previews_without_writes` |
| AC-6 | dry-run `inactive + broker_qty>0 + avg_price unavailable` no-write preview | ✅ PASS | `test_sync_dry_run_no_write` |
| AC-7 | live fail-closed (status/qty/avg_price 모두 미반영, non-zero 종료) | ✅ PASS | independent QC-7이 `test_sync_fail_closed_skipped_error_exits_nonzero_without_writes` 및 legacy-only `0.0`/negative/NaN/+Inf no-write 회귀를 재실행·검토함; CLI rc=1, mutation SQL 0, warning clear 0 확인 |
| AC-8 | HOLDING 정상 동기화 회귀 및 seed 적용 유지 | ✅ PASS | `test_sync_holding_qty_mismatch_updated`, `test_sync_seeds_avg_price_from_cost_basis_port`, `test_sync_seeds_avg_price_holding_with_cost_basis`, `test_sync_degrades_gracefully_cost_basis_returns_none` |
| AC-9 | response-contract + regression 테스트 전체 통과 | ✅ PASS | `tests/runtime/test_cli_operator.py`, `tests/adapters/portfolio/test_hub_read.py`, `tests/application/test_sync.py` (independent QC-7 affected suite: 113 passed; full suite: 833 passed, 47 skipped) |
| AC-10 | public diagnostic source/reason contract normalization (raw field/detail non-leak) 및 release gate | ✅ PASS (release 실행은 별도 gate) | independent QC-7이 `docs/interface-contract.md`, `docs/runtime-flows.md`, `docs/operations.md`와 bounded diagnostic/no-leak 구현을 대조함. REQ가 요구하는 actual release artifact·approval·live evidence는 Release 단계에서만 생성/판정하며, 본 batch verification은 이를 승인하거나 대체하지 않음. |
## 독립 QC-7 및 Lead `confirm-batch-verification`
- QC-7 실행: 2026-07-17 00:30:22 UTC 시작, supervised child exit `0`.
- QC verdict: `PASS-WITH-NOTES`.
- QC-7 독립 재실행: legacy-invalid focused `8 passed, 19 deselected`; affected suite `113 passed`; full suite `833 passed, 47 skipped`; Ruff `All checks passed`; compileall exit `0`; `git diff --check` exit `0`.
- Lead 판정: 사용자 지시에 따라 QC-7 `PASS-WITH-NOTES`를 수용하여 BAT-058의 `confirm-batch-verification`을 완료하고 `release-candidate`로 승격한다.
- Note 해소: REQ-123 AC-10의 release artifact/explicit approval/dry-run evidence/live execution은 **Release 단계의 별도 gate**다. 이 batch confirmation은 release approval, `sync --live`, 배포, credential 접근을 승인하지 않는다.
- Ledger note: 이전 QC 반복 횟수 표기는 과거 handoff 사이에 불일치가 있어 이 verification의 판정 근거로 사용하지 않는다. 이번 confirmation의 근거는 QC-7의 독립 verdict와 위 재실행 증적이다.
- Immutable provenance mapping: 구현 payload는 PR #43의 [`81a853a`](https://github.com/fourblocks2020/trex-core/commit/81a853a314391da7a8bc9df5517013c7fd296d61)이다. post-commit PR QC는 docs-only head `81c9259`에서 focused `113 passed`, full `833 passed, 47 skipped`, Ruff/compileall/diff hygiene를 독립 재실행했고, `81a853a..81c9259`의 `trex_core`/`tests` delta가 없음을 확인했다. 그 QC의 initial FAIL은 코드 동작이 아니라 REL-012 source-publication 문서 모순이며, 해당 문서-only remediation과 re-QC가 merge 전 필요하다.
## 실행 커맨드 및 실제 재실행 결과
아래는 delivery worktree에서 실행한다. 공유 가상환경은 worktree의 상위 repository root에 있으므로 `git rev-parse`로 worktree root를 해석해 사용한다.
```bash
WORKTREE="$(git rev-parse --show-toplevel)"
PYTHON="${TREX_CORE_PYTHON:-$(cd "$WORKTREE/../.." && pwd)/.venv312/bin/python}"
cd "$WORKTREE"
"$PYTHON" -m pytest tests/runtime/test_cli_operator.py tests/adapters/portfolio/test_hub_read.py tests/application/test_sync.py -q
"$PYTHON" -m pytest -q
"$PYTHON" -m ruff check trex_core/runtime/cli.py trex_core/adapters/portfolio/hub_read.py trex_core/application/reconciliation.py tests/runtime/test_cli_operator.py tests/adapters/portfolio/test_hub_read.py tests/application/test_sync.py
"$PYTHON" -m compileall -q trex_core/runtime/cli.py trex_core/adapters/portfolio/hub_read.py trex_core/application/reconciliation.py tests/runtime/test_cli_operator.py tests/adapters/portfolio/test_hub_read.py tests/application/test_sync.py
```
- focused pytest (2026-07-17, QC-5 재실행): `105 passed in 0.28s`
- full pytest (2026-07-17, QC-5 재실행): `825 passed, 47 skipped in 1.71s`
- ruff (2026-07-17, QC-5 재실행): `All checks passed!`
- compileall (2026-07-17, QC-5 재실행): `compileall: PASS` (exit 0; quiet mode)
- `git diff --check` (2026-07-17, QC-5 재실행): `PASS` (exit 0; no output)
- QC-6 targeted pytest (2026-07-17): `tests/application/test_sync.py -q -k legacy_only_invalid_wac` → `8 passed, 19 deselected in 0.05s`.
- QC-6 focused pytest (2026-07-17): `tests/runtime/test_cli_operator.py tests/adapters/portfolio/test_hub_read.py tests/application/test_sync.py -q` → `113 passed in 0.29s`.
- QC-6 full pytest (2026-07-17): `pytest -q` → `833 passed, 47 skipped in 1.72s`.
- QC-6 ruff (2026-07-17): 대상 6개 source/test 파일 → `All checks passed!` (exit 0).
- QC-6 compileall (2026-07-17): 대상 6개 source/test 파일 → exit 0 (quiet mode; no output).
## 미해결/미실행 항목
- 실운영 배포, `sync --live` 실행, DB 직접 수정은 실행하지 않음.
- 본 문서는 implementation self-approval가 아니다. 독립 QC-7 `PASS-WITH-NOTES`와 lead `confirm-batch-verification`까지 반영한 batch verification 기록이며, release approval은 포함하지 않는다.
## 위험/차단 요인
- 없음. 단, Release 단계에서 REQ-123 AC-10의 explicit release approval, 단일 대상 live 실행 승인, rollback 기준, post-release monitoring plan을 별도 `draft-release` 산출물로 점검해야 한다.
@@ -1,9 +0,0 @@
# BAT-059 intent venue fan-in
- Profile: standard
- Status: release-candidate
- Discovery: dcy-039
- Included REQ: REQ-124
전략 wire의 venue를 슬롯과 브로커 주문·취소까지 additive로 연결한다.
배포와 실주문은 이 batch 범위가 아니다.
@@ -1,8 +0,0 @@
# BAT-059 Design
`Slot.venue`는 현재 pending 주문의 거래소 구분값이다. admission이 허용값을
검증하고 pending 전이에 저장한다. HOLDING에는 마지막 주문값이 남지만 sell intent가
현재 청산 venue로 덮어쓴다. INACTIVE에서는 KRX로 초기화한다.
기본 KRX는 기존 전략의 wire·stub·운영 동작을 보존한다. NXT 취소만 venue keyword를
명시해 이전 테스트 double과의 하위 호환도 유지한다.
@@ -1,7 +0,0 @@
# BAT-059 Implementation
- `BuyIntent`/`SellIntent`와 handler venue 추가
- `Slot.venue`, migration 017, PostgreSQL mapping 추가
- `ExecutionService` 주문·취소 venue 전달
- invalid venue 사전 거부
- 전용 NXT flow 테스트 추가
@@ -1,7 +0,0 @@
# BAT-059 Planning
1. venue wire·domain 계약 추가
2. 슬롯 persistence와 migration 추가
3. execution submit/cancel 전달
4. NXT 신규 테스트와 전체 KRX 회귀
5. interface/data/runtime 문서 동기화
@@ -1,10 +0,0 @@
# BAT-059 Verification
- Status: PASS
- Targeted: 32 passed
- Full: 837 passed, 47 skipped
- 실계좌·실주문: 실행하지 않음
- 기존 runtime: 변경하지 않음
검증 범위는 wire→slot→broker submit/cancel, invalid venue, INACTIVE reset,
PostgreSQL row mapping, 전체 회귀다.
@@ -1,48 +0,0 @@
# BAT-062 Design
- Status: confirmed
- Batch: BAT-062
## Architecture Summary
- `trex_core.domain.capital_allocation`은 외부 상태를 읽지 않는 순수 정책이다.
- 호출 계층이 계좌 가용 현금과 후보별 보호 한 주 가격을 준비하고, 정책은 후보별 배정액과 제외 사유를 반환한다.
- 전략 조립 계층은 이 배정액으로 최종 정수 주문수량을 계산하며, 기존 Core 주문 실행과 Portfolio Hub 원장은 그대로 유지한다.
## Changed Areas
- Docs: `docs/project-structure.md`, Discovery·REQ·BAT 문서
- Modules: `trex_core/domain/capital_allocation.py`, 로컬 KIS 어댑터 연결부
- Interfaces: `CapitalCandidate`, `CapitalAllocation`, `CapitalAllocationPlan`, `protected_share_cost`, `plan_liquid_capital`, `allocate_liquid_slots`
## Key Decisions
1. 공통 계산은 Core가 소유하지만 브로커 현금·호가 조회와 실제 주문수량 산출은 소유하지 않는다.
2. 한 주 유동 여유는 가장 싼 보호 한 주 가격 단위로 제한한다.
3. 최악 활성 슬롯 조합을 먼저 만족시킨 뒤 최대 공통 배정액을 이분 탐색한다.
4. 제외와 현금 없음은 0원만 반환하지 않고 안정적인 사유를 함께 반환한다.
5. 기존 어댑터 호출부는 `portfolio_id → 배정액` 호환 표면으로 단계적으로 전환한다.
## Edge Cases
- 현금 0: 전 후보 `no_available_cash`.
- 빈 후보·음수 현금·0 이하 슬롯·중복 식별자·0 이하 보호가격: 명시적 예외.
- 적격 후보가 슬롯보다 적음: 실제 적격 수만 활성 조합에 포함.
- 동가 최고 후보 제외: 입력 순서의 뒤 후보를 먼저 제외해 기존 공식과 결정성을 보존.
- 후보가 모두 제외됨: 사유를 보존한 0원 계획.
## Architecture Impact
- project-structure
## Reference Doc Update Plan
- project-structure.md: 새 순수 도메인 모듈, 책임, 의존 방향, 테스트 경로를 추가한다.
- runtime-flows.md: 런타임·주문 흐름 변경이 없어 갱신하지 않는다.
## 비설계 대상
- `BrokerPort` 현금 조회 확장
- `BuyIntent` 또는 메시지 계약 변경
- Portfolio Hub API·원장 변경
- 실전 배포·주문 활성화
@@ -1,46 +0,0 @@
# BAT-062 Implementation
- Status: completed
- Batch: BAT-062
## Plan Summary
- 로컬 KIS 어댑터의 검증된 배분 공식을 Core 순수 도메인으로 옮기고, 어댑터는 외부 입력과 결과 매핑만 담당하도록 정리한다.
## Changed Files
- `trex_core/domain/capital_allocation.py`
- `tests/domain/test_capital_allocation.py`
- `tests/integration/test_kis_submission_recovery_virtual.py`
- `validation/trading-execution-adapter.json`
- `docs/project-structure.md`
- `docs/discovery/dcy-042_20260729_shared-account-capital-allocation-policy.md`
- `docs/srs/Interface/req-127_shared-account-capital-allocation-policy.md`
- `docs/batches/bat-062_20260729_shared-account-capital-allocation/*`
- 별도 docker-local 작업트리의 `live_tools/account_capital_allocator.py`
## Execution Log
1. 보호 한 주 가격, 후보·배정·계좌 계획 불변 데이터 모델을 추가했다.
2. 균등 기준, 한 주 유동 여유, 최악 활성 슬롯 제거, 최대 공통 배정액 계산을 구현했다.
3. 입력 검증과 계획 내부 현금 불변식 검사를 추가했다.
4. 메가·탑50 대표값, 오류 입력, 10,000건 무작위 불변식 테스트를 추가했다.
5. 로컬 KIS 어댑터의 중복 계산을 Core 정책 호출로 대체했다.
6. 선행 Core 브랜치의 가상 KIS 복구 테스트 시계를 거래 가능 시각으로 고정해
야간 실행에서도 같은 주문 복구 경로를 검증하도록 했다.
7. Core 공용 인증의 pytest 단계를 `uv --with pytest`로 고정해 격리 작업트리의
로컬 가상환경 상태와 무관하게 재현되도록 했다.
## Validation
- Core 신규 테스트: 16 passed.
- 최신 선행 Core 전체 회귀: 919 passed, 51 skipped.
- 기존 어댑터 공식 50,000건 완전 일치: PASS.
- Core 연결 로컬 어댑터 전체 회귀: 161 passed, 4 skipped.
- 탑50 월간 가상거래 요약·거래원장·배분결정 원장 완전 일치: PASS.
- 최종 재실행 결과는 `verification.md`에 고정한다.
## Remaining Risks
- 이 브랜치는 실전 이미지에 반영되지 않았다. Core 병합 후 어댑터 이미지에서 모듈 가용성과 공용 가상 인증을 다시 확인해야 한다.
- 주문수량·주문 게이트·계좌 설정 변경은 이 배치에 포함되지 않는다.
@@ -1,29 +0,0 @@
# BAT-062: 공통 계좌 자본배분 정책
- Status: release-candidate
- Profile: standard
- Source Discovery: dcy-042_20260729_shared-account-capital-allocation-policy
- Source REQ: REQ-127
- Included REQ: REQ-127
- Excluded REQ: 없음
## Scope
### In Scope
- Core 순수 도메인 자본배분 정책과 입력 검증
- 메가·탑50 대표 경계값 및 무작위 불변식 테스트
- 로컬 KIS 어댑터를 Core 호출 연결부로 축소
- 기존 공식 완전 일치, 전체 회귀, 탑50 월간 가상 원장 비교
- 프로젝트 구조 문서 동기화
### Out of Scope
- 브로커·Portfolio Hub API 또는 데이터 변경
- 주문 의도, 슬롯 상태머신, 실전 런타임·주문 게이트 변경
- 계좌 입금·전략 선택·주문 실행
## Notes
- 병렬 Core 브랜치의 BAT-059~061과 충돌하지 않도록 BAT-062를 사용한다.
- 실전 적용은 Core 병합·이미지 검증과 별도 운영 승인 뒤에만 수행한다.
@@ -1,47 +0,0 @@
# BAT-062 Planning
- Status: confirmed
- Batch: BAT-062
- Profile: standard
## Included REQ
- REQ-127 (Approved): 공통 계좌 자본배분 순수 정책
## Delivery Plan
1. 기존 로컬 어댑터 공식과 Core 책임 경계를 고정한다.
2. Core에 데이터 모델·보호가격·배분 계획·호환 결과 함수를 구현한다.
3. 로컬 KIS 어댑터는 현금·호가 입력과 Core 결과 매핑만 남긴다.
4. 단위·속성·완전 일치·전체 회귀·월간 가상거래를 순서대로 검증한다.
5. 문서와 검증 증거를 동기화한 뒤 Draft PR로 제출한다.
## Dependencies
- Internal:
- Python 표준 `dataclasses`, `decimal`
- 기존 `dcy-021`/`REQ-061` 주문수량 계약
- External:
- 검증 시 로컬 KIS 어댑터와 탑50 가상거래 도구
- 실전 브로커·Portfolio Hub 연결은 필요하지 않음
## Design Gate
- Design Required Before Implementation: yes
- Reason: 새 공개 도메인 모듈과 프로젝트 구조 기준선이 추가되며 Core·어댑터·전략·원장 책임 경계를 구현 전에 고정해야 한다.
## Milestones
1. 순수 정책과 Core 단위·속성 테스트
2. 어댑터 얇은 연결부와 기존 공식 완전 일치
3. 전체 회귀와 메가·탑50 가상 검증
4. 문서·품질 게이트와 Draft PR
## Risks
- 기존 공식과 반올림 또는 동률 제외 순서가 달라질 수 있다.
- 대응: 고정 시드 50,000건 결과 완전 일치.
- Core가 외부 현금·호가 조회 책임까지 흡수할 수 있다.
- 대응: 새 모듈의 외부 의존을 금지하고 어댑터 연결부를 별도 검토.
- 실제 현금보다 큰 동시 슬롯 배정이 생길 수 있다.
- 대응: 최악 활성 슬롯 합 불변식을 구현 내부와 10,000건 속성 테스트에서 이중 검증.
@@ -1,68 +0,0 @@
# BAT-062 Verification
- Status: confirmed
- Batch: BAT-062
- Verified By: delivery-runner
## Acceptance Mapping
| AC | 결과 | 근거 |
| --- | --- | --- |
| REQ-127 AC-1 | PASS | `test_mega_two_million_excludes_hynix_and_uses_samsung_lane` |
| REQ-127 AC-2 | PASS | `test_mega_three_million_flexes_one_share_into_hynix_lane` |
| REQ-127 AC-3 | PASS | `test_top50_three_slots_exclude_unaffordable_high_price_candidate` |
| REQ-127 AC-4 | PASS | `test_zero_cash_fails_closed_with_explicit_reason` |
| REQ-127 AC-5 | PASS | 잘못된 보호가격·현금·슬롯·후보·중복·유동 여유 입력 단위 테스트 |
| REQ-127 AC-6 | PASS | 고정 시드 10,000건 최악 활성 슬롯 현금 불변식 |
| REQ-127 AC-7 | PASS | 이전 어댑터 커밋 `46f5936`과 고정 시드 50,000건 결과 완전 일치 |
| REQ-127 AC-8 | PASS | 최신 선행 Core 919 passed/51 skipped, Core 연결 어댑터 161 passed/4 skipped |
| REQ-127 AC-9 | PASS | 탑50 월간 요약 동일, `trade_ledger.csv`·`allocation_decisions.csv` 바이트 동일 |
| REQ-127 AC-10 | PASS | Core·docker-local 격리 작업트리만 변경, 실전 런타임·주문 게이트·계좌·Hub 변경 없음 |
## Evidence
- Test:
- Core 정책: 16 passed.
- 최신 선행 Core 전체: 919 passed, 51 skipped.
- Core 대상 Ruff: PASS.
- 로컬 어댑터 연결부: 11 passed.
- 로컬 어댑터 전체: 161 passed, 4 skipped.
- 기존 공식 완전 일치: 50,000 cases PASS, seed `20260729`.
- 탑50 월간 가상계좌: PASS, 111 signals, 71 completed trades,
max active 3, minimum cash 40,500원, open orders 0, positions 0,
duplicate orders/fills 0, restart equivalent true.
- 공용 가상 인증: logic→regression→virtual contract→virtual end-to-end
→fault injection→restart recovery→ledger reconcile 1~7단계 PASS.
- 선행 Core 가상 KIS 복구 3건은 거래 가능 시각을 고정해 야간 회귀에서도
주문 결과불명·재시작·거부 후 재시도 경로가 결정적으로 통과한다.
- Core 공용 인증은 테스트 도구를 명시적으로 설치해 격리 작업트리에서도 같은
1~7단계를 실행한다.
- Manual:
- Core는 순수 계산, 어댑터는 현금·호가, 전략은 후보·슬롯·최종 수량,
Portfolio Hub는 원장 정본으로 책임 경계를 대조했다.
- Logs:
- 탑50 월간 최종 재실행: `/tmp/top50-core-policy-final.AhUjsm`
- 공용 인증:
`/Volumes/J_HDD/virtual_exchange/artifacts/validation/trex-top50-swing-v1/top50-core-allocation-20260729-1953-kst/run.json`
- Core 실행 인증:
`/Volumes/J_HDD/virtual_exchange/artifacts/validation/trex-core-execution/core-capital-allocation-20260729-2005-kst/run.json`
## Architecture Verification
- Architecture Impact: `project-structure`.
- `docs/project-structure.md`에 새 모듈·책임·의존 규칙과 테스트 경로를 동기화했다.
- `runtime-flows.md`는 런타임 흐름 변경이 없어 갱신 대상이 아니다.
## Result
- Ready for Release: true
- Blocking Issues:
- 없음.
## Safety Classification
- `research_only=true`
- `official_comparable=false`
- `real_order_allowed=false`
- `order_adapter=virtual_locked_replay`
- 이 확인은 실전 배포·실주문·주문 게이트 활성화를 승인하지 않는다.
@@ -1,33 +0,0 @@
# BAT-063 Design
- Status: confirmed
- Batch: BAT-063
## Architecture Summary
- KIS 일별 체결조회 거래 ID와 공통 파라미터는 `KISBroker`의 private helper가
한 번만 생성한다.
- 실전은 `TTTC0081R`, 모의는 `VTTC0081R`을 사용한다.
- 일반 주문번호 조회와 결과불명 주문 탐색 모두
`EXCG_ID_DVSN_CD=ALL`을 전달한다.
- 체결·부분체결·취소·거부·결과불명 해석과 폴링 간격은 그대로 유지한다.
## Changed Areas
- Module: `trex_core/adapters/broker/kis.py`
- Tests: `tests/adapters/broker/test_kis.py`,
`tests/adapters/broker/test_venue.py`
- Docs: `docs/interface-contract.md`, `docs/runtime-flows.md`
## Key Decisions
1. sidecar가 거래 ID를 바꾸지 않아도 Core 단독으로 KRX·NXT 체결을 조회한다.
2. 거래소별 분기 대신 `ALL` 하나로 주문번호와 결과불명 후보를 찾는다.
3. 공개 `BrokerPort.poll_fill` 시그니처는 변경하지 않는다.
4. 구형 거래 ID fallback을 두지 않는다. 공식 명세가 구형 거래 ID 중단 가능성을
명시하기 때문이다.
## Architecture Impact
- KIS broker transport
- 주문 상태 해석과 도메인·애플리케이션 경계에는 영향 없음
@@ -1,39 +0,0 @@
# BAT-063 Implementation
- Status: completed
- Batch: BAT-063
## Plan Summary
- KIS 일별 체결조회 요청 계약을 Core의 단일 private helper로 모으고 일반 주문과
결과불명 주문 조회가 동일한 신형 거래 ID와 전체 거래소 조건을 사용하게 한다.
## Changed Files
- `trex_core/adapters/broker/kis.py`
- `tests/adapters/broker/test_kis.py`
- `docs/interface-contract.md`
- `docs/runtime-flows.md`
- `docs/srs/Integration/req-118_kis-broker-venue-parameterization.md`
- `docs/batches/bat-063_20260729_kis-daily-fill-all/*`
## Execution Log
1. 날짜·종목·주문번호를 받는 `_daily_fill_request`를 추가했다.
2. 실전 `TTTC0081R`, 모의 `VTTC0081R`, `EXCG_ID_DVSN_CD=ALL`을 한 곳에
고정했다.
3. 일반 `poll_fill`과 결과불명 주문 복구가 같은 helper를 사용하도록 바꿨다.
4. 기존 응답 파싱, 상태 판정, 공개 포트와 재시도 주기는 변경하지 않았다.
5. 일반·결과불명·실전·모의 요청 계약을 단위검사로 고정했다.
## Validation
- KIS broker 집중검사: 59 passed, 1 skipped.
- Core 전체 회귀: 922 passed, 51 skipped.
- 변경 파일 Ruff: PASS.
- 공용 가상 인증은 커밋 후 깨끗한 작업트리에서 1~7단계로 실행한다.
## Remaining Risks
- 가상 인증은 실전 KIS 응답이나 주문 가능 상태를 증명하지 않는다.
- 이 배치는 실주문, 계정 설정, 주문 게이트, 배포를 변경하지 않는다.
@@ -1,29 +0,0 @@
# BAT-063: KIS 전체 거래소 일별 체결조회
- Status: release-candidate
- Profile: standard
- Source Discovery: REQ-118 CHG-20260729-01
- Source REQ: REQ-118
- Included REQ: REQ-118 CHG-20260729-01
- Excluded REQ: 없음
## Scope
### In Scope
- 일반 주문과 결과불명 주문의 일별 체결조회 거래 ID를 KIS 신형 계약으로 전환
- `EXCG_ID_DVSN_CD=ALL`을 통한 KRX·NXT 통합 체결조회
- 요청 계약 단위검사, 전체 회귀, 가상거래소 주문 수명주기 인증
- 인터페이스·런타임 문서 동기화
### Out of Scope
- 주문 제출·취소·수량·가격·전략 신호 변경
- 실주문 생성 또는 계좌·주문 게이트 변경
- KIS sidecar 배포와 제거
## Notes
- KIS 공식 예제와 저장소 내 KIS REST 명세가 신형 거래 ID와 `ALL`을 동일하게
요구한다.
- Core 인증 통과 뒤 docker-local sidecar 축소를 별도 저장소에서 수행한다.
@@ -1,34 +0,0 @@
# BAT-063 Planning
- Status: confirmed
- Batch: BAT-063
- Profile: standard
## Included REQ
- REQ-118 CHG-20260729-01 (Approved): KIS 전체 거래소 일별 체결조회
## Delivery Plan
1. KIS 공식 거래 ID·거래소 파라미터 계약을 테스트로 고정한다.
2. 일반 주문과 결과불명 주문 조회에 하나의 요청 생성 함수를 적용한다.
3. 기존 체결·취소·거부·결과불명 상태 해석을 변경하지 않는다.
4. 집중검사, 전체 회귀, 가상 주문 수명주기 1~7단계를 순서대로 검증한다.
5. 문서와 검증 증거를 동기화하고 Draft PR로 제출한다.
## Dependencies
- Internal: `KISBroker.poll_fill`, `_poll_unknown_submission`
- External: KIS `inquire-daily-ccld` 신형 요청 계약
## Design Gate
- Design Required Before Implementation: yes
- Reason: 실전 주문 체결조회 경로이므로 요청 생성 책임과 불변 동작을 먼저 고정한다.
## Risks
- 결과불명 주문 조회만 구형 계약에 남을 수 있다.
- 대응: 일반·결과불명 두 경로의 거래 ID와 `ALL`을 별도 회귀검사로 고정한다.
- 체결 상태 해석이 바뀔 수 있다.
- 대응: 요청 생성만 변경하고 응답 파싱 함수와 공개 포트 시그니처를 보존한다.
@@ -1,51 +0,0 @@
# BAT-063 Verification
- Status: confirmed
- Batch: BAT-063
- Verified By: delivery-runner
## Acceptance Mapping
| AC | 결과 | 근거 |
| --- | --- | --- |
| REQ-118 AC-1~3 | PASS | 선행 BAT-056 증거 + 이번 KIS broker 전체 회귀 |
| REQ-118 AC-4 일반 조회 | PASS | `test_poll_fill_uses_current_daily_fill_contract_for_all_venues` |
| REQ-118 AC-4 결과불명 조회 | PASS | `test_unknown_submission_resolves_from_recent_daily_orders` |
| REQ-118 AC-5~6 | PASS | 기존 현재가 시장구분 테스트 + Core 전체 회귀 |
| REQ-118 AC-7~8 | PASS | 주문·취소 집중검사와 Core 전체 회귀 |
| 실전·모의 거래 ID | PASS | `test_daily_fill_request_uses_current_live_and_mock_contract` |
| 가상 주문 수명주기 | PASS | 공용 인증 1~7단계 |
## Evidence
- Test:
- KIS broker 집중검사: 59 passed, 1 skipped.
- Core 전체: 922 passed, 51 skipped.
- 변경 파일 Ruff: PASS.
- Certification:
- 공용 가상 인증: logic → regression → virtual contract → virtual end-to-end
→ fault injection → restart recovery → ledger reconcile 전 단계 PASS.
- 최종 상태: 미체결 0, 포지션 0, 중복 주문 0, 원장 일치.
- 산출물:
`/Volumes/J_HDD/virtual_exchange/artifacts/validation/trex-core-execution/core-daily-fill-all-20260729-2146-kst/run.json`
## Architecture Verification
- 공개 broker 포트, 상태 해석, 폴링 간격, 전략·가격·수량 정책은 변경하지 않았다.
- 수익률 BT 영향은 없다. 주문 수명주기 변화 가능성은 공용 가상 인증 1~7단계로
검증했다.
- 저장소에 FROZEN 규칙 파일이 없고 변경 범위에 `TODO`, `FIXME`, `HACK`,
`Quick Fix` 잔여가 없다.
## Result
- Ready for Release: true
- Blocking Issues:
- 없음.
## Safety Classification
- `research_only=true`
- `official_comparable=false`
- `real_order_allowed=false`
- `order_adapter=virtual_locked_replay`
@@ -1,227 +0,0 @@
# 0. 문서 상태
- 상태: confirmed
- 문서 ID: dcy-038_20260716_cost-basis-response-contract-and-safe-sync-seeding
- 이슈명: P1 — portfolio-hub cost-basis response contract 정합 및 안전한 sync 평균가 seed
- 작성 시각(KST): 2026-07-16 22:37:30 KST
- 마지막 갱신 시각(KST): 2026-07-16 22:44:20 KST (confirm-discovery)
- 대체됨: 없음
- 선행 Discovery: dcy-034, dcy-037
- 후속 Discovery 참조: 없음
- 생성된 REQ 참조: `docs/srs/Integration/req-123_cost-basis-canonical-parse-and-safe-sync-seeding.md`
# 1. 계획 상태 요약
- 연계 문서 현황:
- 선행 `dcy-034`/REQ-105는 broker-held position을 inactive slot에 onboarding하는 `sync`와 portfolio-hub cost-basis seed를 도입했다.
- 선행 `dcy-037`/BAT-057/REL-011은 fetched credential broker truth 및 managed non-holding divergence를 정정했다. 운영 preflight는 해당 credential/config 가설을 정상으로 확인했다.
- portfolio-hub는 `GET /v1/portfolios/{portfolio_id}/holdings/cost-basis`에서 `HoldingsCostBasisResponse.cost_basis_per_unit`을 canonical WAC field로 반환한다.
- 해석:
- trex-core의 realized-PnL adapter는 이미 `cost_basis_per_unit`을 읽지만, sync용 `PortfolioHubReadAdapter.get_cost_basis()`는 legacy `avg_cost`, `wac`, `cost_basis`만 탐색한다.
- 따라서 정상 HTTP 200 response의 `cost_basis_per_unit=124580`도 `None`으로 해석된다. reconcile은 이를 graceful degrade로 취급해 qty/status만 seed하고 `avg_price`를 누락할 수 있다.
# 2. 요구사항 판정 결과
- 판정: 신규 P1 hotfix Discovery
- 근거:
- REQ-105 FR-7은 cost-basis seed 목적을 정의했지만, portfolio-hub canonical response field를 adapter contract와 regression test로 고정하지 못했다.
- dcy-037의 credential source/broker truth 결함과 달리, 이번 결함은 정상 인증·정상 HTTP 200 response를 잘못 parse하는 consumer contract defect다.
- `avg_price`는 slot의 전략/손익 판단에 영향을 주는 값이므로, 양수 broker holding에 avg_price 없이 live sync하는 안전 정책을 별도 명시해야 한다.
# 3. 문제점의 요약
## 현재 상태
- portfolio-hub schema: `HoldingsCostBasisResponse.cost_basis_per_unit: float | None`.
- portfolio-hub endpoint: 보유 수량이 양수면 WAC를 `cost_basis_per_unit`에 반환한다.
- trex-core `PortfolioHubReadAdapter.get_cost_basis()`:
```python
val = body.get("avg_cost") or body.get("wac") or body.get("cost_basis")
```
- trex-core reconcile `sync_slot_to_broker()`:
- adapter가 `None`을 반환하면 avg_price seed를 건너뛰고, dry-run은 `avg_price: null`, `seeded_avg_price: false`를 반환한다.
- live path는 현재 inactive slot을 `holding`으로 전이하고 broker qty를 쓸 수 있다.
- 운영 재현(비밀 없는 response shape):
```text
HTTP 200: {"current_quantity": 5.0, "cost_basis_per_unit": 124580.0}
current adapter result: None
sync --dry-run: holding / qty=5 / avg_price=null / seeded_avg_price=false
```
## 기대 상태
- trex-core가 portfolio-hub의 canonical `cost_basis_per_unit`을 우선 읽고, 정상 양수 WAC를 slot `avg_price` seed에 사용한다.
- legacy field는 호환 필요성이 확인된 경우에만 lower-priority fallback으로 남긴다.
- cost-basis가 실제로 unavailable/malformed/invalid인 경우에는 정상 canonical 응답 parsing failure와 구분 가능한 operator reason을 낸다.
- 양수 broker holding을 inactive slot으로 live onboarding할 때 WAC가 없으면, 승인된 안전 정책에 따라 불완전한 slot write를 방지한다.
## 이해관계자 및 제약
- 사장님: P1 범위 및 live sync fail-closed 정책의 승인자.
- 팀장: Discovery/REQ/Batch/release gate 관리.
- trex-core-dev: adapter, reconcile, CLI/operator output, tests 및 baseline 문서 delivery 담당.
- qc-reviewer: canonical response contract, no-write barrier, regression의 독립 검증 담당.
- portfolio-hub maintainers: existing response contract의 source owner; 이 Discovery에서는 API/model 변경을 수행하지 않는다.
- 제약:
- `sync --live`, DB 직접 수정, token/config 변경은 본 Discovery/REQ/Batch/verification 완료 및 별도 release approval 전 금지한다.
- raw credential, account number, token을 fixtures/logs/문서에 기록하지 않는다.
- portfolio-hub endpoint/schema 및 service-token authorization policy를 변경하지 않는다.
## 주요 사용자 시나리오
1. 운영자가 inactive slot과 broker qty>0을 대상으로 `sync --dry-run`을 실행한다. canonical WAC가 있으면 결과는 `avg_price=<WAC>`, `seeded_avg_price=true`로 표시되며 DB write는 없다.
2. portfolio-hub가 WAC를 `null`로 반환하거나 response가 malformed이면 dry-run은 왜 seed가 불가능한지 안전하게 표시한다. live sync는 확정된 no-write policy를 위반하지 않는다.
3. QC는 portfolio-hub canonical payload와 legacy fallback payload를 각각 주입해 adapter→reconcile 경로를 검증한다. 정상 canonical payload가 `None`으로 degrade하지 않음을 증명한다.
# 4. 이번에 정의할 변경
- 변경 1 — canonical response parsing: `get_cost_basis()`가 `cost_basis_per_unit`을 canonical source로 읽고 수치 검증을 수행한다.
- 변경 2 — compatibility policy: legacy `avg_cost`/`wac`/`cost_basis`의 유지 필요성·우선순위·허용 값(0/negative/null)을 REQ에서 명시한다. canonical field가 존재하지만 invalid한 경우 legacy fallback으로 조용히 마스킹하지 않는다.
- 변경 3 — safe sync behavior: broker qty>0 + avg_price unavailable의 dry-run diagnostics와 live write barrier를 명시한다. 기존 `cost_basis_port` 미연결/네트워크 실패의 generic graceful degrade와 canonical parsing defect를 구분한다.
- 변경 4 — evidence and baseline alignment: HTTP response contract, sync output semantics, `avg_price` source/absence policy를 relevant interface/runtime/operations baseline 문서와 동기화한다.
## 영향 범위
- 코드 영향:
- `trex_core/adapters/portfolio/hub_read.py`: canonical parser 및 typed/result error semantics.
- `trex_core/application/reconciliation.py`: avg_price unavailable 조건의 dry-run/live behavior 및 output.
- `tests/adapters/portfolio/test_hub_read.py`, `tests/application/test_sync.py`: response-contract 및 no-write regression.
- 문서 영향:
- `docs/interface-contract.md`, `docs/runtime-flows.md`, `docs/operations.md`, 신규 REQ/Batch/verification/release 문서.
- 서비스 영향:
- portfolio-hub는 existing GET response contract의 producer로 참조만 한다. API, schema, DB migration, auth policy 변경은 없다.
# 5. 요구사항 목록
## 기능 요구사항
- FR-1: trex-core cost-basis adapter는 successful response에서 `cost_basis_per_unit`을 canonical WAC field로 parse한다.
- FR-2: canonical response가 valid numeric WAC를 제공하면 `sync --dry-run`과 approved live sync candidate는 동일한 avg_price seed value를 산출한다.
- FR-3: canonical field missing/null/non-numeric/invalid, non-2xx, response-shape failure, transport failure는 서로 구분 가능한 sanitized reason으로 operator surface에 나타난다.
- FR-4: broker qty>0의 inactive slot onboarding에서 avg_price unavailable일 때 dry-run은 no-write preview와 reason을 제공하고, live path는 확정된 fail-closed/explicit-confirmation policy를 적용한다.
- FR-5: legacy aliases가 유지되면 canonical field보다 낮은 우선순위로만 사용하며, canonical invalid value를 legacy value로 은폐하지 않는다.
- FR-6: response contract와 reconcile behavior는 deterministic test fixtures로 고정하고 independent QC가 adapter, application, CLI/output 경로를 재실행한다.
## 비기능 요구사항
- NFR-1 (안전성): cost-basis unavailable 상태에서 broker qty/status만 write해 avg_price 없는 holding slot을 만드는 경로는 승인된 정책 없이 허용되지 않는다.
- NFR-2 (회귀): 정상 `cost_basis_per_unit`, legacy fallback(채택 시), no cost-basis port, port transport failure, current holding update, dry-run no-write가 기존 의도에 맞게 분리 검증된다.
- NFR-3 (관측성): sync output/log는 `seeded_avg_price`, source/absence reason, dry-run/live 여부를 비밀 없이 판별 가능하게 한다.
- NFR-4 (계약 안정성): portfolio-hub HTTP response producer를 변경하지 않으며, trex-core AMQP wire contract와 broker credential source behavior를 변경하지 않는다.
## 범위 경계
- In Scope:
- trex-core consumer의 canonical `cost_basis_per_unit` parsing, legacy compatibility policy, safe sync behavior, regression tests, baseline/runbook 정합.
- Out of Scope:
- portfolio-hub endpoint/schema/authorization/ledger WAC calculation 변경.
- fetched credential/token lifecycle 수정, broker balance/reconcile classification 수정(dcy-037 범위).
- live sync 실행, database direct edit, bulk onboarding, automatic trading/automatic remediation.
- avg_price 재계산 알고리즘 또는 broker WAC를 새 authoritative source로 도입하는 설계 변경.
# 6. 리스크/가정 목록
## 리스크
- R-1 (High): canonical parser만 고치고 WAC absence live-write policy를 두지 않으면, 다른 unavailable failure에서 avg_price 없는 holding write가 계속 가능하다.
- 완화: OQ-1을 Discovery confirmation 전에 결정하고, live no-write assertion을 tests/QC/release gate에 둔다.
- R-2 (Medium): `or`-chain parsing은 `0`/negative/invalid values 및 canonical-invalid+legacy-present response의 의미를 흐릴 수 있다.
- 완화: explicit presence/type/range validation 및 deterministic precedence tests를 REQ에 명시한다.
- R-3 (Medium): producer schema 변경 없이 consumer만 수정하므로 실제 cross-service drift가 재발할 수 있다.
- 완화: canonical fixture는 portfolio-hub schema/endpoint implementation에 근거하고, producer-owned contract reference와 consumer regression을 함께 기록한다.
- R-4 (High): P1 수정 전 live sync를 실행하면 avg_price null slot이 실제 DB에 기록될 수 있다.
- 완화: release 문서에 dry-run → QC → explicit release approval → single-target live execution 순서를 강제한다.
## 가정
- A-1: portfolio-hub response의 `cost_basis_per_unit`은 `HoldingsCostBasisResponse` 및 endpoint implementation의 현재 canonical public field다.
- A-2: broker qty>0 onboarding에서 avg_price는 downstream 전략/손익 판단에 필요한 safety-relevant slot data다.
- A-3: current `sync --dry-run`은 DB write를 하지 않으며, actual live execution은 별도 release gate에서만 허용된다.
## 오픈 질문
- OQ-1: `broker_qty > 0`이고 avg_price/WAC가 unavailable일 때 live sync 정책은 무엇인가?
- 상태: Answered (2026-07-16, Justin Ko)
- 결정: **(a) fail-closed** — status/qty/avg_price를 모두 write하지 않고 non-zero failure로 중단한다. dry-run은 reason과 no-write preview를 제공하되, 평균가 없는 inactive→holding 전이는 차단한다.
- 종료 근거: Justin Ko가 팀장 권고인 fail-closed 정책을 명시 승인했다.
- OQ-2: legacy `avg_cost`/`wac`/`cost_basis` aliases를 지원할 실제 producer가 현재 존재하는가?
- 상태: Answered (2026-07-16, in REQ-123)
- 결정: producer/fixture의 canonical은 `cost_basis_per_unit` 기반이며, 호환성 유지를 위해 `avg_cost` → `wac` → `cost_basis` fallback은 유지한다. 단, canonical key가 유효하지 않으면 legacy fallback을 허용하지 않는다.
- OQ-3: response unavailability reason을 API adapter result type으로 확장할지, existing Optional port를 유지하고 reconcile-local diagnostics를 추가할지?
- 상태: Answered (2026-07-16, in REQ-123)
- 결정: `Optional[float]` port를 유지하고, unavailable는 reconcile-local diagnostics(`seeded_avg_price`, source, reason)로 분리 표시한다.
# 7. 초기 성공 기준
- S-1: canonical fixture `{current_quantity: 5.0, cost_basis_per_unit: 124580.0}`가 adapter에서 `124580.0`으로 parse된다.
- S-2: inactive slot + broker qty=5 + canonical WAC=124580 dry-run이 `after.status=holding`, `after.qty=5`, `after.avg_price=124580`, `seeded_avg_price=true`를 반환하며 slot/warning write가 없다.
- S-3: unavailable/malformed WAC fixture가 source reason을 남기고, OQ-1에서 확정한 live no-write/override behavior를 만족한다.
- S-4: canonical field가 invalid일 때 legacy alias가 있더라도 silent masking하지 않는 precedence regression이 통과한다.
- S-5: existing no-port/transport-failure graceful-degrade behavior, holding update, dry-run no-write and relevant core suite pass evidence가 있다.
## 측정 방식 및 데이터 출처
- S-1: `tests/adapters/portfolio/test_hub_read.py`에서 HTTP response fixture → adapter return assertion.
- S-2/S-3: `tests/application/test_sync.py`에서 in-memory slot/warning store, broker qty, fake cost-basis port를 이용한 dry-run/live no-write assertions.
- S-4: adapter contract test에서 canonical/legacy presence matrix를 명시한다.
- S-5: targeted adapter/application suite 및 affected CLI/output tests, independent QC rerun.
# 8. REQ로 넘기기 전 확인 체크
- [x] producer canonical field 및 consumer mismatch의 source evidence 확인
- 근거: portfolio-hub `HoldingsCostBasisResponse.cost_basis_per_unit`; trex-core `get_cost_basis()` legacy-only parser; synthetic canonical response 재현.
- [x] P1 scope 및 non-goals 초안화
- 근거: producer API 변경, credential/token scope, live execution, DB direct edit를 명시적으로 제외했다.
- [x] 성공 기준의 test seam과 evidence source 정의
- 근거: adapter response fixture, sync application fixture, independent QC rerun을 S-1~S-5에 연결했다.
- [x] OQ-1 live sync WAC-unavailable policy 확정
- 근거: Justin Ko가 2026-07-16 fail-closed 정책을 승인했다. dry-run은 no-write preview, live는 non-zero failure 및 status/qty/avg_price 무기록이다.
- [x] OQ-2/OQ-3: producer canonical 우선순위와 compatibility strategy, unavailability diagnostics는 REQ-123에서 확정했다.
- [x] Freeze 확정 담당자 지정
- 근거: 승인자 Justin Ko가 2026-07-16 Discovery scope와 OQ-1 fail-closed 정책을 confirm했다.
# 9. 파일 처리 결과
- 처리 결과: 생성
- 생성 경로: `docs/discovery/dcy-038_20260716_cost-basis-response-contract-and-safe-sync-seeding.md`
- 참조 문서:
- `docs/discovery/dcy-034_20260625_slot-account-blocking-reconcile-sync-coldstart.md`
- `docs/discovery/dcy-037_20260714_operator-broker-truth-and-managed-slot-divergence-hotfix.md`
- `trex_core/adapters/portfolio/hub_read.py:get_cost_basis`
- `trex_core/application/reconciliation.py:sync_slot_to_broker`
- `tests/adapters/portfolio/test_hub_read.py`, `tests/application/test_sync.py`
- portfolio-hub `app/schemas/v1.py:HoldingsCostBasisResponse`, `app/api/v1_portfolios.py:get_holdings_cost_basis`
# 10. 사용자 결정 필요 항목 요약
## DECIDE
- OQ-1: WAC가 unavailable인 양수 broker holding의 live sync는 **fail-closed**로 차단한다.
- 승인자: **Justin Ko**, 2026-07-16 KST.
## CONFIRM
- 본 P1을 trex-core consumer hotfix로 채택한다. portfolio-hub producer API는 변경하지 않는다.
- 수정·independent QC·dry-run 성공 증적·별도 release approval 전까지 live sync는 실행하지 않는다.
## DATA
- 실제 운영 대상의 portfolio/slot/account/credential 값은 Discovery/REQ/test fixture에 기록하지 않는다. release gate에서 필요한 대상은 별도 안전한 운영 증적으로만 취급한다.
# 11. Discovery Freeze
## 섹션 참조
- 범위: [# 5. 요구사항 목록 > 범위 경계](#5-요구사항-목록)
- 리스크/오픈 질문: [# 6. 리스크/가정 목록](#6-리스크가정-목록)
- 성공 기준: [# 7. 초기 성공 기준](#7-초기-성공-기준)
- 인계 전 확인: [# 8. REQ로 넘기기 전 확인 체크](#8-req로-넘기기-전-확인-체크)
- 사용자 결정 필요: [# 10. 사용자 결정 필요 항목 요약](#10-사용자-결정-필요-항목-요약)
## Freeze 플래그
- Handoff Decision: 승인 — REQ 인계
- Handoff Rationale: canonical field mismatch와 P1 scope를 evidence로 확정했고, Justin Ko가 avg_price unavailable 상태의 live sync를 fail-closed로 차단하는 안전 정책을 승인했다. 이 승인은 REQ drafting을 허용하지만 live sync 실행·배포·DB 변경 승인을 의미하지 않는다.
- Ready for REQ Drafting: true
- Confirmed By: Justin Ko
- Confirmed At (KST): 2026-07-16 22:44:20 KST
@@ -1,37 +0,0 @@
# dcy-039: intent venue end-to-end propagation
- Status: confirmed
- Date: 2026-07-27
- Owner: trex-core-dev
## Problem
`OrderRequest`와 KIS adapter는 KRX/NXT venue를 지원하지만 전략 intent의
`venue`가 슬롯에 저장되지 않아 execution이 항상 기본 KRX로 주문한다.
08:00 NXT 전략은 이 단절 때문에 계정 연결 준비 상태가 아니다.
## Decision
buy/sell wire에 선택적 `venue`를 추가하고 기본값은 KRX로 유지한다. 허용값은
KRX/NXT/SOR다. admission에서 검증한 venue를 pending 슬롯에 저장해 주문·취소에
전달하고, INACTIVE 복귀 시 KRX로 초기화한다.
## Scope
- intent DTO와 RabbitMQ handler
- slot 필드·PostgreSQL migration
- execution submit/cancel 전달
- 기존 KRX 회귀와 NXT 왕복 테스트
실계좌 연결, 실주문, 배포, 기존 runtime 변경은 제외한다.
## Evidence
- 신규 venue 흐름 테스트 4건
- 전체 회귀 `837 passed, 47 skipped`
- 별도 worktree `codex/top50-venue-aware-intents`
## Follow-up
- `dcy-041_20260729_broker-uncertainty-and-adapter-retirement`: `AUTO` 실제 거래소
확정, 취소 결과 상태, 짧은 계좌 스냅샷 복구.
@@ -1,145 +0,0 @@
# 0. 문서 상태
- 상태: confirmed
- 문서 ID: dcy-041_20260729_broker-uncertainty-and-adapter-retirement
- 이슈명: Core 브로커 불확실 상태 구분과 로컬 KIS 어댑터 축소
- 작성 시각(KST): 2026-07-29 16:59:16 KST
- 마지막 갱신 시각(KST): 2026-07-29 17:54:02 KST
- 대체됨: 없음
- 선행 Discovery:
- dcy-013_20260608_kis-external-cancel-pending-sell-stuck
- dcy-022_20260610_stuck-pending-order-recovery
- dcy-039_20260727_intent-venue-end-to-end-propagation
- 후속 Discovery 참조: 없음
- GitHub 이슈: #44, #45
# 1. 계획 상태 요약
- 해석:
- Core에는 intent 거래소 전달, 외부 취소 감지, 결과불명 주문 복구가 일부 구현돼 있다.
- 남은 공용 공백은 `AUTO` 거래소의 실제 제출 거래소 확정, 취소 요청 결과의 명시적 상태, 짧은 계좌 스냅샷 복구다.
- 목표:
- 공용 기능을 Core가 소유하고 로컬 KIS 호환 어댑터는 KIS 중계만 남도록 축소한다.
- 호출 실패를 실제 불일치로 오판하거나 결과불명 주문을 재전송하지 않는다.
# 2. 요구사항 판정 결과
- 판정: 신규 운영 후속 Discovery
- 근거:
- 선행 Discovery들은 이미 확정·구현됐지만 2026-07-28~29 실전 운영에서 취소 응답과 계좌조회 불확실 상태가 다시 확인됐다.
- 이번 변경은 기존 기능의 문구 보강이 아니라 Core와 외부 호환 어댑터의 책임 경계를 바꾸므로 독립 추적한다.
# 3. 문제점의 요약
## 현재 상태
- `dcy-039`는 intent의 `KRX|NXT|SOR` 거래소를 슬롯과 주문·취소까지 보존한다.
- 전략이 거래소를 보내지 않으면 기본 KRX이므로, 전략을 바꾸지 않고 08:00 NXT 주문을 보내는 `AUTO` 계약은 없다.
- `BrokerPort.cancel_order()`는 참/거짓만 반환하므로 요청 접수, 명시적 거부, 응답유실을 구분하지 못한다.
- KIS `get_balance()`는 호출 실패를 검증된 빈 잔고와 구분해 예외로 올리지만, 마지막 정상 계좌 스냅샷의 출처·시각·만료 정책은 Core에 없다.
- 로컬 `nxt_adapter`가 주문번호-거래소와 짧은 잔고 스냅샷을 별도 SQLite에 저장한다.
## 기대 상태
- `AUTO` 주문은 KIS 브로커 경계에서 KST 세션에 따라 실제 거래소를 확정하고 슬롯·취소·Core 원장에 보존한다.
- 취소 요청 결과는 `요청 접수`, `명시적 거부`, `결과불명`으로 구분하고 최종 취소·체결은 기존 체결조회로 확정한다.
- 마지막 정상 계좌 스냅샷은 계정 범위와 캡처 시각을 포함해 원자적으로 저장하고 짧은 유효시간 안에서만 사용한다.
- 신규매수와 위험축소 매도의 허용시간을 분리하고 결과불명 주문은 종목·방향 범위에서 재전송을 차단한다.
# 4. 이번에 정의할 변경
1. `AUTO` 거래소를 실제 KRX/NXT로 확정하고 재시작 후에도 원 주문 거래소를 복구한다.
2. 취소 API 결과를 명시적 상태값으로 바꾸고 응답유실 시 재취소·재주문을 자동 반복하지 않는다.
3. 계정별 마지막 정상 잔고 스냅샷과 방향별 만료 정책을 Core/KIS 브로커 계층에 둔다.
# 5. 요구사항 목록
## 기능 요구사항
- FR-1: `AUTO|KRX|NXT|SOR`를 입력받고 `AUTO`는 KST 세션에 따라 실제 KRX/NXT로 결정한다. 결정 불가능 시간은 KRX로 대체하지 않고 거부한다.
- FR-2: 실제 제출 거래소를 슬롯과 Core 체결 전달 대기 원장에 저장하며 취소·재시작 복구에서 재사용한다.
- FR-3: 취소 요청은 `requested|rejected|unknown` 결과를 반환한다. `requested`는 최종 취소가 아니며 체결조회가 `cancelled|filled`를 확정한다.
- FR-4: `unknown` 취소 결과와 결과불명 주문은 자동 재주문하지 않고 기존 주문 조회로만 수렴한다.
- FR-5: 성공한 계좌조회 영역만 계정 해시·독립 캡처 시각과 함께 원자 저장한다. 보유수량과 주문가능금액 중 한쪽 실패가 다른 정상값을 덮지 않는다.
- FR-6: 일시적 전송 오류에만 미만료 스냅샷을 사용한다. 계정 불일치·만료·응답 오류는 차단한다.
- FR-7: 신규매수와 위험축소 매도의 스냅샷 최대 나이를 분리하며 KIS 복구 후 다음 백그라운드 조회가 정상값으로 교체한다.
## 비기능 요구사항
- NFR-1: 동일 intent와 동일 결과불명 주문의 중복 제출은 0건이어야 한다.
- NFR-2: 기존 KRX 명시 주문, PaperBroker, 전략 wire의 미지정 필드 호환성을 보존한다.
- NFR-3: 토큰·키·계좌번호·원본 KIS 응답은 로그·문서·테스트 산출물에 남기지 않는다.
- NFR-4: DB 마이그레이션은 멱등이고 기존 슬롯·체결 전달 대기 원장을 보존한다.
## 범위 경계
- In Scope:
- TREX Core의 KIS 브로커, 주문·취소 실행, 슬롯·계좌 스냅샷 영속화, 회귀·장애·재시작 테스트.
- Out of Scope:
- Portfolio Hub API·스키마 변경, 전략 자금배분, 계정관리 대시보드, 토큰 갱신, NXT 분봉 수집, 수동 보유수량 분리.
# 6. 리스크/가정 목록
- R-1: 취소 요청 접수를 취소 확정으로 처리하면 체결 경쟁에서 중복매수가 발생할 수 있다.
- 완화: 슬롯은 pending을 유지하고 체결조회만 최종 상태를 전이한다.
- R-2: 오래된 계좌 스냅샷이 신규매수를 허용하면 주문가능금액을 초과할 수 있다.
- 완화: 신규매수 유효시간을 짧게 두고 수량·금액 한도를 기존 전략 intent 이하로 제한한다.
- R-3: `AUTO` 실제 거래소가 영속화되지 않으면 재시작 후 취소가 잘못된 거래소로 갈 수 있다.
- 완화: 브로커 응답 직후 슬롯 저장을 주문 접수의 완료 조건으로 둔다.
- A-1: KST 세션은 NXT 08:00~08:49:59, KRX 09:00:30~15:29:59, NXT 15:30~20:00으로 운영한다.
- A-2: 미확인주문 상태는 기존 슬롯의 broker_order_id와 결과불명 제출 메타데이터가 정본이다.
- 오픈 질문: 없음. 사용자가 2026-07-29 핵심 3개 범위와 순차 구현을 승인했다.
# 7. 초기 성공 기준
- S-1: 거래소 미지정 전략 intent가 08:00 KST에 NXT로 제출되고 재시작 후 NXT로 취소된다.
- S-2: 취소 응답유실은 `unknown`으로 남고 같은 주문의 자동 재취소·재매수가 발생하지 않는다.
- S-3: KIS 계좌조회 전송 실패에서 미만료 동일계정 스냅샷만 사용하며 신규매수·매도 유효시간이 각각 적용된다.
- S-4: 스냅샷 만료 후 신규매수는 차단되고 보유분 매도·체결조회·원장복구는 독립적으로 검증된다.
- S-5: 단위→회귀→가상계약→가상종단간→오류주입→재시작→원장대조가 모두 통과한다.
# 8. REQ로 넘기기 전 확인 체크
- [x] 기존 Core와 로컬 어댑터의 중복 책임을 코드로 확인했다.
- [x] GitHub #44·#45의 완료 기준과 범위를 확인했다.
- [x] 실전 반영 전 가상 검증과 원복 게이트를 고정했다.
- [x] 사용자 결정이 필요한 오픈 질문이 없다.
# 9. 파일 처리 결과
- 처리 결과: 생성
- 생성 경로: `docs/discovery/dcy-041_20260729_broker-uncertainty-and-adapter-retirement.md`
- 참조:
- `trex_core/application/broker_port.py`
- `trex_core/application/execution.py`
- `trex_core/adapters/broker/kis.py`
- `trex_core/application/snapshot_scheduler.py`
- `docker-local/KIS_ADAPTER_REMOVAL_CONTRACT.md`
# 10. 사용자 결정 필요 항목 요약
- 없음. 사용자가 핵심 범위, 기능별 브랜치, Docker 선검증, 서버 차이 검증, 실전 적용 후 어댑터 축소 순서를 승인했다.
# 11. Discovery Freeze
- Handoff Decision: 승인
- Handoff Rationale: Core의 기존 거래소 전달·결과불명 주문 처리와 남은 취소·계좌조회 공백을 재확인했고, 사용자 승인 범위와 충돌하는 오픈 질문이 없다.
- Ready for REQ Drafting: true
- Confirmed By: 형님
- Confirmed At (KST): 2026-07-29 17:01:50 KST
# 12. 구현 증거
- `AUTO` 실제 거래소 확정·주문번호 동시 저장·재시작/취소 재사용: 구현 완료.
- 취소 `requested|rejected|unknown` 영속화와 poll 전용 최종확정: 구현 완료.
- 계정 해시·독립 TTL·백그라운드 단일 소유자·매수/매도 분리: 구현 완료.
- 계정 해시는 KIS endpoint·계좌·상품·app identity를 함께 묶어 실전/모의·자격증명 출처를 분리한다.
- 동일 instance advisory lock 안에서 현금 예약검사와 pending 전이를 원자 처리한다.
- timeout·운영자 취소 모두 compare-and-set 예약으로 동일 주문 재취소를 차단한다.
- KIS 현금 권위 필드: `inquire-psbl-order.output.nrcvb_buy_amt`.
- 수동 혼합 보유 계정: `OwnershipAwareBroker` 투영 뒤 스냅샷을 생성하여 전략 소유 수량만 매도 허용.
- 운영 기본값: 기능 OFF, 매수 600초, 매도 3600초, 갱신 300초.
- 검증: 로컬 903 passed, 격리 Docker PostgreSQL/RabbitMQ 953 passed,
공용 가상 인증 7단계 PASS.
- 검증 문서: `docs/validation/dcy-041-core-adapter-gap-closure-20260729.md`
- 실전 반영: 가상 검증과 사용자 별도 승인 전까지 미적용.
@@ -1,172 +0,0 @@
# 0. 문서 상태
- 상태: confirmed
- 문서 ID: dcy-042_20260729_shared-account-capital-allocation-policy
- 이슈명: 메가 스윙과 탑50 스윙 공통 계좌 자본배분 정책
- 작성 시각(KST): 2026-07-29 19:43:47 KST
- 마지막 갱신 시각(KST): 2026-07-29 19:47:39 KST (REQ-127 생성·승인)
- 대체됨: 없음
- 선행 Discovery: dcy-021_20260610_position-sizing-target-intent
- 후속 Discovery 참조: 없음
- 생성된 REQ 참조: `docs/srs/Interface/req-127_shared-account-capital-allocation-policy.md`
# 1. 계획 상태 요약
- 연계 문서 현황:
- `dcy-021`은 전략이 최종 정수 주문수량과 가격을 계산하고 Core는 실행하는 계약을 확정했다.
- 로컬 KIS 어댑터에는 메가 스윙과 탑50 스윙이 공통으로 쓸 수 있는 계좌 현금 배분 계산이 존재한다.
- 해석:
- 종목 가격 때문에 균등 슬롯 금액으로 한 주도 살 수 없는 후보는 제외하고, 살 수 있는 후보끼리 실제 현금을 넘지 않게 유동 배분해야 한다.
- 이 결정적 계산은 Core 도메인이 소유하고, KIS 어댑터는 현금·호가 조회와 설정 반영만 담당하는 편이 재사용과 검증에 적합하다.
# 2. 요구사항 판정 결과
- 판정: 새 Discovery 생성
- 근거:
- `dcy-021`은 주문수량 계약과 실행 책임을 다루며, 계좌 후보군 전체의 현금 배분 정책은 명시적으로 범위 밖이었다.
- 이번 변경은 메가 스윙과 탑50 스윙의 공통 정책 소유권을 Core로 옮기는 독립 후속 반복이다.
# 3. 문제점의 요약
## 현재 상태
- Core에는 주문·체결·슬롯 상태는 있으나, 후보 가격과 슬롯 수를 받아 계좌 현금을 안전하게 나누는 순수 도메인 정책이 없다.
- 동일한 배분 공식이 로컬 KIS 어댑터에만 있어 다른 전략이 재사용하려면 어댑터 구현에 의존하거나 공식을 복제해야 한다.
- Portfolio Hub는 현금·보유·손익 원장의 정본이며, 기존 `BuyIntent`는 전략이 계산한 정수 수량과 가격을 그대로 전달한다.
## 기대 상태
- Core가 네트워크·브로커·전략에 의존하지 않는 결정적 자본배분 정책을 제공한다.
- 전략은 후보군과 슬롯 수를 정하고, 어댑터는 계좌 현금과 보호 호가를 입력한 뒤 Core 결과를 기존 주문수량 계산에 사용한다.
- Portfolio Hub 원장과 기존 주문 의도 계약은 변경하지 않는다.
## 이해관계자 및 제약
- 운영자: 메가 스윙과 탑50 스윙을 같은 계좌 배분 원칙으로 운영한다.
- Core 개발자: 공통 순수 정책과 불변식의 유지보수 주체다.
- 전략·KIS 어댑터 개발자: 외부 입출력과 전략별 후보·슬롯 연결을 유지한다.
- 실전 런타임·주문 게이트·계좌 상태는 본 반복에서 변경하지 않는다.
- 토큰·키·계좌번호·원본 브로커 응답은 코드·문서·검증 산출물에 남기지 않는다.
## 주요 사용자 시나리오
1. 메가 스윙 계좌의 절반으로 고가 종목 한 주를 살 수 없으면 해당 종목을 제외하고 저가 종목에 가용 현금을 배정한다.
2. 입금으로 고가 종목 한 주를 포함할 여력이 생기면 한 주 가격만큼 유동적으로 양보해 두 종목을 함께 운영한다.
3. 탑50 스윙 3슬롯에서 5개 후보 중 한 주도 살 수 없는 고가 후보를 제외하고, 가능한 후보의 최악 3종 조합도 현금을 넘지 않게 배정한다.
# 4. 이번에 정의할 변경
1. Core에 보호된 한 주 가격과 슬롯 수를 입력받는 순수 자본배분 정책을 추가한다.
2. 적격·제외 사유와 종목별 최대 배정액을 결정적으로 반환하고, 어떤 활성 슬롯 조합도 실제 현금을 넘지 않는 불변식을 둔다.
3. KIS 어댑터의 중복 공식을 제거하고 현금·호가 입력과 Core 결과 매핑만 남긴다.
## 영향 범위
- 코드 영향:
- `trex_core/domain/capital_allocation.py`
- `tests/domain/test_capital_allocation.py`
- 로컬 KIS 어댑터의 호환 연결부
- 문서 영향:
- `docs/project-structure.md`
- 신규 요구사항·배치·검증 문서
- 운영 영향:
- 이번 브랜치에서는 실전 배포와 주문 활성화가 없다.
# 5. 요구사항 목록
## 기능 요구사항
- FR-1: 보호된 한 주 가격은 현재가와 실행 여유율을 반영해 원 단위 올림으로 계산한다.
- FR-2: 기본 슬롯 균등액보다 비싼 후보도 가장 싼 후보 한 주 가격 범위의 유동 여유 안이면 적격으로 유지한다.
- FR-3: 적격 후보 중 최악의 활성 슬롯 조합이 현금을 넘으면 가장 비싼 후보부터 결정적으로 제외한다.
- FR-4: 남은 후보에는 한 주 이상 살 수 있으면서 최악의 활성 슬롯 조합 합계가 가용 현금을 넘지 않는 최대 공통 배정액을 준다.
- FR-5: 결과는 후보별 배정액과 `eligible`, `no_available_cash`, `protected_share_above_flex_limit`, `worst_case_cash_overcommit` 사유를 제공한다.
## 비기능 요구사항
- NFR-1: 정책은 네트워크, 환경변수, 저장소, 브로커 및 전략 코드에 의존하지 않는 순수 함수여야 한다.
- NFR-2: 입력 순서가 같은 경우 결과가 항상 같고, 중복 식별자·음수 현금·잘못된 가격은 명시적으로 거부해야 한다.
- NFR-3: 기존 로컬 공식과 대량 무작위 입력에서 완전 일치하고, 메가·탑50 가상 검증 결과가 바뀌지 않아야 한다.
## 범위 경계
- In Scope:
- 공통 배분 계산과 입력 검증, 제외 사유, 불변식 검증
- 로컬 어댑터의 얇은 연결부와 메가·탑50 가상 회귀
- Out of Scope:
- Core가 브로커 현금을 직접 조회하거나 전략 후보를 선택하는 기능
- `BuyIntent` 수량·가격 계약, Portfolio Hub 원장, 슬롯 상태머신 변경
- 자동 주문수량 재조정 스케줄러, 실전 배포, 주문 게이트 변경
# 6. 리스크/가정 목록
## 리스크
- R-1 (High): Core가 현금·호가 조회까지 소유하면 브로커와 Portfolio Hub 경계가 섞인다.
- 완화: Core에는 순수 계산만 두고 외부 조회는 어댑터에 남긴다.
- R-2 (High): 기존 공식과 작은 반올림 차이가 실제 주문 가능 종목을 바꿀 수 있다.
- 완화: 기존 공식과 50,000개 무작위 입력 완전 일치 및 대표 경계값 회귀를 통과시킨다.
- R-3 (Medium): 적격 후보별 배정액의 합만 비교하면 실제로 동시에 열릴 수 있는 최악 조합을 놓칠 수 있다.
- 완화: 슬롯 수만큼의 가장 큰 양수 배정액 합이 현금 이하인지 정책 내부와 속성 테스트에서 검증한다.
## 가정
- A-1: 슬롯 수와 후보군은 전략·계정 설정이 제공한다.
- A-2: 가용 현금과 현재가는 KIS 어댑터 또는 동등한 외부 경계가 최신 값을 제공한다.
- A-3: 최종 정수 주문수량과 가격을 담은 주문 의도는 기존 `dcy-021` 계약대로 전략 조립 계층이 만든다.
## 오픈 질문
- 없음. 사용자가 공통 배분을 Core에 두고 가상 검증 후 Core 브랜치로 올리는 방향을 승인했다.
# 7. 초기 성공 기준
- S-1: 메가 스윙 200만원·300만원 대표 사례와 탑50 3슬롯 고가종목 제외 사례가 기대 배분을 반환한다.
- S-2: 10,000개 무작위 입력에서 최악 활성 슬롯 배정 합이 가용 현금을 초과하지 않는다.
- S-3: 기존 어댑터 공식과 50,000개 무작위 입력의 결과가 완전히 일치한다.
- S-4: Core 전체 회귀, 어댑터 전체 회귀, 탑50 월간 가상 원장 비교가 모두 통과한다.
## 측정 방식 및 데이터 출처
- S-1/S-2: `tests/domain/test_capital_allocation.py`
- S-3: 기존 어댑터와 Core 정책의 고정 시드 비교 스크립트 출력
- S-4: Core·docker-local 테스트 결과와 탑50 월간 가상거래 요약·거래원장·배분결정 파일
# 8. REQ로 넘기기 전 확인 체크
- [x] 범위와 책임 경계를 사용자 승인 및 기존 Core 계약으로 확정했다.
- [x] 성공 기준의 측정 방식과 데이터 출처를 지정했다.
- [x] High 리스크는 순수 도메인 경계와 완전 일치 회귀로 해소한다.
- [x] `Open` 상태의 질문이 없다.
- [x] Freeze 확정 담당자는 본 방향을 승인한 사용자다.
# 9. 파일 처리 결과
- 처리 결과: 생성
- 생성 경로: `docs/discovery/dcy-042_20260729_shared-account-capital-allocation-policy.md`
- 참조 문서:
- `docs/discovery/dcy-021_20260610_position-sizing-target-intent.md`
- `docs/srs/Interface/req-061_asset-keyed-intent-contract.md`
- `docs/project-structure.md`
# 10. 사용자 결정 필요 항목 요약
- 없음. 공통 정책의 Core 소유, 어댑터의 외부 입출력 소유, 가상 검증 후 Core Draft PR 순서를 사용자가 승인했다.
# 11. Discovery Freeze
## 섹션 참조
- 범위: [# 5. 요구사항 목록 > 범위 경계](#5-요구사항-목록)
- 리스크/오픈 질문: [# 6. 리스크/가정 목록](#6-리스크가정-목록)
- 성공 기준: [# 7. 초기 성공 기준](#7-초기-성공-기준)
- 인계 전 확인: [# 8. REQ로 넘기기 전 확인 체크](#8-req로-넘기기-전-확인-체크)
- 사용자 결정 필요: [# 10. 사용자 결정 필요 항목 요약](#10-사용자-결정-필요-항목-요약)
## Freeze 플래그
- Handoff Decision: 승인
- Handoff Rationale: 기존 주문수량 계약과 새 순수 정책의 책임 경계를 코드·문서로 재확인했고, 사용자 승인 범위와 충돌하는 오픈 질문이 없다.
- Ready for REQ Drafting: true
- Confirmed By: 형님
- Confirmed At (KST): 2026-07-29 19:45:57 KST
@@ -1,54 +0,0 @@
# REL-011 운영 실행 증적 및 `confirm-release` 기록
- Release: `REL-011`
- Batch: `BAT-057` (`REQ-119`–`REQ-122`)
- Service: `trex-core` — `trex-admin-daily-trading`, `genichin-longterm` daemon instances
- Evidence received: 2026-07-17 KST, `trex-op` reconstructed release-specific execution handoff
- Evidence sources: immutable git reflog, Supervisor/runtime logs, and secret-free Mattermost terminal capture `20260716_160613_e1fc5e64` (messages `32666`, `32668`, `32670`, `32725`, `32746`–`32758`)
- Approval: Justin Ko — Mattermost message `32675`, `2026-07-16 17:22:50 KST`
- Release decision: **Released** — lead `confirm-release` after reviewing this record.
- Secret policy: no credentials, tokens, account identifiers, DSNs, raw broker responses, raw payloads, or full config values are recorded.
## Immutable release identity
| Role | Revision | Evidence |
| --- | --- | --- |
| Deployed target | [`6d90f895`](https://github.com/fourblocks2020/trex-core/commit/6d90f8951dbf354eb41301492d0bcaf0af24d882) | requested target; target object verified and reachable from `origin/main` |
| Runtime implementation | [`94e14894`](https://github.com/fourblocks2020/trex-core/commit/94e148940adb008db0bbd215052a1e984f7c566b) | parent merge for runtime implementation |
| Release confirmation record | this commit | records operation evidence and lead decision |
## Approval, preflight, and activation
- The approved-instance preflight record confirms that the existing configuration was retained and that `credential_fetch_enabled` / break-glass semantic state was unchanged.
- Approval was recorded by Justin Ko in Mattermost message `32675` at `2026-07-16 17:22:50 KST`.
- The actual pre-deploy rollback revision was captured as `7845fbe856e125e712ac481e36ac582c1a2fe54b`.
- Reflog correlates checkout from `main` to target `6d90f895…` at `2026-07-16 17:36:42 +0900`.
- Supervisor records both approved daemon instances stopping cleanly, spawning as PIDs `2014346` and `2014347`, and reaching `RUNNING` at `17:36:47.316 +0900`.
- Startup runtime interval contains `ERROR=0`, `CRITICAL=0`, and `Traceback=0`; AMQP connection establishment succeeded.
- No DB migration, dependency change, AMQP topology change, configuration activation, or live mutation was in release scope.
## Read-only smoke evidence
Secret-free terminal capture window: `2026-07-16 17:40:09.567`–`17:40:14.377 +0900`.
| Approved instance | `sync --all --dry-run` | `verify` | Mutation boundary |
| --- | --- | --- | --- |
| `trex-admin-daily-trading` | exit `0`; `146` A-case-guard skips | exit `0`; `slot_not_found`, count `1` | no write flags or mutation markers |
| `genichin-longterm` | exit `0`; result count `1`, `dry_run=true`, `skipped=false` | exit `0`; `cleared`, count `1` | no write flags or mutation markers |
No credential/broker-balance error or CLI failure was recorded. `sync --live`, `--yes`, live order, automatic remediation, and manual DB changes were not executed.
## Rollback disposition
- Pre-deploy `ROLLBACK_REVISION`: `7845fbe856e125e712ac481e36ac582c1a2fe54b`.
- Rollback was **not performed**: no stop condition, startup failure, mutation risk, or smoke failure was recorded.
## `confirm-release` decision
**Released.** The committed runbook’s completion criteria are satisfied by the explicit approval reference, immutable target identity, captured rollback revision, controlled restart, startup health, both read-only smoke results, and a recorded no-rollback disposition. The production live-sync gate remains separate and was not opened by REL-011.
## Source records
- [REL-011 release document](../rel-011_20260716_m5_operator-broker-truth-hotfix.md)
- [REL-011 deployment runbook](../../runbooks/rel-011-m5-operator-broker-truth-hotfix-deployment.md)
- operator handoff: `rel-011-execution-evidence-20260717-c3j4ujma3tdnzg5ogiztdbum6r.md` (received 2026-07-17 KST)
@@ -1,79 +0,0 @@
# REL-012 운영 실행 증적 및 `confirm-release` 기록
- Release: `REL-012`
- Batch: `BAT-058` / `REQ-123`
- Service: `trex-core` — 기존 `trex-core` daemon 1개
- Evidence received: 2026-07-17 13:42:37 KST, `trex-op`의 M5 common evidence 보정본
- Canonical source payload: [`81a853a314391da7a8bc9df5517013c7fd296d61`](https://github.com/fourblocks2020/trex-core/commit/81a853a314391da7a8bc9df5517013c7fd296d61)
- Canonical release manifest: [`869e07fb34bd8a65d330da58a28cbcb1505b9625`](https://github.com/fourblocks2020/trex-core/commit/869e07fb34bd8a65d330da58a28cbcb1505b9625)
- Evidence status: **complete for REL-012 release scope**
> 이 문서는 운영팀이 반환한 secret-free 실행 증적을 REL-012 전용 canonical release record로 전사한 것이다. M5 공통 handoff는 supporting evidence이며, 이 문서가 REL-012의 release-specific primary record다. credential, token, account number, DSN, raw broker/portfolio response는 기록하지 않는다.
## 1. 승인 및 preflight
| Gate | Result | Recorded evidence |
| --- | --- | --- |
| Production execution approval | PASS | 사용자 명시 지시: `지금 실행` |
| Execution window | PASS | 승인 시각부터 완료 시까지의 off-market window |
| Initial checkout | PASS | detached `6d90f895...` |
| Release refresh | PASS | `deploy-trex-core.sh --update-only`; refreshed main `869e07f` |
| Pre-deploy / rollback revision | PASS | `ROLLBACK_REVISION = 869e07fb34bd8a65d330da58a28cbcb1505b9625` |
| Checkout cleanliness | PASS | clean |
| Payload publication | PASS | `81a853a` verified on origin feature ref before execution |
| Dependency / migration delta | PASS | none |
| Configuration / credential / break-glass change | PASS | none |
| Approved activation method | PASS | approved supervisor; program `trex-core:trex-admin-daily-trading` |
## 2. Daemon activation
| Check | Result | Recorded evidence |
| --- | --- | --- |
| Pinned payload checkout | PASS | `81a853a314391da7a8bc9df5517013c7fd296d61` |
| Existing daemon stop | PASS | old daemon stopped through approved supervisor |
| New daemon start | PASS | same approved configuration |
| Supervisor state | PASS | `RUNNING` |
| Startup log interval | PASS | error / critical / traceback count `0` |
| RabbitMQ / application connectivity | PASS | startup connection accepted |
| Rollback | Not required | no stop condition observed |
## 3. Read-only / dry-run acceptance
| Command / check | Result | Sanitized evidence |
| --- | --- | --- |
| `sync --all --dry-run --config trex-admin-daily-trading --json` | PASS | exit `0`; results `145`; skipped `145`; write indications `0`; classification `broker_qty_zero` |
| `verify --config trex-admin-daily-trading --json` | PASS | exit `0`; result count `0`; `no C-warned slots found` |
| Prohibited rollout actions | PASS | no `sync --live`, no `--yes` live write, no order action during REL-012 deployment scope |
The deployment evidence satisfies the REL-012 runbook's restart and read/dry-run-only acceptance contract. No DB migration, DB edit, dependency/configuration/credential change, AMQP topology change, or strategy daemon deployment was recorded.
## 4. Separate post-release operational action
After the REL-012 daemon rollout, a **separately user-approved** single-target remediation was performed for `genichin-longterm` / symbol `005930`. It is not deployment smoke and does not enlarge REL-012's deployment scope.
- Prior dry-run: projected `holding`, broker qty `5`, projected avg price `124580.0`, `seeded_avg_price=true`, no write.
- Separately authorized action: `sync --live --yes` for that single target; exit `0`.
- Readback: `holding`, qty `5.0`, avg price `124580.0`, no warning / reconcile warning; `verify` PASS.
- Rollback: not required.
## 5. `confirm-release` decision
- **Decision:** `Released`
- **Lead rationale:** immutable deployed payload and manifest are identified; approval, preflight, activation, startup observation, rollback disposition, and permitted read-only/dry-run smoke are complete and PASS. No release-scope stop condition was observed.
- **Released scope:** existing `trex-core` daemon only.
- **Excluded services / changes:** `portfolio-hub`, `kis-data-hub`, `trex-orb-v1`, `trex-tsl-v1`, strategy daemons, DB schema, credentials/configuration, and AMQP topology.
- **Remaining boundary:** any future `sync --live` remains independently authorized per target and is not a daemon rollout shortcut.
## 6. Feedback and train boundary
- The no-write dry-run and `verify` acceptance checks passed after restart; no new release blocker was observed.
- This record confirms **REL-012 only**. It does not reclassify the M5 release train as `Train Released`.
- `trex-core REL-011` still lacks its own release-specific execution evidence in the received M5 handoff and remains `draft`; therefore the M5 train remains **not fully released** pending a separate REL-011 evidence decision.
## 7. Evidence sources
- [REL-012 release draft](../rel-012_20260717_cost-basis-safe-sync-seed-hotfix.md)
- [REL-012 deployment runbook](../../runbooks/rel-012-cost-basis-safe-sync-seed-deployment.md)
- [REL-012 operations handoff](../../runbooks/rel-012-cost-basis-safe-sync-seed-ops-handoff.md)
- [BAT-058 verification](../../batches/bat-058_20260716_cost-basis-safe-sync-seed/verification.md)
- Operations supporting handoff: `M5-common-evidence-handoff-revised-20260717-134237-ozxbxkeaybgzpgzmgt3aqshcba.md` (received by lead, 2026-07-17)
@@ -1,84 +0,0 @@
# REL-012: cost-basis canonical parse 및 안전 sync seed 긴급 핫픽스
- Status: **Released**
- Profile: `app-service`
- Priority: **urgent**
- Owner: 팀장 / 운영팀
- Included batch: **BAT-058** (`REQ-123`)
- Source discovery: [`dcy-038`](../discovery/dcy-038_20260716_cost-basis-response-contract-and-safe-sync-seeding.md)
- Batch verification: [`BAT-058 verification`](../batches/bat-058_20260716_cost-basis-safe-sync-seed/verification.md) — lead `confirm-batch-verification`, QC-7 `PASS-WITH-NOTES`
- Operations handoff: [`REL-012 deployment request`](../runbooks/rel-012-cost-basis-safe-sync-seed-ops-handoff.md)
## Release intent
portfolio-hub의 기존 cost-basis 응답에서 canonical `cost_basis_per_unit`을 우선 해석하고, broker quantity가 양수인 inactive-slot onboarding에서 WAC/avg_price를 얻지 못하면 live sync가 slot status/qty/avg_price/warning을 쓰지 않은 채 non-zero로 fail-closed하도록 한다. dry-run은 sanitized source/reason과 no-write preview를 제공한다.
## Source publication boundary
| Item | Current evidence | Release constraint |
| --- | --- | --- |
| Remote `origin/main` | [`869e07f`](https://github.com/fourblocks2020/trex-core/commit/869e07fb34bd8a65d330da58a28cbcb1505b9625) | BAT-058 source and immutable REL/runbook manifest are included; this is the recorded release-manifest revision. |
| BAT-058 candidate | [`81a853a`](https://github.com/fourblocks2020/trex-core/commit/81a853a314391da7a8bc9df5517013c7fd296d61), reachable from `origin/main` | Reviewed code payload was deployed; operational evidence is recorded in the REL-012 execution record. |
| Immutable deploy payload | [`81a853a`](https://github.com/fourblocks2020/trex-core/commit/81a853a314391da7a8bc9df5517013c7fd296d61) | Immutable **code/test payload** checked out for the daemon rollout. |
| Authoritative release manifest | [`869e07f`](https://github.com/fourblocks2020/trex-core/commit/869e07fb34bd8a65d330da58a28cbcb1505b9625) | Immutable REL/runbook manifest recorded with the operational evidence. |
The immutable daemon deploy payload and the release manifest have distinct roles: `81a853a` is the verified code/test payload; `869e07f` is the immutable release/runbook manifest. Operations recorded both revisions in the [REL-012 execution evidence](evidence/rel-012_20260717_execution-evidence.md). The pre-execution authorization boundary is historical: the approved daemon restart and read/dry-run smoke completed without a release-scope stop condition. `sync --live` remains a separately authorized per-target operational action and is not deployment smoke.
## Included scope
1. `cost_basis_per_unit` canonical priority parsing and approved legacy compatibility chain (`avg_cost` → `wac` → `cost_basis`) only when canonical is absent.
2. Canonical invalid/null/non-numeric/zero/negative values are not silently masked by a legacy value; diagnostic reasons remain sanitized.
3. Inactive slot + positive broker quantity + unavailable WAC: dry-run no-write preview; live non-zero fail-closed with no slot/warning mutation.
4. Existing HOLDING reconciliation remains compatible; sanitized CLI/operator diagnostics preserve `seeded_avg_price`, source, and absence reason.
## Explicit exclusions / no-go
- portfolio-hub API/schema/auth changes, token/config changes, dependency upgrade, DB migration, direct DB edit/delete, AMQP wire/topology change.
- Any `sync --live`, `--yes` live write, automatic remediation, order action (`enter`/`exit`/`cancel`), or target portfolio selection.
- Changing `credential_fetch_enabled`, break-glass mode, instance configuration, endpoint, or secrets.
- Strategy daemon deployment.
A single-target `sync --live --yes` remains a separately approved post-release action even after this source rollout. It must not be used as a deployment smoke shortcut.
## Draft verification evidence
| Check | Result | Evidence / limitation |
| --- | --- | --- |
| Independent QC-7 | **PASS-WITH-NOTES** | Legacy-invalid focused `8 passed, 19 deselected`; affected suite `113 passed`; full suite `833 passed, 47 skipped`; Ruff/compileall/diff hygiene pass. |
| Release-prep full regression | **PASS** | 2026-07-17 delivery worktree: `833 passed, 47 skipped in 1.88s`. |
| Focused Ruff | **PASS** | Changed adapter/reconcile/CLI and relevant tests: `All checks passed!`. |
| Focused compile check | **PASS** | Six changed source/test paths, `compileall -q`, exit `0`. |
| Live broker, live PostgreSQL, production deploy | **PASS** | Operations returned secret-free primary evidence: pinned daemon rollout, startup acceptance, and permitted dry-run / verify smoke all PASS; see [execution evidence](evidence/rel-012_20260717_execution-evidence.md). |
| Source publication | **PUBLISHED / DEPLOYED** | Immutable daemon payload [`81a853a`](https://github.com/fourblocks2020/trex-core/commit/81a853a314391da7a8bc9df5517013c7fd296d61) is reachable from `origin/main`; manifest [`869e07f`](https://github.com/fourblocks2020/trex-core/commit/869e07fb34bd8a65d330da58a28cbcb1505b9625) and both revisions are recorded in the execution evidence. |
## Release approval gates
All release gates are recorded as satisfied in the [REL-012 execution evidence](evidence/rel-012_20260717_execution-evidence.md):
- [x] Reviewed source and REL/runbook manifest are published and immutable.
- [x] Operations verified payload `81a853a`, recorded manifest `869e07f`, and captured `ROLLBACK_REVISION`.
- [x] User explicitly authorized production execution; off-market window is recorded.
- [x] Operations confirmed clean checkout and unchanged runtime configuration / credential / break-glass modes.
- [x] Operations confirmed no dependency or migration delta.
- [x] Operations used the approved supervisor, recorded startup acceptance, and completed permitted dry-run / verify smoke with no write indication.
## Rollout and rollback policy
1. Use the pinned immutable source only; use the host’s existing approved supervisor to restart the same daemon with unchanged approved configuration.
2. After startup acceptance, run only the read/dry-run commands in the REL-012 runbook.
3. Stop and roll back on startup failure, traceback/error regression, unexpected credential/source-mode behavior, unexpected mutation indication, or smoke output that violates the no-write/fail-closed contract.
4. Roll back by restoring the actual captured `ROLLBACK_REVISION` and restarting with unchanged configuration. Do not run database rollback commands, direct DB edits, or live sync.
## Release Decision Evidence
- **Released Decision:** `Released` — lead `confirm-release` recorded after reviewing the release-specific execution record.
- **Rationale:** immutable payload `81a853a` was deployed with manifest `869e07f`; clean preflight, unchanged safety-sensitive configuration, daemon restart/startup, read-only dry-run/verify acceptance, and no rollback trigger are all documented as PASS.
- **Recorded by:** 팀장, based on secret-free `trex-op` evidence received 2026-07-17 13:42:37 KST.
- **Primary record:** [REL-012 execution evidence](evidence/rel-012_20260717_execution-evidence.md).
- **Boundary:** `sync --live` is not daemon rollout smoke; any later per-target use remains an independent approved operational action.
## Feedback Handoff
- **Observation summary:** post-restart `sync --all --dry-run` exited `0` with `145` skipped `broker_qty_zero` results and no write indication; `verify` exited `0` with no C-warned slots. No release-scope failure was reported.
- **Discovery / REQ input:** none from this rollout evidence.
- **Train boundary:** REL-012 is released, but M5 is not reclassified as Train Released because REL-011 still lacks a separate execution-evidence decision.
@@ -1,116 +0,0 @@
# REL-012 cost-basis safe sync seed hotfix — deployment runbook
- Release: `REL-012`
- Target source: [`81a853a314391da7a8bc9df5517013c7fd296d61`](https://github.com/fourblocks2020/trex-core/commit/81a853a314391da7a8bc9df5517013c7fd296d61) (must be verified on origin before execution)
- Execution owner: 운영팀
- Approval required: team lead review + user production execution approval
- Safety classification: daemon restart; no DB migration; read/dry-run smoke only
> Do not record credentials, tokens, account numbers, DSNs, `.env` content, raw broker/portfolio responses, or command lines containing secrets. Use `[REDACTED]`.
## 1. Hard boundaries
Allowed only after every REL-012 approval gate is checked:
- Check out the approved immutable source and restart the existing trex-core daemon through the host’s approved supervisor.
- Inspect startup status/logs and execute `sync --all --dry-run --json` plus `verify --json` using the unchanged approved instance configuration.
Not allowed:
- `sync --live`, `--yes` with a live write, automatic remediation, `enter`, `exit`, `cancel`, target portfolio selection, or any DB write.
- Changes to credential/break-glass mode, instance config, endpoints, secrets, dependency versions, DB schema, AMQP topology, or strategy daemons.
If a prohibited action appears necessary, stop and return to the approver.
## 2. Preflight
1. Confirm the exact approved target SHA (`81a853a314391da7a8bc9df5517013c7fd296d61`), origin publication, approval, and off-market execution window. If the SHA cannot be verified on origin, **stop**.
2. Capture only redacted operating-checkout metadata:
```bash
pwd
git status --short
git rev-parse HEAD
git rev-parse --abbrev-ref HEAD
```
Record the current SHA as `ROLLBACK_REVISION`. A dirty checkout is a stop condition.
3. Identify the active instance configuration and existing supervisor method without exposing configuration values. Confirm credential/break-glass state will remain unchanged.
4. From a clean source checkout, verify the candidate and migration/dependency delta:
```bash
git fetch origin
git show -s --format='%H %s' <APPROVED_TARGET_SHA>
git diff --name-only "$ROLLBACK_REVISION" <APPROVED_TARGET_SHA> -- pyproject.toml trex_core/adapters/persistence/migrations
```
Any output on a dependency/migration path or any uncertainty about the target SHA is a stop condition requiring lead review.
## 3. Source deployment and restart
Use the approved host supervisor; do not infer a service-manager command from this document.
```bash
git fetch origin
git checkout --detach <APPROVED_TARGET_SHA>
# Stop the existing daemon through the approved host supervisor.
# Start exactly one daemon with the unchanged approved instance configuration.
.venv/bin/trex-core-daemon --config <approved-instance-config>
```
Record redacted proof of target SHA, old-process stop, new-process supervisor/PID status, selected instance key, and startup log interval.
## 4. Startup acceptance and read-only smoke
Before CLI smoke, confirm startup completed, RabbitMQ topology declaration completed, expected workers are running, and the post-restart window has no `ERROR`, `CRITICAL`, or traceback.
Run only:
```bash
.venv/bin/trex-core sync --all --dry-run --config <approved-instance-config> --json
.venv/bin/trex-core verify --config <approved-instance-config> --json
```
Acceptance:
- Output is sanitized; no credential/account/raw payload is collected.
- For available WAC, dry-run may show `seeded_avg_price=true` with the preview only; no slot/warning write occurs.
- For unavailable WAC on a positive-broker inactive slot, dry-run must show `seeded_avg_price=false`, sanitized reason, and no-write preview.
- No invocation adds `--live` or `--yes`; no remediation occurs.
- Any unexpected write indication, unsafe success/empty-balance interpretation, or error inconsistent with fail-closed behavior is a rollback trigger.
## 5. Rollback
For any stop condition, stop the new daemon through the approved supervisor, restore the actual captured `ROLLBACK_REVISION`, and restart with the unchanged configuration:
```bash
# Stop the new daemon through the approved host supervisor.
git checkout --detach "$ROLLBACK_REVISION"
# Restart with the same approved configuration through the supervisor.
```
Do not run DB rollback, direct DB changes, or `sync --live` as a diagnostic shortcut.
## 6. Evidence template
```text
Release: REL-012
Approved by / timestamp: [REDACTED AS NEEDED]
Window: [REDACTED]
Host / instance key: [REDACTED]
Pre-deploy revision (ROLLBACK_REVISION): <sha>
Target revision: <approved immutable daemon payload SHA>
Release manifest revision: <immutable commit URL of the REL-012/runbook revision used>
Configuration/credential mode unchanged: yes/no (values not recorded)
Checkout clean: pass/fail
Dependency/migration path delta: none / stop
Daemon stop/start evidence: <redacted supervisor status or PID>
Startup acceptance: pass/fail
sync --dry-run result: pass/fail + sanitized exit/status summary
verify result: pass/fail + sanitized exit/status summary
Unexpected write indication: none / details and stop action
Rollback performed: no / yes (reason and restored SHA)
Operator: [REDACTED]
```
REL-012 can move to `released` only after this record is returned and the lead completes `confirm-release`. `sync --live` remains a separate explicit approval gate.
@@ -1,50 +0,0 @@
# TREX REL-012 운영팀 배포 요청 근거
- 대상: 운영팀 (`trex-op`)
- 작성일: 2026-07-17
- 상태: **REL-012 Released — 운영 실행 증적 수령 및 lead `confirm-release` 기록 완료**
> 이 문서는 사전 운영 요청의 historical handoff와 완료 상태를 함께 보존한다. 실행의 primary record와 release decision은 [REL-012 execution evidence](../releases/evidence/rel-012_20260717_execution-evidence.md)를 따른다.
## 1. 결론
BAT-058은 independent QC를 통과했고, REL-012 daemon rollout은 immutable payload [`81a853a`](https://github.com/fourblocks2020/trex-core/commit/81a853a314391da7a8bc9df5517013c7fd296d61)로 완료됐다. 운영팀은 승인된 off-market window에서 preflight, clean checkout, approved supervisor restart, startup acceptance, `sync --all --dry-run`, `verify`를 완료했고 모두 PASS를 반환했다. 팀장은 release-specific primary record를 검토하여 `confirm-release`를 기록했다. 상세 결과는 [execution evidence](../releases/evidence/rel-012_20260717_execution-evidence.md)를 참조한다.
## 2. 배포 전 근거와 원본
| 순서 | Release | 배포 대상 source/artifact | 비운영 근거 | 실행 전 남은 gate | immutable 원본 링크 |
| --- | --- | --- | --- | --- | --- |
| 1 | REL-012 / BAT-058 | [`81a853a`](https://github.com/fourblocks2020/trex-core/commit/81a853a314391da7a8bc9df5517013c7fd296d61) | QC: focused `113 passed`, full `833 passed, 47 skipped`; release-prep full regression and Ruff/compileall PASS | **Completed:** production approval/window, host preflight, approved restart, dry-run/verify acceptance, and `confirm-release`; see execution evidence | [commit 81a853a](https://github.com/fourblocks2020/trex-core/commit/81a853a314391da7a8bc9df5517013c7fd296d61) |
원본 문서(현재 release candidate worktree):
- REL draft: [`REL-012`](../releases/rel-012_20260717_cost-basis-safe-sync-seed-hotfix.md)
- 운영 runbook: [`REL-012 deployment runbook`](rel-012-cost-basis-safe-sync-seed-deployment.md)
- batch verification: [`BAT-058 verification`](../batches/bat-058_20260716_cost-basis-safe-sync-seed/verification.md)
## 3. 승인 후 실행 순서
1. **Git-ops / lead** — reviewed source와 REL/runbook의 commit·push 및 immutable URL 검증을 완료했다. 운영팀은 실행 직전에 target SHA를 독립 재확인한다.
2. **운영팀** — 승인된 장외 window에 `ROLLBACK_REVISION`, clean checkout, unchanged config/credential mode, dependency/migration delta를 preflight에 기록한다.
3. **운영팀** — approved supervisor로 동일 config의 trex-core daemon을 target SHA로 재기동한다.
4. **운영팀** — startup acceptance 후 `sync --all --dry-run --json` 및 `verify --json`만 수행하고 runbook evidence template을 반환한다.
5. **팀장** — evidence를 검토해 `confirm-release` 여부를 판정한다.
어느 단계에서든 stop condition이면 즉시 rollout을 멈추고 captured `ROLLBACK_REVISION`으로 되돌린다. `sync --live`는 이 배포 요청의 범위가 아니다.
## 4. 핵심 주의사항 / 중단 조건
- 허용 범위: daemon restart, startup/log inspection, `sync --dry-run`, `verify`.
- 금지 범위: `sync --live`, `--yes` live write, 자동 remediation, 주문 명령, DB/스키마 변경, config/credential/break-glass 변경, AMQP topology 변경.
- target SHA가 정확하지 않거나 dirty checkout, dependency/migration delta, startup error/traceback, unexpected write indication, fail-closed 위반, unsafe empty-balance 해석이 있으면 **중단 및 rollback**.
- token, secret, account number, DSN, raw broker/portfolio payload를 전달서 또는 증적에 기록하지 않는다.
## 5. 운영팀 반환 증적
runbook의 evidence template을 작성해 아래만 전달한다.
- approver/window/operator 및 redacted host/instance identifier
- target SHA와 `ROLLBACK_REVISION`
- clean-checkout·dependency/migration preflight 결과
- daemon restart/startup·dry-run·verify의 secret-free 결과
- no-write/fail-closed 관찰 결과, rollback 여부와 사유/복구 SHA
@@ -1,149 +0,0 @@
# req-123: cost-basis canonical parse 및 WAC unavailable 안전 sync seed
- Status: Approved
- Type: Integration
- Priority: High
- Owner: trex-core-dev
## Intent
- portfolio-hub의 기존 `GET /v1/portfolios/{portfolio_id}/holdings/cost-basis` 응답에서 canonical field인 `cost_basis_per_unit`을 trex-core `PortfolioHubReadAdapter.get_cost_basis()`가 우선 해석하도록 하여 정상 HTTP 200 응답이 `None`으로 degrade되는 consumer contract defect를 수정한다.
- legacy alias는 실제 호환 필요성이 확인되는 경우에만 낮은 우선순위 fallback으로 유지하고, canonical field의 invalid 값을 legacy 값으로 조용히 은폐하지 않는다.
- broker 수량이 양수인데 WAC/avg_price가 unavailable한 inactive-slot onboarding에서 불완전한 live write를 방지한다. Discovery 승인 결정에 따라 live path는 fail-closed이며, dry-run은 진단과 no-write preview를 제공한다.
- 본 REQ는 dcy-038의 consumer hotfix 요구사항만 다루며 portfolio-hub producer API 변경, 인증·토큰·설정 변경, DB migration/direct edit, `sync --live` 실행, 배포를 포함하지 않는다.
## Requirement
1. `PortfolioHubReadAdapter.get_cost_basis(portfolio_id)`는 successful JSON object response에서 `cost_basis_per_unit`을 canonical WAC source로 먼저 검사하고, 유효한 양의 수치이면 `float` 값으로 반환해야 한다.
2. canonical key가 없을 때에만 승인된 legacy alias를 정해진 순서로 검사할 수 있다. 현재 지원 alias는 `avg_cost` → `wac` → `cost_basis`이며 순차 적용한다. canonical key가 response에 존재하지만 null, non-numeric, 0, negative 또는 허용 범위 밖이면 legacy 값으로 fallback masking하지 않는다.
3. cost-basis 결과는 최소한 다음 상태를 구분해 reconcile/operator surface가 sanitized reason을 표시할 수 있어야 한다: valid canonical/legacy value, field missing/null, malformed/non-numeric/invalid value, non-2xx response, transport/timeout failure, unexpected response shape. 기존 호출부 호환성이 필요한 경우 `Optional[float]` port를 유지하되, unavailable reason은 reconcile-local result/diagnostic으로 보존한다.
4. canonical WAC가 유효하면 `sync --dry-run`의 `after.avg_price`와 `seeded_avg_price=true`가 해당 값과 일치해야 하며, 실제 DB slot/warning write는 발생하지 않아야 한다. 승인된 live candidate도 동일한 source value를 사용해야 한다.
5. `broker_qty > 0`인 inactive slot onboarding에서 WAC/avg_price가 unavailable이면 dry-run은 `after.avg_price=null`, `seeded_avg_price=false`, sanitized source/absence reason 및 no-write preview를 제공해야 한다. 이 경우 inactive→holding 전이와 qty/status/avg_price write를 dry-run에서 수행하지 않아야 한다.
6. 동일 조건의 live sync path는 Discovery에서 승인된 fail-closed 정책을 적용해야 한다. 즉 status, qty, avg_price를 모두 기록하지 않고 non-zero failure로 중단하며, 평균가 없는 inactive→holding 전이를 허용하지 않는다. 기존 double-ack/live gate와 별도로 이 no-write barrier를 유지한다.
7. cost-basis port가 연결되지 않았거나 transport failure인 기존 graceful-degrade 경로와, canonical response parsing/validation failure 경로를 operator reason 및 테스트에서 구분해야 한다. unavailable을 정상적인 0 또는 빈 잔고로 해석하지 않아야 한다.
8. sync diagnostics/logging은 비밀 없이 `portfolio_id`/`slot_id`/`symbol`의 허용된 식별자, `dry_run` 또는 `live` mode, `seeded_avg_price`, cost-basis source(`canonical`/legacy/none), sanitized absence reason, no-write/fail-closed 결과를 판별 가능하게 해야 한다. token, credential payload, account number 등은 출력·로그·fixture에 포함하지 않는다.
9. response-contract 및 regression test는 adapter→reconcile→CLI/output 경로를 고정해야 한다. canonical payload, legacy payload(채택 시), canonical-invalid+legacy-present, missing/null, malformed, non-2xx/transport failure, current holding update, positive broker qty unavailable WAC의 dry-run no-write와 live no-write를 각각 검증해야 한다.
10. 관련 baseline 문서(`docs/interface-contract.md`, `docs/runtime-flows.md`, `docs/operations.md`)와 REQ/Batch/verification/release 문서는 portfolio-hub existing response contract, WAC absence semantics, dry-run diagnostics, fail-closed barrier, observability, rollback 및 release gate를 구현 결과와 일치하게 기술해야 한다.
## Acceptance Criteria
1. Given `{\"current_quantity\": 5.0, \"cost_basis_per_unit\": 124580.0}`인 HTTP 200 response, When `get_cost_basis()`를 호출하면, Then `124580.0`을 반환하고 요청 경로가 `/v1/portfolios/{portfolio_id}/holdings/cost-basis`임을 contract test가 고정한다.
2. Given canonical field가 없고 승인된 legacy fixture가 있는 response, When adapter가 parsing하면, Then 명시된 legacy 우선순위에 따라 값이 반환된다. Given canonical field가 invalid하고 legacy 값이 함께 있으면, When parsing하면, Then legacy로 masking하지 않고 invalid/unavailable reason을 반환한다.
3. Given canonical field가 null/missing/non-numeric/0/negative 또는 response shape가 object가 아닌 경우, When adapter/reconcile가 처리하면, Then 정상 WAC seed로 취급하지 않고 케이스별 sanitized reason을 기록·반환한다.
4. Given cost-basis endpoint가 non-2xx 또는 transport/timeout failure를 반환하는 경우, When sync 경로가 처리하면, Then parsing failure와 구분되는 unavailable reason을 내고 credential/token/raw response 비밀을 노출하지 않는다.
5. Given inactive slot과 `broker_qty=5`, canonical WAC `124580.0`인 경우, When `sync --dry-run`을 실행하면, Then preview의 `after.status=holding`, `after.qty=5`, `after.avg_price=124580.0`, `seeded_avg_price=true`를 표시하되 slot/warning write count는 0이다.
6. Given inactive slot과 `broker_qty>0`, WAC가 unavailable인 경우, When `sync --dry-run`을 실행하면, Then inactive→holding 전이 및 qty/status/avg_price write를 하지 않고 `avg_price=null`, `seeded_avg_price=false`, reason 및 no-write preview를 표시한다.
7. Given 동일한 positive broker holding/WAC unavailable 조건과 live path, When reconcile/sync가 실행되면, Then non-zero failure로 종료하고 status, qty, avg_price, warning clear를 포함한 write가 모두 0이며 inactive→holding 전이가 일어나지 않는다.
8. Given 이미 HOLDING인 slot의 broker 수량 정합 업데이트, When sync/reconcile가 처리하면, Then 기존 의도된 current-holding update와 warning semantics가 유지되고 canonical WAC가 있으면 avg_price seed 값이 일관되게 적용된다.
9. Given adapter/application/CLI regression suite를 실행하면, Then canonical contract, alias precedence, unavailable reason, dry-run no-write, live fail-closed, current holding regression, output/log redaction 및 `seeded_avg_price` 관측성 검증이 모두 통과한다.
10. Given release candidate를 운영에 반영하려는 경우, When release gate를 점검하면, Then targeted regression 및 independent QC PASS, dry-run evidence, explicit release approval, 단일 대상 live 실행 승인, rollback 명령/기준과 post-release monitoring plan이 없이는 배포 또는 `sync --live`를 시작하지 않는다.
## Impacted Area
- Docs:
- `docs/interface-contract.md` — portfolio-hub existing cost-basis response의 canonical field와 legacy compatibility 경계
- `docs/runtime-flows.md` — sync/reconcile WAC seed 및 unavailable fail-closed/no-write 흐름
- `docs/operations.md` — dry-run diagnostics, operator reason, observability, rollback/release gate
- `docs/discovery/dcy-038_20260716_cost-basis-response-contract-and-safe-sync-seeding.md`
- Modules:
- `trex_core/adapters/portfolio/hub_read.py` — `get_cost_basis()` canonical/legacy parse와 unavailable 분류
- `trex_core/application/reconciliation.py` — WAC unavailable no-write barrier, dry-run result 및 failure semantics
- `trex_core/runtime/cli.py` 또는 현재 sync output 경계 — sanitized diagnostics와 non-zero failure
- 기존 warning/observability 호출부 — fail-closed 시 warning clear/write 차단 및 식별 가능한 로그
- Tests:
- `tests/adapters/portfolio/test_hub_read.py` — HTTP response contract와 precedence/validation regression
- `tests/application/test_sync.py` 및 관련 reconcile/CLI test — dry-run no-write, live fail-closed, diagnostics/output regression
## Notes
- Source Discovery: `docs/discovery/dcy-038_20260716_cost-basis-response-contract-and-safe-sync-seeding.md`.
- Producer source of truth: portfolio-hub existing `HoldingsCostBasisResponse.cost_basis_per_unit`; 이 REQ에서는 producer endpoint/schema/auth를 수정하지 않는다.
- Discovery confirmation: Justin Ko, 2026-07-16 22:44:20 KST. 승인 내용은 REQ drafting을 허용하며 `sync --live`, DB 변경, token/config 변경 또는 배포 승인을 의미하지 않는다.
- OQ-2 Answered(2026-07-16): Producer 확인 결과, 현재 producer/fixture는 canonical `HoldingsCostBasisResponse.cost_basis_per_unit`을 기준으로 하고, 호환성 사유로 `avg_cost` → `wac` → `cost_basis` fallback을 유지한다. 단, canonical key가 유효하지 않으면 legacy fallback을 허용하지 않는다.
- OQ-3 Answered(2026-07-16): 기존 `Optional[float]` port를 유지한다. unavailable reason은 reconcile-local 진단 구조에서 `seeded_avg_price`, source(`canonical`/`legacy`/`none`), reason을 분리해 노출한다.
- Rollback은 consumer hotfix의 코드/문서 revert와 release 이전 candidate 철회를 의미한다. 이미 운영에 기록된 slot을 자동으로 직접 수정하거나 DB migration으로 복구하는 범위는 포함하지 않으며, 별도 승인된 운영 절차로만 다룬다.
- Dependencies: existing REQ-105 sync contract, dcy-034의 WAC seed 범위, dcy-037의 broker truth/credential 변경은 재설계하지 않고 현재 baseline과 회귀 확인만 한다.
- No live action: 구현·QC·release approval 전 `sync --live`, production sync, deployment, credential/token/config access는 금지한다.
## Change Log
### CHG-20260716-01
- Date: 2026-07-16
- Author: delivery-runner
#### Change Summary
- dcy-038 confirmed Discovery의 consumer hotfix 범위를 단일 Integration REQ 초안으로 분해했다.
- canonical `cost_basis_per_unit` 우선 parsing, legacy alias 정책, WAC unavailable fail-closed sync, dry-run diagnostics, response-contract/regression test, observability 및 rollback/release gate를 요구사항과 AC로 고정했다.
#### Acceptance Criteria Delta
- Added: AC-1~10 전부 신규.
- Updated: 없음.
- Removed: 없음.
#### Impacted Area Delta
- `hub_read.py`, `reconciliation.py`, sync CLI/output, 관련 adapter/application tests 및 runtime/interface/operations baseline 문서를 영향 범위로 명시했다.
- portfolio-hub API/schema/auth, token/config, DB migration/direct edit, live sync, deployment는 명시적으로 제외했다.
#### Delivery Trace Delta
- Batches: none (draft only; suggest-batch-reqs 이후 편성).
- Releases: none (별도 release approval gate 대상).
#### Revalidation Impact
- Reverification Needed: yes — confirm-req 전 legacy producer 증거, diagnostic result seam, source-level no-write 경계를 재검토하고 구현/QC에서 AC별 재검증한다.
- Existing Implementation Invalidated: no — 현재 구현의 canonical-field parsing gap과 WAC unavailable live-write risk를 후속 hotfix requirement로 명시한다.
- Status Recommendation: Proposed
- Follow-up Action: OQ-2/OQ-3이 이 문서에서 Answered로 전환되었으나, implementation에서 reason enum/출력 형식 고정은 설계 단계에서 재확인한다.
#### Validation Plan
- adapter HTTP fake/fixture로 canonical field와 alias precedence를 검증하고, canonical-invalid에서 legacy fallback 금지(차단) 및 canonical-absent에서 legacy fallback 동작을 함께 검증한다.
- application/CLI recording double로 dry-run/live write count, fail-closed no-write, `seeded_avg_price`, source, reason 분리 표시를 검증한다.
- targeted suite와 independent QC rerun 결과를 Batch verification에 AC별로 기록한다.
### CHG-20260716-02
- Date: 2026-07-16
- Author: delivery-runner
#### Change Summary
- OQ-2/OQ-3을 본 REQ에서 답변 상태로 확정한다.
- OQ-2: canonical-absent fallback alias 순서 및 canonical-invalid/no-valid canonical 값 처리 우선순위를 `Requirement`/`AC`/`Notes`에 고정한다.
- OQ-3: `Optional[float]` 시그니처 유지 + unavailable reason을 reconcile-local 진단으로 분리 표기( `seeded_avg_price`, source, reason )하도록 변경한다.
#### Acceptance Criteria Delta
- Added: AC-2/AC-3의 canonical-invalid/legacy precedence 경로 재정의(유효성 규칙 및 canonical masking 금지).
- Updated: 없음.
- Removed: 없음.
#### Impacted Area Delta
- `trex_core/adapters/portfolio/hub_read.py`, `trex_core/application/reconciliation.py`, `tests/adapters/portfolio/test_hub_read.py`, `tests/application/test_sync.py`, `docs/interface-contract.md` 문안에 OQ 결정 반영.
#### Delivery Trace Delta
- Batches: none (draft/confirm 단계).
- Releases: none (별도 release approval gate 대상).
#### Revalidation Impact
- Reverification Needed: yes — batch design에서 reason enum/표시 고정값, AC-별 regression test matrix 보강 및 independent QC rerun을 통해 재검증한다.
- Existing Implementation Invalidated: no.
- Status Recommendation: Proposed.
- Follow-up Action: 확인된 OQ 반영 후 confirm-req 재평가에서 pass 되면 `suggest-batch-reqs` 진행.
### CHG-20260716-03
- Date: 2026-07-16
- Author: trex-dev-lead
#### Change Summary
- confirm-req 게이트에서 dcy-038 승인 근거(제한범위/구조/결정사항)를 기준으로 `REQ-123`을 `Approved`로 전환하고, 다음 배치 인계만 선행한다.
- 구현/검증/배포는 이 상태 전이 이후 `trex-core-dev`가 `suggest-batch-reqs`에서 생성되는 `BAT-058`로 진행한다.
#### Acceptance Criteria Delta
- Added: StagePilot 상태 transition 근거(확정 OQ 0개, OQ-1/BLA no-write 정책 승인, scope non-goals 고정)을 confirm 증빙으로 고정.
- Updated: none.
- Removed: none.
#### Impacted Area Delta
- StagePilot state: docs/srs/index REQ-123 status 및 문서 연계 준비.
- Implementation trace: batch planning 경로로 이동.
#### Delivery Trace Delta
- Next: `BAT-058` (dcy-038, dcy-038-based hotfix) 생성 후 implementation 단계로 이관.
- Scope: implementation은 REQ-123 AC-1~10 + no-write 리스크 관리 포함.
#### Revalidation Impact
- Reverification Needed: yes (batch implementation/verification 단계).
- Existing Implementation Invalidated: no.
- Status Recommendation: Approved.
- Follow-up Action: suggest-batch-reqs 완료 후 implementation/handoff.
@@ -1,23 +0,0 @@
# REQ-124: intent venue persistence and execution
- Type: Integration
- Status: Implemented
- Priority: High
- Source: dcy-039
## Requirements
1. buy/sell intent는 `venue`를 선택적으로 받으며 미지정은 KRX다.
2. KRX/NXT/SOR 외 값은 슬롯 전이 전에 `invalid_venue`로 거부한다.
3. venue는 intent log, pending slot, `OrderRequest`, timeout/operator cancel에
동일하게 전달한다.
4. INACTIVE 복귀 시 venue는 KRX로 초기화한다.
5. 기존 venue 미지정 전략과 KRX 테스트는 변경 없이 통과한다.
6. PostgreSQL migration은 기존 행을 KRX로 안전하게 backfill한다.
## Acceptance Evidence
- `tests/application/test_venue_intent_flow.py`
- `tests/domain/slot/test_contract.py`
- `tests/adapters/persistence/test_postgresql_slot_repository_parity.py`
- 전체 회귀 837 passed, 47 skipped
@@ -1,160 +0,0 @@
# req-127: 공통 계좌 자본배분 순수 정책
- Status: Approved
- Type: Interface
- Priority: High
- Owner: trex-core-dev
## Intent
- 메가 스윙과 탑50 스윙이 같은 계좌 현금 배분 규칙을 사용하도록, 외부 입출력과 분리된 결정적 정책을 Core 도메인에 제공한다.
- 고가 종목을 무조건 균등액으로 자르지 않고 제한된 한 주 가격 여유를 허용하되, 실제로 동시에 열릴 수 있는 최악 슬롯 조합도 가용 현금을 넘지 않게 한다.
- KIS 어댑터는 현금·호가 조회와 결과 매핑만 맡고, Portfolio Hub 원장과 기존 주문수량 계약은 변경하지 않는다.
## Requirement
1. Core는 `available_cash`, 양의 `slot_count`, 고유한 `portfolio_id`·`symbol`과 양의 `protected_share_cost`를 가진 후보 목록, 0 이상의 `flex_share_count`를 입력받는 순수 자본배분 정책을 제공해야 한다.
2. 보호된 한 주 가격은 양의 `price`와 0 이상의 `slippage_pct`를 받아 `price × (1 + slippage_pct/100)`을 원 단위 올림해 계산해야 한다.
3. 초기 후보 적격 상한은 `available_cash // slot_count + cheapest_protected_share_cost × flex_share_count`여야 하며, 이를 넘는 후보는 `protected_share_above_flex_limit`으로 제외해야 한다.
4. 1차 적격 후보 중 가장 비싼 `min(slot_count, eligible_count)`개 한 주 가격 합이 가용 현금을 넘으면, 후보 가격과 입력 순서를 안정적인 동률 기준으로 가장 비싼 후보부터 제외하고 `worst_case_cash_overcommit`을 반환해야 한다.
5. 남은 후보에는 각 후보의 보호 한 주 가격 이상이면서, 가장 큰 `min(slot_count, eligible_count)`개 배정액 합이 가용 현금 이하인 최대 공통 배정액을 적용해야 한다.
6. 현금이 0이면 모든 후보의 배정액을 0으로 하고 `no_available_cash`를 반환해야 한다. 음수 현금, 0 이하 슬롯 수, 빈 후보, 음수 유동 여유, 중복 식별자, 0 이하 한 주 가격은 명시적으로 거부해야 한다.
7. 결과는 입력 후보 순서를 보존한 후보별 `portfolio_id`, `symbol`, 배정액, 사유와 계좌 수준 `available_cash`, `slot_count`를 제공해야 하며, 단순 `portfolio_id → 배정액` 호환 표면도 제공해야 한다.
8. 정책 모듈은 네트워크, 파일, 환경변수, 데이터베이스, 브로커 및 전략 모듈에 의존하지 않아야 한다. 현금·호가 조회, 후보·슬롯 선택, 설정 저장, 실제 주문수량 산출과 주문 제출은 호출 계층 책임으로 유지해야 한다.
9. 기존 `BuyIntent`의 정수 수량·가격 계약, Core 주문 실행, 슬롯 상태머신, Portfolio Hub 원장 계약을 변경하지 않아야 한다.
10. `docs/project-structure.md`와 배치 검증 문서는 공통 정책의 책임, 호출 계층 책임, 비운영 범위, 회귀 증거를 구현과 일치하게 기록해야 한다.
## Acceptance Criteria
1. Given 메가 스윙 후보가 삼성전자 보호가격 220,000원과 SK하이닉스 보호가격 1,400,000원이고 현금 2,000,000원·2슬롯인 경우, When 정책을 실행하면, Then 삼성전자 2,000,000원·SK하이닉스 0원을 반환하고 SK하이닉스 제외 사유가 `protected_share_above_flex_limit`이다.
2. Given 같은 후보에서 현금 3,000,000원이고 SK하이닉스 보호가격이 1,510,000원인 경우, When 한 주 유동 여유를 적용하면, Then 삼성전자 1,490,000원·SK하이닉스 1,510,000원을 반환한다.
3. Given 현금 2,000,000원·3슬롯과 보호가격 100,000/200,000/500,000/700,000/1,050,000원인 탑50 후보 5개, When 정책을 실행하면, Then 마지막 후보를 제외하고 650,000/650,000/650,000/700,000/0원을 반환한다.
4. Given 현금 0원, When 정책을 실행하면, Then 모든 배정액은 0이고 사유는 `no_available_cash`이다.
5. Given 잘못된 가격·현금·슬롯·후보·중복 식별자 입력, When 정책을 실행하면, Then 조용한 보정 없이 `ValueError`로 거부한다.
6. Given 고정 시드 무작위 입력 10,000개, When 모든 결과의 가장 큰 양수 배정액을 슬롯 수만큼 합하면, Then 모든 경우 가용 현금 이하이고 양수 배정액은 해당 후보의 한 주 가격 이상이다.
7. Given 기존 로컬 어댑터 공식과 고정 시드 무작위 입력 50,000개, When 양쪽 결과를 비교하면, Then `portfolio_id → 배정액` 결과가 모든 경우 완전히 일치한다.
8. Given Core 전체 테스트와 로컬 어댑터 전체 테스트, When Core 정책 연결 상태로 실행하면, Then 기존 회귀와 신규 정책 테스트가 모두 통과한다.
9. Given 탑50 3슬롯 한 달 가상거래 입력, When 기존 공식과 Core 정책 연결 결과를 비교하면, Then 요약 지표와 거래원장·배분결정 원장이 동일하다.
10. Given 이 브랜치가 검토 중인 경우, When 운영 상태를 확인하면, Then 실전 런타임·주문 게이트·계좌 설정·Portfolio Hub 데이터에는 변경이 없다.
## Impacted Area
- Docs:
- `docs/discovery/dcy-042_20260729_shared-account-capital-allocation-policy.md`
- `docs/project-structure.md`
- 신규 배치의 계획·설계·구현·검증 문서
- Modules:
- `trex_core/domain/capital_allocation.py`
- 로컬 KIS 어댑터의 자본배분 호환 연결부
- Tests:
- `tests/domain/test_capital_allocation.py`
- 로컬 어댑터 회귀
- 메가 대표 경계값과 탑50 월간 가상거래 비교
## Notes
- Source Discovery: `docs/discovery/dcy-042_20260729_shared-account-capital-allocation-policy.md`.
- 기존 `dcy-021`과 `REQ-061`의 주문수량 책임은 유지한다. Core는 이 반복에서 계좌 배분의 순수 계산만 소유하며, 실제 주문 의도의 수량을 자동 수정하지 않는다.
- Portfolio Hub는 현금·보유·손익 원장의 정본으로 유지한다. 이 요구사항은 Hub API·스키마·데이터를 변경하지 않는다.
- `REQ-124`~`REQ-126`과 `dcy-039`~`dcy-041`, `BAT-059`~`BAT-061`은 병렬 Core 브랜치 작업에 예약되어 있어 충돌 방지를 위해 다음 미사용 번호를 선택했다.
- 구현·가상 검증·Draft PR은 허용되지만 실전 배포와 주문 활성화는 별도 승인 대상이다.
## Change Log
### CHG-20260729-01
- Date: 2026-07-29
- Author: delivery-runner
#### Change Summary
- 확정된 `dcy-042`의 공통 계좌 자본배분을 단일 Core 도메인 인터페이스 요구사항으로 작성했다.
#### Intent
- 공통 계산의 중복을 제거하면서 브로커·원장·전략 책임 경계를 유지한다.
#### Acceptance Criteria Delta
- Added:
- 메가 대표 2건, 탑50 대표 1건, 입력 거부, 10,000건 불변식, 50,000건 기존 공식 완전 일치, 전체 회귀, 월간 원장 비교, 비운영 확인.
- Updated:
- 없음.
- Removed:
- 없음.
#### Impacted Area Delta
- Docs:
- Discovery, 프로젝트 구조, 신규 배치 문서.
- Modules:
- Core 순수 도메인 정책과 로컬 어댑터 연결부.
- Tests:
- Core 단위·속성 회귀, 어댑터 전체 회귀, 탑50 월간 가상 비교.
#### Delivery Trace Delta
- Batches:
- none.
- Releases:
- none.
#### Revalidation Impact
- Reverification Needed: yes
- Existing Implementation Invalidated: no
- Status Recommendation: keep-current
- Follow-up Action: 요구사항 승인 후 단일 배치에서 구현·문서·교차 검증을 완료한다.
#### Validation Plan
- Core 단위·전체 회귀, 기존 공식 50,000건 완전 일치, 로컬 어댑터 전체 회귀, 메가 경계값, 탑50 월간 가상 원장 비교와 공용 7단계 가상 인증을 수행한다.
### CHG-20260729-02
- Date: 2026-07-29
- Author: trex-dev-lead
#### Change Summary
- 사용자 승인 범위, 필수 문서 필드, 측정 가능한 수락 기준과 비운영 경계를 재검토해 `REQ-127`을 Approved로 전환했다.
#### Intent
- 단일 배치의 구현·검증 기준으로 사용할 수 있도록 요구사항을 동결한다.
#### Acceptance Criteria Delta
- Added:
- 없음.
- Updated:
- 없음.
- Removed:
- 없음.
#### Impacted Area Delta
- Docs:
- SRS 상태와 인덱스 상태 동기화.
- Modules:
- 없음.
- Tests:
- 없음.
#### Delivery Trace Delta
- Batches:
- 다음 단계에서 BAT-062 생성.
- Releases:
- none.
#### Revalidation Impact
- Reverification Needed: yes
- Existing Implementation Invalidated: no
- Status Recommendation: keep-current
- Follow-up Action: BAT-062에서 구현·검증 증거를 수락 기준별로 연결한다.
#### Validation Plan
- REQ-127 수락 기준 1~10을 배치 검증 문서와 자동화된 테스트·가상거래 증거로 대조한다.
@@ -1,82 +0,0 @@
# dcy-041 Core broker gap closure validation
- 검증 시각: 2026-07-29 17:54 KST
- 브랜치: `codex/core-adapter-gap-closure`
- 검증 HEAD: `611805d`
- 분류: `research_only=true`, `official_comparable=false`,
`real_order_allowed=false`, `order_adapter=virtual_locked_replay`
## 검증 결과
1. 정적검사: `uv run --extra dev ruff check .` PASS.
2. 로컬 전체 회귀: `903 passed, 50 skipped`.
3. 격리 Docker PostgreSQL/RabbitMQ 전체 통합: `953 passed, 1 skipped`.
4. 공용 매매 인증 7단계:
- logic
- regression
- virtual_contract
- virtual_e2e
- fault_injection
- restart_recovery
- ledger_reconcile
- 결과: PASS
5. 인증 산출물:
`/Volumes/J_HDD/virtual_exchange/artifacts/validation/trex-core-execution/core-gap-final-20260729-1753-kst/run.json`
## 장애 주입·재시작 판정
- 결과불명 주문은 동일 broker order 조회로만 수렴하고 중복 주문 0.
- timeout·운영자 취소는 `cancel_state IS NULL` 비교·설정에 성공한 한
호출만 전송하며 재시작 후 반복 취소 0.
- AUTO는 실제 KRX/NXT로 확정되어 주문번호와 함께 저장되고 취소·재시작에 재사용.
- 체결 후 잔량과 재시작 원장대조는 실시간 잔고만 사용.
- KIS 조회 전송 오류에만 동일 endpoint·계좌·상품·app identity의 미만료
스냅샷을 사용.
- 계정 2와 같은 혼합계정은 전략 소유수량만 스냅샷·매도수량에 노출.
- 신규매수 현금검사와 pending 전이는 동일 instance advisory lock 안에서 원자 처리.
## 최초 Docker 실패와 해소
- 최초 실패 단계: PostgreSQL 마이그레이션 재적용.
- 최초 원인: 017 마이그레이션이 019에서 도입한 기존 `AUTO` 행을 허용하지 않아
반복 적용 시 check constraint 위반.
- 마지막 안전 상태: 운영 컨테이너·DB·주문 경로 미접촉, 격리 테스트 DB만 사용.
- 수정: 017 제약을 AUTO 호환으로 만들고 기존 AUTO 행이 있는 상태의 migration
replay 회귀를 추가.
- 재실행: 새 격리 DB에서 전체 마이그레이션과 953개 통합검사 PASS.
## 실전 잠금
- 실전 계정·주문·운영 컨테이너 변경 없음.
- `live_dry_run`, `live_one_shot`, 서버 배포, 로컬 어댑터 제거는 미수행.
- 실전 반영은 서버 차이 검증 후 대상 계정·전략·이미지 SHA를 명시한 별도 승인 필요.
## 서버 이미지 차이와 적용 대기
- 검증 후보 이미지:
- tag: `trex-core:candidate-12b6eb5`
- image id: `sha256:343f0a0955f92357db5ce53f26d22d8196bc325ec0e07919c53c44a7997a44aa`
- revision label: `12b6eb5`
- import smoke: PASS
- 현재 운영 Core 이미지:
- 계정 2: `sha256:21c4ed9bfdcf...`
- 계정 3: `sha256:bf8064a94e93...`
- Top50 가상: `sha256:fa9b9c609a01...`
- Top50 섀도: `sha256:fb981d8c62fc...`
- 네 운영 이미지의 `execution.py`, `kis.py`, `intents.py` 해시는 모두 후보와 다르다.
- 계정 2·3의 `TREX_ACCOUNT_SNAPSHOT_ENABLED`는 현재 미설정이라 기본 OFF다.
- 계정 2는 `TREX_KIS_STRATEGY_OWNERSHIP_PATH`가 설정되어 수동 보유 보호가 유지되고,
계정 3은 전용 전략계정이라 이 경로가 미설정이다.
- 현재 Compose의 Docker build context는 정본 `/Volumes/J_HDD/trex/trex-core`
(`7084304`)를 복사한다. PR 브랜치는 별도 worktree이므로 일반 rebuild만 실행하면
후보가 아니라 이전 소스를 다시 만든다.
### 승인 후 적용 순서
1. 현재 계정별 image id를 rollback tag로 보존한다.
2. PR 브랜치 또는 승인된 merge SHA를 명시한 이미지로만 교체한다.
3. DB 백업 뒤 migration 017/019/020/021 재적용과 테이블·제약을 확인한다.
4. 계정 3에서 주문 없는 `live_dry_run`과 계좌범위 해시·조회만 확인한다.
5. 계정 3 Core 적용·재시작 복구·원장대조 후 계정 2를 별도 적용한다.
6. 계정 2는 수동보유 보호와 전략 소유수량을 재확인한다.
7. 두 계정의 Core 동작이 확인된 뒤에만 로컬 KIS 어댑터 중복 기능을 제거한다.
@@ -1,30 +0,0 @@
# TREX Core KIS 주문경로 가상 인증
- 실행 ID: `trex-kis-traffic-20260728-140301`
- 실행일: 2026-07-28 KST
- 대상 브랜치: `codex/account2-ownership-broker-adapter`
- 분류: `research_only=true`, `official_comparable=false`
- 주문 연결: `virtual_locked_replay`
- 실주문 허용: `real_order_allowed=false`
## 결과
공용 거래 실행 인증의 로직, 전체 회귀, 가상 주문계약, 매수·매도 왕복,
장애주입, 재시작 복구, 원장대조 1~7단계를 모두 통과했다.
- 최종 슬롯: `inactive`
- 주문 원장 거래: 2건
- 미체결: 0건
- 최종 포지션: 0건
- 중복 주문: 0건
- 전략·Core·가상 브로커 원장: 일치
기계 판독 정본은 아래 경로에 보존한다.
```text
artifacts/validation/trex-core-execution/
trex-kis-traffic-20260728-140301/
```
이 인증은 실계좌 또는 실주문 승인이 아니다. 실제 주문 관문과 계정 설정은
별도 승인 없이 변경하지 않는다.