16 KiB
16 KiB
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()는 legacyavg_cost,wac,cost_basis만 탐색한다. - 따라서 정상 HTTP 200 response의
cost_basis_per_unit=124580도None으로 해석된다. reconcile은 이를 graceful degrade로 취급해 qty/status만 seed하고avg_price를 누락할 수 있다.
- trex-core의 realized-PnL adapter는 이미
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():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를 쓸 수 있다.
- adapter가
- 운영 재현(비밀 없는 response shape):
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를 slotavg_priceseed에 사용한다. - 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를 변경하지 않는다.
주요 사용자 시나리오
- 운영자가 inactive slot과 broker qty>0을 대상으로
sync --dry-run을 실행한다. canonical WAC가 있으면 결과는avg_price=<WAC>,seeded_avg_price=true로 표시되며 DB write는 없다. - portfolio-hub가 WAC를
null로 반환하거나 response가 malformed이면 dry-run은 왜 seed가 불가능한지 안전하게 표시한다. live sync는 확정된 no-write policy를 위반하지 않는다. - 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_pricesource/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_unitparsing, legacy compatibility policy, safe sync behavior, regression tests, baseline/runbook 정합.
- trex-core consumer의 canonical
- 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_basisaliases를 지원할 실제 producer가 현재 존재하는가?- 상태: Answered (2026-07-16, in REQ-123)
- 결정: producer/fixture의 canonical은
cost_basis_per_unit기반이며, 호환성 유지를 위해avg_cost→wac→cost_basisfallback은 유지한다. 단, 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로 넘기기 전 확인 체크
- producer canonical field 및 consumer mismatch의 source evidence 확인
- 근거: portfolio-hub
HoldingsCostBasisResponse.cost_basis_per_unit; trex-coreget_cost_basis()legacy-only parser; synthetic canonical response 재현.
- 근거: portfolio-hub
- P1 scope 및 non-goals 초안화
- 근거: producer API 변경, credential/token scope, live execution, DB direct edit를 명시적으로 제외했다.
- 성공 기준의 test seam과 evidence source 정의
- 근거: adapter response fixture, sync application fixture, independent QC rerun을 S-1~S-5에 연결했다.
- 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 무기록이다.
- OQ-2/OQ-3: producer canonical 우선순위와 compatibility strategy, unavailability diagnostics는 REQ-123에서 확정했다.
- 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.mddocs/discovery/dcy-037_20260714_operator-broker-truth-and-managed-slot-divergence-hotfix.mdtrex_core/adapters/portfolio/hub_read.py:get_cost_basistrex_core/application/reconciliation.py:sync_slot_to_brokertests/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. 요구사항 목록 > 범위 경계
- 리스크/오픈 질문: # 6. 리스크/가정 목록
- 성공 기준: # 7. 초기 성공 기준
- 인계 전 확인: # 8. REQ로 넘기기 전 확인 체크
- 사용자 결정 필요: # 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