codex 교차 리뷰 후속 티켓 DAG

배경

wave 2(머지 완료 8티켓)와 wave 3(미머지 3티켓)을 codex 런타임(gpt-5.6-sol, effort=high)으로 재검수한 결과다. 기존 리뷰는 전부 Claude 런타임이었고, roles.jsoncross-review 프로파일이 review.code를 codex로 라우팅하는데 그 경로를 타지 않았다.

11티켓 전량 REQUEST_CHANGES. p0 24건 · p1 39건. Claude 리뷰가 COMMENT로 통과시켜 머지한 8티켓에서 p0 12건이 나왔다.

반복된 실패 패턴 — 질문의 강도

Claude가 검증한 질문실제 필요한 질문결과
유니크 제약을 깨뜨리는가?멱등성을 제공하는가?매도 중복 반영 (BE-05)
결과가 유효한 호가인가?결과가 3호가인가?500,000원에서 1,500원 오차 (BE-08)
cost_model이 필수 인자인가?프로덕션이 그 함수를 부르는가?백테스트가 비용 미반영 (BE-06)
구현이 티켓과 일치하는가?구현이 스펙과 일치하는가?OCO leg 반전 (BE-08, 3차에서 발견)
호출부가 있는가?다음 wave에서 생기는가?킬 스위치 덮어쓰기 (BE-02)

다섯 번 다 사실 인식은 같았고 질문의 강도만 달랐다. 약한 질문에 참이면 통과시켰다. 리뷰 프롬프트가 약한 질문을 유도한 경우도 있다.

롤백하지 않는 근거

정책 기본값이 PAPER이고 LiveTradeExecutor 호출부가 아직 없어 실주문 경로가 실행되지 않는다. 다만 아래 2건은 PAPER에서도 판정이 틀어지므로 최우선이다.

결함PAPER 영향
TradingDay 낙관적 락 부재 → 손익 유실일일 손실 한도 판정이 무력화돼 모의 주문이 한도를 넘어 계속된다
정책 merge가 킬 스위치를 덮어씀킬 스위치를 켜도 되살아난다 — 최후 정지 수단이 작동하지 않는다

Wave 너비 분포

Wave너비티켓성격
A1DB-02스키마 선행 병목 (연관 병목 일괄)
B6BE-27, BE-28, BE-29, BE-30, BE-31, BE-32스키마 무관 — 즉시 병렬
C4BE-23, BE-24, BE-25, BE-26DB-02 의존
D2BE-33, BE-34후행 정리·경계

총 13티켓. 직선형 DAG가 아니다 — wave B가 6폭, wave C가 4폭으로 벌어진다.

wave A와 wave B는 서로 독립이므로 동시 착수 가능하다 — DB-02가 wave B를 막지 않는다.

flowchart LR
    DB02[DB-02 스키마] --> WC[wave C 4폭]
    WB[wave B 6폭 스키마무관]
    WB --> WD[wave D 2폭]
    WC --> WD

기존 후속 티켓 흡수

wave 2·3 Claude 리뷰에서 만든 BE-18~22를 이번 분해에 흡수한다 — 같은 파일을 건드려 그대로 두면 Single Writer 충돌이다.

기존처리
BE-18 RiskTier.totalExposureRatioBE-30 에 흡수 (게이트 입력 불변식과 같은 파일)
BE-19 일반 주문 호가 정렬유지 · BE-27 후행으로 강등 (KoreanPriceTick 수정이 선행)
BE-20 OCO 멱등키 + ymlBE-28(멱등키) · BE-32(yml) 로 분할 흡수
BE-21 마감 OCO 유효성 점검BE-29 에 흡수 (조회 API 연결이 전제)
BE-22 단계 전환 이력DB-02(스키마) · BE-25(기록 로직) 로 분할 흡수

Single Writer per File 검증

Wave A

티켓수정 파일 범위
DB-02backend/src/main/resources/db/migration/V*__autotrading_concurrency_and_ledger.sql (신규)

Wave B (교집합 ∅)

