docs: formalize issue 60 proofline specification

This commit is contained in:
Hermes Agent
2026-08-19 13:05:02 +09:00
parent 359ea098ff
commit 4975d7166f
11 changed files with 317 additions and 2 deletions
+67
View File
@@ -0,0 +1,67 @@
---
id: "dcy-0005"
status: confirmed
---
# broker 실행 실패의 기록·상태 수렴 경계
## Problem
`trex-core`의 broker execution 경계는 명시적인 주문 거부, timeout·연결 단절·응답 누락처럼 결과가 불명확한 submission, 취소·부분체결·재시작을 서로 다른 authority로 처리해야 한다. 기존 계약이 이 경계를 충분히 명시하지 않으면 core가 broker failure를 retryability로 분류하거나 새 주문 attempt를 자동 생성하여 중복 주문을 만들 수 있고, strategy/TVR이 KIS 실패와 broker retry 판단을 소유하는 잘못된 결합이 생길 수 있다.
주문 venue도 같은 execution lifecycle에서 의미가 혼재될 위험이 있었다. 누락 venue의 기본값, 명시 `KRX`/`NXT`/`SOR`, 폐기된 `AUTO`, 원주문 routing venue와 실제 KRX/NXT 체결 venue를 구분하지 않으면 wall-clock routing, 취소 시 원주문 venue 손실, ledger/outbox에 실제 거래소 대신 `SOR` 기록, 기존 persisted `AUTO` 및 historical `SOR` row의 잘못된 해석이 발생할 수 있다.
기대 상태는 core가 broker-authoritative evidence로 상태를 수렴하되 자동 새 주문을 만들지 않고, strategy/TVR에는 core slot snapshot만 노출하며, 원주문 venue와 실제 체결 venue를 각각 durable하게 보존하는 것이다.
## Evidence
- 원본 승인 문서: GitHub issue #60 `https://github.com/fourblocks2020/trex-core/issues/60`의 승인된 본문은 명시적 sell rejection, unknown outcome reconciliation, TVR wire 경계, SOR 기본값, AUTO 제거와 자동 retry 금지를 정의한다.
- 연계 승인 계약: GitHub issue #61은 venue admission부터 broker request/ack/cancel과 ledger/outbox까지 explicit venue 보존 경계를 정의한다.
- 구현 진행 원본: issue #60 comment `https://github.com/fourblocks2020/trex-core/issues/60#issuecomment-5336722059`은 기준 revision, 계획, 구현·검증 진행, 두 차례의 독립 QC blocker와 교정 기록을 포함한다.
- 구현 완료 원본: issue #60 comment `https://github.com/fourblocks2020/trex-core/issues/60#issuecomment-5337338182`은 구현 내용, 검증 결과, 로컬 commit 및 push·PR·배포·릴리스·실주문 경계를 기록한다.
- 구현 revision은 `15aeae6930f8c3e6ef9cf659f41278b099a78459`, `7366cadf03fc6002bfd01ecf0cba7c24486bb618`, `359ea098ff717e7f04cf4206dcbc64ad17b47755`이며 현재 local `main` HEAD는 `359ea098ff717e7f04cf4206dcbc64ad17b47755`다.
- `trex_core/application/execution.py`는 명시적 sell 미수락·미체결의 `pending_sell → holding`, 결과 불명확 submission의 durable dispatch sentinel·lookup/reconciliation, fill의 actual venue 확인과 ledger enqueue 경계를 구현한다.
- `trex_core/application/broker_port.py`는 original submission/routing venue를 가진 `OrderAck`와 nullable broker-authoritative `FillStatus.execution_venue`를 분리한다.
- `trex_core/adapters/broker/kis.py`는 주문 요청의 `KRX`/`NXT`/`SOR`를 보존하고 `ORD_EXG_GB` 및 명시 exchange-id allowlist로 실제 KRX/NXT만 파싱하며 `SOR` 단독·unknown을 추론하지 않는다.
- `trex_core/adapters/persistence/sql/023_remove_auto_order_venue.sql`은 persisted `AUTO → SOR` 변환과 이후 AUTO 저장 차단을 수행한다.
- `trex_core/adapters/persistence/sql/024_add_ledger_order_venue.sql`은 original `order_venue`와 nullable actual `venue`를 분리하고 historical SOR actual을 NULL로 보존하며 replay-safe NULL-only backfill과 DB constraint를 적용한다.
- `trex_core/adapters/persistence/postgresql.py`와 `trex_core/application/ledger_port.py`는 outbox에서 `order_venue`와 actual `venue`를 독립적으로 저장·복원한다.
- 구현자 검증은 focused unit `77 passed`, 전체 `1074 passed, 52 skipped`, disposable PostgreSQL 16 `24 passed`, Ruff `All checks passed!`였다.
- 최종 독립 read-only QC는 `359ea098ff717e7f04cf4206dcbc64ad17b47755`에서 blocker/critical/major/minor finding 없이 PASS했다. 실제 PostgreSQL 16에서 historical SOR→`order_venue=SOR`+actual NULL, 정상 actual NXT/order KRX의 replay 보존, actual SOR·AUTO·NULL order 거부를 확인했고 worktree는 clean이었다.
- 현재 `proofline validate`는 exit 0이다.
## Scope
- AC admission은 기존 active AC-0011의 timeout/cancel reconciliation 및 AC-0013의 terminal cancel·partial fill 상태 수렴과 인접하지만, 명시적 submission rejection, strategy wire ownership, venue admission/migration 및 order/execution venue 분리를 독립적으로 검증 가능한 장기 계약으로 추가하는 `create`다. 기존 AC의 의미나 status는 변경하지 않는다.
- KIS 조회로 sell 주문의 미수락·미체결이 명시적으로 확인되면 새 broker attempt 없이 실패 evidence를 기록하고 dispatch를 정리하며 broker-authoritative 보유수량으로 `pending_sell → holding` 전이한다.
- timeout, 연결 단절, 응답 누락 등 submission 결과가 불명확하면 durable sentinel을 유지하고 새 주문을 생성하지 않으며 broker lookup과 reconciliation을 먼저 수행한다.
- core는 broker failure의 retryability, retry schedule, retry budget을 판정하거나 자동 새 broker attempt를 생성하지 않는다. 이후 strategy가 독립적으로 생성한 새 intent는 이 자동 retry 금지와 구분한다.
- strategy/TVR에는 KIS failure, raw broker error, broker attempt, retryability를 노출하지 않는다. strategy/TVR은 core가 수렴한 slot snapshot만 소비한다.
- intent venue 누락은 `SOR`로 정규화하고 명시 `KRX`/`NXT`/`SOR`는 보존한다. `AUTO`는 admission과 persistence에서 거부하고 기존 persisted AUTO는 SOR로 전환하며 wall-clock venue routing을 수행하지 않는다.
- original `order_venue` KRX/NXT/SOR는 submit/ack/slot/restart/cancel에 보존한다. broker-authoritative actual `execution_venue`는 KRX/NXT 또는 미확정 NULL로 별도 취급하고 fill·ledger/outbox에 기록한다.
- KIS actual venue는 검증된 `ORD_EXG_GB`와 명시 exchange-id allowlist에서만 KRX/NXT로 변환한다. `SOR` 단독, 누락 또는 미지원 값은 KRX/NXT로 추론하지 않는다.
- historical outbox의 SOR는 original order venue로 보존하고 actual venue는 NULL로 전환한다. migration 재실행은 신규 정상 order/execution venue 조합을 오염시키지 않아야 하며 DB는 actual SOR와 invalid order venue를 거부한다.
- 관련 focused·전체 회귀, PostgreSQL migration·round-trip, lint와 독립 QC evidence를 구현 상태 평가에 포함한다.
## Out of Scope
- strategy/TVR의 KIS failure 수신, retryability 판단, broker attempt 관리 또는 자동 retry 기능
- TVR 세션·portfolio `markets` 판단 구현과 KRX/NXT session policy 자체
- 실제 KIS 주문·취소, production credential, live RabbitMQ·database·portfolio-hub 연결
- push, PR, 원격 main 반영, image publication, 운영 migration, deployment, GitHub Release 또는 실주문
- ProofLine delivery/release lifecycle이 과거 구현 당시 완료되었다는 소급 주장
## Risks and Unknowns
### Risks
- KIS 응답의 실제 venue 필드가 누락되거나 미지원 코드이면 SOR 주문의 actual venue가 NULL로 남아 ledger enqueue가 fail-closed 될 수 있다. 임의 시간 추론보다 안전하며 운영 관측과 authoritative reconciliation이 필요하다.
- migration runner는 history table 없이 SQL을 재실행하므로 migration 024의 NULL-only backfill과 replay-safe 불변식을 이후 변경에서도 유지해야 한다.
- 환경 의존 KIS·RabbitMQ 검증은 skip되었으므로 실제 운영 외부 시스템 상호작용은 별도 승인 환경에서 확인해야 한다.
### Approval State
- Discovery status: `confirmed`
- 승인 근거: 요청자 genichin이 승인 상태인 issue #60 본문을 최대한 보존하여 템플릿 기반 Discovery를 승인된 상태로 정리하라고 명시했다.
- 추가로 필요한 제품 결정: 없음
- 이 확인은 사후 specification 정리이며 구현·테스트·독립 QC의 실제 evidence는 별도 보존한다. push, PR, 배포, release 또는 실주문 승인을 뜻하지 않는다.
@@ -0,0 +1,49 @@
# GitHub issue #60 comment evidence
- Source: https://github.com/fourblocks2020/trex-core/issues/60#issuecomment-5337338182
- Author: `genichin`
- Created At: `2026-08-19T03:57:52Z`
- Evidence Role: repository-local snapshot of a non-canonical implementation comment
- Canonical Authority: GitHub source comment; this snapshot does not create ProofLine lifecycle status
---
## 구현 완료
### 구현 내용
- 명시적 주문 미수락·미체결 sell 실패를 자동 재주문 없이 `pending_sell → holding`으로 수렴시켰다.
- submission outcome unknown은 durable sentinel과 broker lookup/reconciliation을 우선하고 중복 제출하지 않는다.
- strategy RPC에서 KIS failure·broker attempt·retryability 전달을 제거했다.
- venue 누락은 SOR, 명시 KRX/NXT/SOR는 보존하고 AUTO 및 wall-clock routing을 제거했다.
- 기존 persisted AUTO를 SOR로 전환하는 migration을 추가했다.
- SOR 원주문 venue와 실제 체결 venue를 분리했다.
- slot·재시작·취소: `order_venue` KRX/NXT/SOR
- fill·ledger/outbox actual venue: KRX/NXT, 복원 불가능한 historical SOR는 NULL
- KIS `ORD_EXG_GB` 및 명시 exchange-id allowlist로 실제 KRX/NXT를 파싱하며 SOR/unknown은 추론하지 않는다.
- migration replay가 정상 `(actual=NXT, order=KRX)` 행을 오염시키지 않도록 NULL-only backfill과 DB constraint를 적용했다.
### 실제 검증 결과
- 구현자 전체 회귀: `1074 passed, 52 skipped`
- 구현자 focused unit: `77 passed`
- 구현자 disposable PostgreSQL 16: `24 passed`
- Ruff: `All checks passed!`
- 독립 QC 전체 pytest + PostgreSQL 16 검증: PASS
- 독립 QC finding: blocker/critical/major/minor 모두 없음
- 독립 QC worktree: clean
### 로컬 커밋
- `15aeae6930f8c3e6ef9cf659f41278b099a78459` — broker 실패 수렴·AUTO 제거
- `7366cadf03fc6002bfd01ecf0cba7c24486bb618` — SOR 원주문/실제 체결 venue 분리
- `359ea098ff717e7f04cf4206dcbc64ad17b47755` — actual venue DB constraint 및 replay-safe migration 보강
### 비차단 후속 권고
- migration runner는 history table 없이 SQL 전체를 재실행하므로 현재 migration의 replay-safe 불변식을 계속 유지한다.
- 실제 운영 KIS 응답·RabbitMQ·자격증명 게이트 검증은 별도 승인된 환경에서 수행한다.
### 범위 경계
현재 완료된 것은 **로컬 `main`의 구현·테스트·PostgreSQL 검증·커밋과 독립 QC PASS**다. push, PR, 원격 main 반영, 이미지 게시, 운영 배포, migration 운영 실행, GitHub Release, 실주문은 수행하지 않았으며 완료를 의미하지 않는다.
@@ -0,0 +1,55 @@
# GitHub issue #60 comment evidence
- Source: https://github.com/fourblocks2020/trex-core/issues/60#issuecomment-5336722059
- Author: `genichin`
- Created At: `2026-08-19T02:20:28Z`
- Evidence Role: repository-local snapshot of a non-canonical implementation comment
- Canonical Authority: GitHub source comment; this snapshot does not create ProofLine lifecycle status
---
## 구현 진행 상황
- **대상:** [Discovery] broker 실행 실패의 기록·상태 수렴 경계
- **브랜치:** `main`
- **기준:** `main` (`be98965f2c5d1bad933802282bd7284642203cb8`)
- **상태:** 로컬 main 구현·검증·커밋 및 독립 QC PASS — 미푸시, 미배포
- **최종 업데이트:** 2026-08-19 12:57:49 KST
### 구현 계획 및 작업 목록
- [x] 명시적으로 주문이 거부되고 미체결임이 확인된 매도 주문을 새 주문 없이 보유 상태로 복구한다.
- 주문 배정 정보를 정리하고 기존 보유수량을 유지하는 집중 검증을 통과했다.
- [x] 주문 결과가 불명확한 경우 중복 주문을 만들지 않고 조회·대조 절차를 우선한다.
- 기존 결과불명 sentinel·조회 경로와 재시작 중복 제출 방지 회귀를 확인했다.
- [x] 전략 서비스에 KIS 실패·재시도 판단 정보를 전달하지 않고, core가 자동 주문 재시도를 추가하지 않았음을 검증한다.
- sell 응답의 `retryable` 필드를 제거하고 strategy RPC 응답에 retryability가 없음을 검증했다.
- [x] venue 누락 시 SOR 기본값, 명시 venue 보존, AUTO 제거·기존 데이터 전환 계약과의 교차 회귀를 검증한다.
- 원주문 `order_venue`와 실제 `execution_venue`를 분리하고, KIS allowlist 파싱·outbox 이중 보존·실제 PostgreSQL migration 검증을 완료했다.
- [x] 관련 집중 테스트와 전체 회귀 테스트를 실행하고 결과를 기록한다.
- [x] 변경사항을 검토하고 로컬 main에 커밋한다.
- commits: `15aeae6930f8c3e6ef9cf659f41278b099a78459`, blocker fixes `7366cadf03fc6002bfd01ecf0cba7c24486bb618`, `359ea098ff717e7f04cf4206dcbc64ad17b47755`; push·PR·배포·릴리스·실주문은 수행하지 않았다.
### 진행 기록
- 2026-08-19 11:29:51 KST — 승인된 이슈 본문과 `main` 기준 SHA를 확인하고 별도 worktree·기능 브랜치 없이 구현을 시작했다.
- 2026-08-19 11:29:51 KST — sell 명시적 거부 후 `pending_sell → holding`, dispatch 정리, 후속 자동 재주문 부재를 검증했다.
- 2026-08-19 11:29:51 KST — 결과불명 주문은 durable sentinel과 broker lookup/reconciliation 경로를 유지하며 재제출하지 않는 회귀를 확인했다.
- 2026-08-19 11:29:51 KST — venue 누락→SOR, explicit KRX/NXT/SOR 보존, AUTO admission 거부, wall-clock routing 제거, persisted AUTO→SOR migration을 구현했다.
- 2026-08-19 11:29:51 KST — 집중 테스트 `127 passed`; 전체 회귀 `1062 passed, 51 skipped`; ruff `All checks passed!`.
- 2026-08-19 11:29:51 KST — disposable PostgreSQL 16 실제 엔진 검증 `23 passed`; 검증 컨테이너를 제거했다.
- 2026-08-19 11:29:51 KST — 로컬 main commit `15aeae6930f8c3e6ef9cf659f41278b099a78459` 생성 후 집중 테스트 `127 passed`, ruff 통과, 작업트리 clean을 재확인했다.
- 2026-08-19 11:59:05 KST — 독립 QC 결과 FAIL. KIS adapter가 SOR 요청값을 ack/slot/ledger에 그대로 보존하고 일별 체결조회 row의 실제 KRX/NXT venue를 파싱하지 않아 AC 6을 충족하지 못한다. 수정·독립 재검증 전에는 delivery 검증 완료로 판정하지 않는다.
- 2026-08-19 12:47:57 KST — 승인된 해결책을 적용했다. Slot/취소에는 원주문 KRX/NXT/SOR를 유지하고, KIS fill의 `ORD_EXG_GB` 및 명시 exchange-id allowlist에서 실제 KRX/NXT를 `FillStatus.execution_venue`로 전달한다. outbox는 `order_venue`와 실제 `venue`를 분리 저장하며 SOR 단독·미지원 코드는 실제 거래소로 추론하지 않는다.
- 2026-08-19 12:47:57 KST — 전체 회귀 `1074 passed, 51 skipped`, disposable PostgreSQL 16 검증 `23 passed`, ruff 통과, migration 024 및 outbox round-trip을 확인했다.
- 2026-08-19 12:48:36 KST — blocker 수정 commit `7366cadf03fc6002bfd01ecf0cba7c24486bb618`을 로컬 main에 생성하고 worktree clean을 확인했다. 독립 재QC를 시작했다.
- 2026-08-19 12:54:15 KST — 독립 재QC에서 migration 024의 historical SOR actual 값과 replay-safe backfill 결함을 blocker로 확인했다. `order_venue IS NULL` one-time backfill, historical SOR actual→NULL, actual venue KRX/NXT/NULL DB constraint를 적용했다. 신규 actual NXT + order KRX 행이 replay 후 보존됨을 검증했다.
- 2026-08-19 12:54:15 KST — 수정 commit `359ea098ff717e7f04cf4206dcbc64ad17b47755`; 전체 `1074 passed, 52 skipped`, PostgreSQL 16 `24 passed`, ruff 통과, worktree clean. 독립 재QC를 다시 요청한다.
- 2026-08-19 12:57:49 KST — 독립 재QC PASS. blocker/critical/major/minor finding 없음. 독립 PG16에서 historical SOR→order SOR+actual NULL, 정상 NXT/KRX replay 보존, actual SOR·AUTO·NULL order 거부를 확인했다. HEAD `359ea098ff717e7f04cf4206dcbc64ad17b47755`, worktree clean.
> 각 작업이 실제로 완료되고 검증될 때마다 이 댓글의 체크박스와 진행 기록을 갱신한다. 체크 표시는 구현 완료 또는 해당 검증의 실제 통과를 의미하며, 배포·릴리스 완료를 의미하지 않는다.
+3
View File
@@ -0,0 +1,3 @@
---
id: "line-0005"
---
+61
View File
@@ -0,0 +1,61 @@
---
id: "req-0005"
status: approved
discovery: "dcy-0005"
criteria:
create:
- "ac-0015"
- "ac-0016"
- "ac-0017"
- "ac-0018"
- "ac-0019"
update: []
retire: []
satisfy: []
---
# broker 실행 실패의 기록·상태 수렴 경계
## Objective
`trex-core`가 broker-authoritative evidence를 기준으로 주문 실패·불명확 결과·부분체결·취소 상태를 중복 주문 없이 수렴하게 한다. core가 자동 broker retry를 생성하거나 strategy/TVR에 KIS 실패 판단을 넘기지 않도록 ownership을 제한하고, original order venue와 actual execution venue를 durable하게 분리하여 submit부터 restart·cancel·ledger/outbox까지 의미를 보존한다.
## Scope
- 명시적으로 미수락·미체결이 확인된 sell 주문은 실패 evidence를 기록하고 dispatch를 정리하며 새 broker attempt 없이 broker-authoritative 보유수량으로 `pending_sell → holding` 전이한다.
- submission timeout·연결 단절·응답 누락 등 결과 불명확 상태는 durable sentinel로 보존하고 신규 주문 없이 original order lookup과 reconciliation을 우선한다.
- core는 broker failure의 retryability, retry schedule, retry budget 또는 자동 새 broker attempt를 산출하지 않는다. 이후 별도 strategy intent는 자동 retry와 구분한다.
- strategy/TVR 응답·이벤트에는 KIS failure, raw broker error, broker attempt와 retryability를 노출하지 않는다. strategy/TVR은 core slot snapshot만 소비한다.
- intent venue 누락은 `SOR`로 정규화하고 explicit `KRX`/`NXT`/`SOR`를 그대로 보존한다. `AUTO`는 admission·broker routing·persistence에서 허용하지 않고 persisted AUTO는 SOR로 전환하며 wall-clock routing을 제거한다.
- original `order_venue`는 submit, ack, slot, restart와 cancel에 KRX/NXT/SOR로 보존한다. actual `execution_venue`는 broker-authoritative KRX/NXT 또는 미확정 NULL이며 fill, ledger와 outbox에 별도로 기록한다.
- KIS actual venue는 `ORD_EXG_GB`의 승인된 mapping과 명시 exchange-id allowlist만 사용한다. `SOR`, 누락, 미지원 값과 wall-clock time으로 actual KRX/NXT를 추론하지 않는다.
- historical outbox SOR는 `order_venue=SOR`, actual `venue=NULL`로 보존한다. migration은 NULL-only backfill로 반복 적용이 안전해야 하고 정상 `(actual=NXT, order=KRX)` 같은 조합을 오염시키지 않아야 한다.
- PostgreSQL은 order venue를 KRX/NXT/SOR non-null로 제한하고 actual venue를 KRX/NXT/NULL로 제한한다.
- focused·전체 회귀, actual PostgreSQL migration·round-trip, lint와 독립 read-only QC를 verification evidence로 유지한다.
## Constraints
- broker evidence가 없는 actual venue, 체결, 미수락 또는 terminal state를 추론하지 않는다.
- explicit order venue와 original order date·broker order identity를 restart/cancel 중 변경하지 않는다.
- strategy/TVR에 KIS failure나 retry ownership을 추가하지 않는다.
- live credential·token·connection string·raw sensitive payload를 문서나 외부 wire에 기록하지 않는다.
- existing active AC-0011의 timeout/cancel reconciliation 및 AC-0013의 terminal cancellation·partial fill 계약을 완화하거나 대체하지 않는다.
- Normative Design Documents: 없음. 이 REQ와 active AC가 normative specification을 소유한다.
### Current Implementation Assessment
- 이 specification은 승인된 GitHub issue #60과 연결된 #61을 이미 local `main`에서 구현·검증한 결과를 사후 정리한다. 과거 구현이 ProofLine delivery chronology를 따랐다는 뜻은 아니다.
- 구현 revision은 `15aeae6930f8c3e6ef9cf659f41278b099a78459`, `7366cadf03fc6002bfd01ecf0cba7c24486bb618`, `359ea098ff717e7f04cf4206dcbc64ad17b47755`다.
- 현재 구현은 명시적 sell rejection 수렴, unknown submission sentinel·lookup, strategy wire retryability 제거, SOR default·explicit venue 보존·AUTO 제거, KIS actual venue parser와 order/execution venue 분리, replay-safe migration·DB constraint를 포함한다.
- 구현자 검증은 focused `77 passed`, 전체 `1074 passed, 52 skipped`, disposable PostgreSQL 16 `24 passed`, Ruff PASS다.
- 최종 독립 read-only QC는 actual PostgreSQL 16 probe를 포함하여 blocker/critical/major/minor finding 없이 PASS했으며 HEAD `359ea098ff717e7f04cf4206dcbc64ad17b47755`와 clean worktree를 확인했다.
- 환경 의존 KIS·RabbitMQ 검증은 skip되었고 실제 외부 system interaction은 수행하지 않았다.
- `approved` REQ와 `active` AC는 current specification authority 및 현재 implementation assessment를 나타낸다. push, PR, 원격 main 반영, deployment, release 또는 운영 주문 완료를 뜻하지 않는다.
## Non-Goals
- strategy/TVR의 KIS failure 수신, retryability 계산, broker retry schedule·budget 또는 자동 재주문
- TVR session·portfolio `markets` gate와 거래 세션 정책 구현
- live KIS 주문·취소, 운영 broker/account/database/RabbitMQ/portfolio-hub 연결
- push, PR, remote main merge, image publication, production migration, deployment, GitHub Release 또는 실주문
- ProofLine delivery·release lifecycle closure의 소급 주장