docs(discovery): reject insufficient manual ledger writes

This commit is contained in:
2026-07-14 15:58:10 +09:00
parent 29bf6de22a
commit 9356e4da3e
@@ -110,6 +110,7 @@
- FR-11: manual ledger history 조회 API는 portfolio별 최근 ledger entries를 반환해야 한다. action, amount/quantity/price/fee/tax, note, recorded_at, after-snapshot을 trex-web이 표시할 수 있어야 한다. - FR-11: manual ledger history 조회 API는 portfolio별 최근 ledger entries를 반환해야 한다. action, amount/quantity/price/fee/tax, note, recorded_at, after-snapshot을 trex-web이 표시할 수 있어야 한다.
- FR-12: disabled portfolio write는 현행 state rule과 일관되게 거부한다. - FR-12: disabled portfolio write는 현행 state rule과 일관되게 거부한다.
- FR-13: daemon-managed portfolio에 manual ledger write 시 완전 차단하지 않는다. 대신 response/request contract에 trex-web이 강한 경고와 명시 확인을 표현할 수 있는 정책을 포함한다. - FR-13: daemon-managed portfolio에 manual ledger write 시 완전 차단하지 않는다. 대신 response/request contract에 trex-web이 강한 경고와 명시 확인을 표현할 수 있는 정책을 포함한다.
- FR-14: manual endpoint는 1차 범위에서 새 음수 상태를 만드는 write를 거부한다. `BUY`는 `quantity * price + fee + tax > cash_balance`이면 HTTP 409, `SELL`은 `quantity > asset_quantity`이면 HTTP 409, `BUFFER_OUT`/`WITHDRAW`는 `amount > profit_buffer`이면 HTTP 409, `BUFFER_IN`은 `amount > cash_balance`이면 HTTP 409로 거부한다. `INVEST`는 `amount > 0`이면 허용한다.
## 비기능 요구사항 ## 비기능 요구사항
- NFR-1: ledger append와 portfolio snapshot update는 단일 DB 트랜잭션으로 처리되어야 한다. - NFR-1: ledger append와 portfolio snapshot update는 단일 DB 트랜잭션으로 처리되어야 한다.
@@ -147,7 +148,7 @@
- R-3 (Medium): manual write와 daemon write가 같은 portfolio에서 혼재되어 운영자가 상태를 오해. - R-3 (Medium): manual write와 daemon write가 같은 portfolio에서 혼재되어 운영자가 상태를 오해.
- 완화: source 분리, daemon-managed warning/explicit confirmation policy, history 표시. - 완화: source 분리, daemon-managed warning/explicit confirmation policy, history 표시.
- R-4 (Medium): 기존 CLI/domain 흐름과 신규 API 회계 공식이 divergence. - R-4 (Medium): 기존 CLI/domain 흐름과 신규 API 회계 공식이 divergence.
- 완화: shared domain helper 추출 또는 동일 test vectors로 검증. - 완화: shared domain helper 추출 또는 동일 test vectors로 검증. manual API는 1차에서 부족 cash/quantity/buffer를 거부하므로 기존 CLI/domain validation 정책과 의도적 차이가 있다면 REQ에서 명시한다.
- R-5 (Mitigated): `REVERSAL`이 target entry 참조 없이 notes 기반으로만 남아 audit성이 약해질 수 있음. - R-5 (Mitigated): `REVERSAL`이 target entry 참조 없이 notes 기반으로만 남아 audit성이 약해질 수 있음.
- 완화: 사용자 결정(2026-07-14)에 따라 `target_ledger_entry_id`를 필수로 한다. REQ에서는 컬럼/metadata 저장 위치와 중복 reversal 방지 규칙을 구체화한다. - 완화: 사용자 결정(2026-07-14)에 따라 `target_ledger_entry_id`를 필수로 한다. REQ에서는 컬럼/metadata 저장 위치와 중복 reversal 방지 규칙을 구체화한다.
- R-6 (Low): `PROFIT` legacy action과 `BUFFER_IN`/`WITHDRAW` 의미가 사용자 UI에서 혼동. - R-6 (Low): `PROFIT` legacy action과 `BUFFER_IN`/`WITHDRAW` 의미가 사용자 UI에서 혼동.
@@ -173,9 +174,11 @@
- REQ 세부화 항목: target이 같은 portfolio 소속인지 검증, 이미 reversed 된 entry의 재 reversal 금지 여부, REVERSAL 자체 reversal 허용 여부, target reference 저장 위치(정식 컬럼 vs metadata)를 확정한다. - REQ 세부화 항목: target이 같은 portfolio 소속인지 검증, 이미 reversed 된 entry의 재 reversal 금지 여부, REVERSAL 자체 reversal 허용 여부, target reference 저장 위치(정식 컬럼 vs metadata)를 확정한다.
- 종료 조건: 완료 - 종료 조건: 완료
- OQ-3: manual `BUY`/`SELL`에서 잔고/수량 부족을 거부할 것인가, 표시 음수 허용 정책과 맞춰 결과 음수를 허용할 것인가? - OQ-3: manual `BUY`/`SELL`에서 잔고/수량 부족을 거부할 것인가, 표시 음수 허용 정책과 맞춰 결과 음수를 허용할 것인가?
- 사용자 정책: trex-web UI 입력 음수 금지, 기존/결과 표시 음수 허용. - 상태: Answered
- 상태: Open - 결정: manual endpoint 1차에서는 부족 cash/quantity/buffer를 거부한다. `BUY`는 required cash(`quantity * price + fee + tax`)가 `cash_balance`를 초과하면 HTTP 409, `SELL`은 requested quantity가 `asset_quantity`를 초과하면 HTTP 409, `BUFFER_OUT`/`WITHDRAW`는 amount가 `profit_buffer`를 초과하면 HTTP 409, `BUFFER_IN`은 amount가 `cash_balance`를 초과하면 HTTP 409로 거부한다. `INVEST`는 amount > 0이면 허용한다.
- 종료 조건: portfolio-hub validation policy 확정 - 사용자 정책 정합: trex-web UI 입력 음수 금지, 기존/결과 표시 음수 허용. 즉 read/display에서는 기존 음수 상태를 숨기지 않지만, manual write가 새 음수 상태를 만드는 것은 1차에서 금지한다.
- 근거: 수동 입력은 사전 검증 가능한 operation이고, `QUANTITY_ADJUSTMENT`를 1차 제외했으므로 부족 수량 SELL 허용은 우회 보정 기능이 될 수 있다.
- 종료 조건: 완료
- OQ-4: daemon-managed portfolio write 시 API가 `warning_required`/`confirmation_token` 같은 기계적 확인 필드를 요구할 것인가, 아니면 trex-web UI 경고만으로 충분한가? - OQ-4: daemon-managed portfolio write 시 API가 `warning_required`/`confirmation_token` 같은 기계적 확인 필드를 요구할 것인가, 아니면 trex-web UI 경고만으로 충분한가?
- 상태: Open - 상태: Open
- 종료 조건: API-level safety contract 확정 - 종료 조건: API-level safety contract 확정
@@ -207,7 +210,7 @@
# 8. REQ로 넘기기 전 확인 체크 # 8. REQ로 넘기기 전 확인 체크
- [x] API endpoint shape 확정(OQ-1) — 기존 `/ledger/trades` 유지 + 신규 `POST /v1/portfolios/{portfolio_id}/ledger/manual` 추가 - [x] API endpoint shape 확정(OQ-1) — 기존 `/ledger/trades` 유지 + 신규 `POST /v1/portfolios/{portfolio_id}/ledger/manual` 추가
- [x] REVERSAL target/reference policy 확정(OQ-2) — `target_ledger_entry_id` 필수 - [x] REVERSAL target/reference policy 확정(OQ-2) — `target_ledger_entry_id` 필수
- [ ] 부족 잔고/수량 validation policy 확정(OQ-3) - [x] 부족 잔고/수량 validation policy 확정(OQ-3) — manual write가 새 음수 상태를 만드는 것은 1차 거부
- [ ] daemon-managed confirmation contract 확정(OQ-4) - [ ] daemon-managed confirmation contract 확정(OQ-4)
- [ ] history response fields 확정(OQ-5) - [ ] history response fields 확정(OQ-5)
- [ ] legacy `PROFIT` mapping policy 확정(OQ-6) - [ ] legacy `PROFIT` mapping policy 확정(OQ-6)
@@ -230,7 +233,7 @@
## DECIDE ## DECIDE
- ~~API shape: 단일 action-dispatch endpoint vs action별 endpoint~~ → 기존 `/ledger/trades` 유지, 신규 `POST /v1/portfolios/{portfolio_id}/ledger/manual` + `action` enum 추가 - ~~API shape: 단일 action-dispatch endpoint vs action별 endpoint~~ → 기존 `/ledger/trades` 유지, 신규 `POST /v1/portfolios/{portfolio_id}/ledger/manual` + `action` enum 추가
- ~~REVERSAL target ledger id/reference 필수 여부~~ → `target_ledger_entry_id` 필수 - ~~REVERSAL target ledger id/reference 필수 여부~~ → `target_ledger_entry_id` 필수
- 부족 cash/quantity 시 validation 거부 vs 결과 음수 허용 - ~~부족 cash/quantity 시 validation 거부 vs 결과 음수 허용~~ → manual write는 부족 cash/quantity/buffer 409 거부, read/display 음수 표시는 허용
- daemon-managed portfolio warning/confirmation을 API-level로 강제할지 UI-level로 둘지 - daemon-managed portfolio warning/confirmation을 API-level로 강제할지 UI-level로 둘지
- history response에 `total_value_after` 포함 여부 - history response에 `total_value_after` 포함 여부
- legacy `PROFIT` action 노출/매핑 정책 - legacy `PROFIT` action 노출/매핑 정책
@@ -246,7 +249,7 @@
# 11. Discovery Freeze # 11. Discovery Freeze
- Handoff Decision: 보류(draft) - Handoff Decision: 보류(draft)
- Handoff Rationale: trex-web dcy-011의 backend-first 후속 Discovery로 생성했다. 현행 portfolio-hub 구조와 action/effect는 확인했고, 기존 `/ledger/trades` 유지 + 신규 `/ledger/manual` 추가 방향은 확정했다. 남은 결정은 validation, daemon-managed confirmation contract, history fields, legacy PROFIT mapping이다. - Handoff Rationale: trex-web dcy-011의 backend-first 후속 Discovery로 생성했다. 현행 portfolio-hub 구조와 action/effect는 확인했고, 기존 `/ledger/trades` 유지 + 신규 `/ledger/manual` 추가 방향은 확정했다. 남은 결정은 daemon-managed confirmation contract, history fields, legacy PROFIT mapping이다.
- Ready for REQ Drafting: false - Ready for REQ Drafting: false
- Confirmed By: 미정 - Confirmed By: 미정
- Confirmed At (KST): 미정 - Confirmed At (KST): 미정