티켓수정 파일 범위
BE-27order/domain/KoreanPriceTick.kt + 그 테스트
BE-28order/domain/OcoOrderSpec.kt·PlaceOrderSpec.kt, order/infrastructure/toss/TossOrderDtos.kt·TossOrderGatewayImpl.kt, order/infrastructure/toss/TossConditionalOrderDtos.kt + 그 테스트
BE-29order/domain/ConditionalOrderGateway.kt, order/infrastructure/toss/TossConditionalOrderGatewayImpl.kt, autotrading/domain/PositionProtectionDomainService.kt(신규), autotrading/application/VerifyPositionProtectionUseCase.kt(신규) + 그 테스트
BE-30autotrading/domain/RiskGate.kt·RiskTier.kt·TradeCandidate.kt + 그 테스트
BE-31ml/app/main.py·backtest.py·cost_model.py, autotrading/domain/TradingCostModel.kt, contracts/autotrading/*, .githooks/pre-push + 그 테스트
BE-32autotrading/infrastructure/notification/DiscordAutoTradingAlertGateway.kt, backend/src/main/resources/application.yml + 그 테스트

교집합 ∅ ✓ — order/infrastructure/toss/TossConditionalOrderDtos.kt는 BE-28(요청 DTO에 멱등키 추가)만, TossConditionalOrderGatewayImpl.kt는 BE-29(조회 연결)만 소유한다. 두 티켓이 같은 패키지를 건드리므로 착수 전 담당자 간 파일 경계를 재확인한다.

BE-32 주의: application.yml은 BE-16(wave 5)도 소유한다. BE-16 머지 이후 착수하거나, BE-16에 yml 키 추가를 위임하고 BE-32는 코드만 소유한다.

Wave C (교집합 ∅)

티켓수정 파일 범위
BE-23autotrading/domain/AutoTradingPolicy.kt·OrderProposal.kt, autotrading/infrastructure/mysql/AutoTradingPolicy*·OrderProposal* + 그 테스트
BE-24autotrading/domain/TradingDay.kt·TradingDayRepository.kt·TradingSettlementDomainService.kt, autotrading/infrastructure/mysql/TradingDay* + 그 테스트
BE-25autotrading/domain/RiskTierPromotionDomainService.kt·RiskTierTransition.kt(신규)·RiskTierTransitionRepository.kt(신규), autotrading/infrastructure/mysql/RiskTierTransition* + 그 테스트
BE-26autotrading/domain/ExecutionFailure.kt(신규)·ExecutionFailureRepository.kt(신규), autotrading/infrastructure/mysql/ExecutionFailure* + 그 테스트

교집합 ∅ ✓ — BE-23이 정책·제안, BE-24가 거래일을 각각 단독 소유한다. 둘을 합치지 않은 이유는 파일이 갈리고 동시 진행이 가능하기 때문이다.

Wave D

티켓수정 파일 범위
BE-33Rich Domain 질의 메서드 추가 + 복원 팩토리 관용구 통일 — wave C 산출물 위에서 수행
BE-34autotrading/domain/AutoTradingDomainService.kt, autotrading/application/RunAutoTradingCycleUseCase.kt — BE-11·BE-13 걸침

wave 3(미머지)의 처리 — 티켓이 아니라 in-place

BE-10·BE-11·BE-12는 아직 머지되지 않았다. p0를 안고 머지하지 않는다 — 브랜치에서 직접 고친다. 예외는 하나다.

티켓p0처리
BE-105in-place 수정 (멱등키는 BE-28 계약을 소비)
BE-117in-place 수정. 단 트랜잭션 경계·복구 가능 실패 상태는 BE-34 로 분리 — BE-13(미착수)과 함께 설계해야 하고 BE-11 단독으로 닫히지 않는다
BE-120 (p1 11)in-place 수정. 사다리 판정 창·이력은 BE-25 로 이관

BE-11 브랜치는 현재 단독 checkout 에서 컴파일되지 않는다TradeExecutor·OpenedPosition·TradeExecutionException 소스가 BE-10 소유라 제외했기 때문이다. 그 브랜치에 남은 테스트 결과 XML은 소스 삭제 전 생성된 stale class 기반이므로 검증 근거로 쓰지 않는다. 유효한 검증은 BE-10 을 포함한 통합 브랜치 결과(294 tests, 0 failures)뿐이다.

공용 테스트 헬퍼 선점 소유

이번 분해에서 새로 필요한 헬퍼는 wave A·B 의 첫 티켓이 소유한다. 후행 wave 는 소비만 한다.

헬퍼소유판정 기준·근거
LostUpdateProbe (실 MySQL 2트랜잭션 동시 갱신)BE-23낙관적 락 검증에 Fake 는 무의미하다 — codex 가 “Fake 가 최신 객체를 그대로 반환해 격리를 검증하지 않는다”고 지적한 지점이다. Testcontainers + 스레드 2개로 실제 lost update 를 재현한다
KoreanPriceTickLadder (호가 사다리 기대값 생성)BE-27기대값을 손으로 박지 않는다 — 현재 테스트가 잘못된 값(2000→1985)을 기대값으로 고정해 버그를 승인했다. 사다리를 한 칸씩 내려가는 독립 구현으로 기대값을 만들어 대조한다
CostModelParityFixture기존 BE-06 소유 유지BE-31 이 확장한다 (프로덕션 경로 연결 검증 추가)

검증 게이트

  • 모든 티켓의 리뷰는 review.code = codex 로 수행한다. harness run 경로(cross-review 프로파일)를 쓰거나, codex CLI 를 직접 호출한다. Claude 단일 런타임 리뷰로 wave 를 닫지 않는다.
  • 리뷰 프롬프트에 “테스트가 하는 주장이 실제로 성립하는가” 를 명시 항목으로 넣는다. 이번 재검수에서 항상-참 테스트·stale class 테스트·잘못된 기대값 테스트가 각각 발견됐다.
  • 외부 API 계약은 스펙 원문과 대조한다 (curl -s https://openapi.tossinvest.com/openapi-docs/latest/openapi.json). 티켓·ADR 은 2차 자료다.

Document History

날짜변경 내용
2026-08-28codex 교차 리뷰 결과로 신규 작성. BE-18~22 흡수