Skip to content

feat: Phase 1 final create POST 활성화 (디폴트 OFF 가드 유지)#1

Closed
pghoya2956 wants to merge 7 commits into
mainfrom
feat/phase1-final-create
Closed

feat: Phase 1 final create POST 활성화 (디폴트 OFF 가드 유지)#1
pghoya2956 wants to merge 7 commits into
mainfrom
feat/phase1-final-create

Conversation

@pghoya2956

Copy link
Copy Markdown
Owner

Summary

place-orderdaemon.py 봉인을 풀어 토스 broker create POST 호출이 가능해진다. live 활성화는 TOSS_BRIDGE_ENABLE_FINAL_SUBMIT=1 명시 enable 시에만 — 디폴트 OFF 영구 유지 (semver minor: 0.2.0).

financier-v2 의 Phase 1 task plan P1-01 ~ P1-06 의 결과물.

Phase 0 P0-02 discovery 요약

토스 정식 web 앱에서 supervised HAR capture 2건으로 final create endpoint 확정:

  • URL: `POST https://wts-cert-api.tossinvest.com/api/v2/wts/trading/order/create\`
  • body schema: prepare body − `withOrderKey` (12 필드)
  • orderKey 흐름: cookie/session 기반 (body 미포함)
  • broker_ack 매핑: `orderId, orderDate, orderNo, message, isReserved`
  • 결정적 발견: 토스 미국주식 "시장가" UI = client-level 추상화. API 는 `orderPriceType="00"` 단일 → spec 가정 일부 무효 (Phase 2 P2-02 영향)

변경 내역

P1-01 — `daemon.py:_run_prepare_preflight` 봉인 제거 + broker create skeleton (05c15e7)

`prepare preflight succeeded; final create remains blocked` 봉인 raise 제거. dict return 9필드 확장 (account_no, prepare_payload 등) → `place_order` 호출자에 `_final_submit_enabled` 가드 분기 추가:

  • OFF (디폴트): 기존 동작 유지 — submit_blocked + capability_not_ready raise + journal append. 메시지만 `final submit is disabled ({guard_reason})` 로 정정 + `broker_ack.guard_reason` 필드
  • ON: `_run_broker_create(normalized, preflight)` 호출 → broker_ack 분기 → `submit_state="submitted"|"broker_rejected"` + journal append + 정상 dict return

`_run_broker_create` skeleton 신규 (92줄).

P1-02 — reject taxonomy enum + 분기 정밀화 (4bfe3fe)

  • enum 9개 + `BROKER_ACK_REJECT_CODES` frozenset
  • `classify_broker_reject(message, status_code, error)` 함수 — error → TIMEOUT, 408/504 → TIMEOUT, 401/403 → AUTH_REQUIRED, ≥500 → HTTP_ERROR, fallback UNKNOWN
  • `_run_broker_create` timeout (`RuntimeError`) try/except 추가 + `status_code` 필드명 bug fix (P1-01 의 `status` 오타)
  • 한글 message → 의미 enum 매핑은 P0-03 backlog (Phase 6 supervised 자연 capture 후 점진 추가)

P1-03 — `BROKER_ACK_ALLOWED_FIELDS` whitelist 갱신 (47d0314)

9필드 → 15필드. 신규: `broker_order_id, order_no, order_date, is_reserved, http_status, guard_reason`. sanitize 보존 검증 단위 테스트 2건 추가 (P0-02 SCHD capture 데이터 그대로).

P1-04 — auto-verify aggregator hook + `--auto-verify` CLI flag (17a95a3)

`place_order` submit success 직후 `auto_verify=True` 일 때 `runtime.verify_order` 1회 호출 → response.data 에 verification_state + verify_snapshot 합침. verify 실패 시 `auto_verify_error` 만 추가하고 ok=True 유지 (submit boundary 보존).

디폴트 OFF — financier-v2 SKILL 측 60s 폴링과 중복 회피.

P1-05 — 단위 테스트 5건 (cc3a709)

`_fetch_many` mock 으로 P0-02 capture 응답 시뮬레이션:

  • `classify_broker_reject` 7개 status_code 분기 enum 매핑
  • `place_order` success path: orderId 존재 시 broker_ack 6필드 보존 (V56qyv7r/orderNo=3/2026-05-04)
  • missing orderId → BROKER_REJECTED_UNKNOWN
  • RuntimeError timeout → BROKER_REJECTED_TIMEOUT + http_status=0
  • auto_verify=True + submitted → verify_snapshot 합쳐짐

P1-06 — `health` 응답 `bridge_version` 필드 추가 (0353d38)

`importlib.metadata.version("toss-browser-bridge")` 로 패키지 메타데이터에서 version 추출 → `BRIDGE_VERSION` 모듈 상수 캐시 → `health()` 응답 top-level 노출. financier-v2 wrapper(P4-06) startup minimum pin 검증의 baseline.

3중 동기 검증 단위 테스트 1건 추가.

chore — version 0.1.0 → 0.2.0 (b1e2e42)

semver minor — 신규 가드된 write 기능.

정책 박제

  • `TOSS_BRIDGE_ENABLE_FINAL_SUBMIT` 디폴트 OFF 영구 유지 — supervised live validation 통과 후에도 디폴트 ON 전환 안 함. 매 사용자 세션마다 명시 enable 필요
  • `PYTEST_CURRENT_TEST` 환경에서 추가 가드 — `TOSS_BRIDGE_ALLOW_TEST_FINAL_SUBMIT=1` 도 함께 set 해야 봉인 우회. 단위 테스트 fixture 외에는 활성화 차단
  • live e2e 봉인 유지 — `tests/test_live_e2e.py` 의 final submit 테스트는 그대로 skipped/봉인 유지. supervised P6 단계까지

RQ-01 idempotency 결정

broker create 의 idempotency 보장 여부는 토스 측에서 미문서. 본 PR 에서는 재시도 X (보수적) 채택:

  • `_fetch_many` RuntimeError timeout → 즉시 `BROKER_REJECTED_TIMEOUT` 반환, 자동 재시도 안 함
  • 동일 `mutation_id` 재발행은 wrapper / SKILL 레이어에서 차단

Phase 6 P6-05 supervised 단계에서 1회 재시도 실측으로 룰 보강 예정.

단위 테스트 결과

  • `test_submit_runtime.py`: 17 PASS (12 → 17, 5건 신규)
  • `test_submit_contract.py`: 11 PASS (9 → 11, 2건 신규)
  • `test_packaging_metadata.py`: 3 PASS (2 → 3, 1건 신규)
  • 전체: 70 PASS / 1 skipped / 1 무관 fail (`bridge_lib.PORT` 환경 분리, 본 PR 무관)

live e2e 봉인 유지

`tests/test_live_e2e.py` 의 final submit 테스트는 `PYTEST_CURRENT_TEST + TOSS_BRIDGE_ENABLE_FINAL_SUBMIT=1 + TOSS_BRIDGE_ALLOW_TEST_FINAL_SUBMIT 미설정` 조합으로 `blocked_in_pytest` 가드. supervised P6 진입 전까지 라이브 호출 발생 X.

Test plan

  • 단위 테스트 70 PASS / 1 무관 fail
  • live e2e `blocked_in_pytest` 가드 동작 확인 (`test_final_submit_stays_blocked_under_pytest_even_if_env_requests_enable`)
  • OFF 분기 디폴트 동작 무영향 확인 (기존 `test_place_order_returns_capability_not_ready_until_verify_is_implemented` PASS)
  • BRIDGE_VERSION 3중 동기 (pyproject ↔ `version` ↔ `daemon.BRIDGE_VERSION`) 검증
  • supervised P6 — financier-v2 측 wrapper PR 머지 + 사용자 PC + 정규장 시간대에 4건 supervised live (limit buy / limit sell / market sell / fx KRW→USD)

🤖 Generated with Claude Code

heeho and others added 7 commits May 5, 2026 00:29
`_run_prepare_preflight` 끝의 dict return 을 9필드(account_no, prepare_payload,
prepared_order_info 등)로 확장하여 broker create 호출에 필요한 정보를
호출자에 전달. `place_order` 호출자에 `_final_submit_enabled` 가드 분기를
추가하여 OFF 시 기존 동작(submit_blocked + capability_not_ready raise) 유지,
ON 시 신규 `_run_broker_create` 호출. broker create skeleton 은
P0-02 capture 로 확정한 `POST /api/v2/wts/trading/order/create` 를
prepare_payload − {"withOrderKey"} body 로 호출, 응답 `result.orderId`
존재 시 submitted, 부재 시 broker_rejected (P1-02 에서 reject taxonomy 정밀
매핑 예정) 분기.

baseline 단위 테스트 12건 PASS (1건 expected message regex 만 갱신).
broker create POST 는 `TOSS_BRIDGE_ENABLE_FINAL_SUBMIT` 명시 enable
시점 (Phase 6 supervised) 까지 발생하지 않음.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
`BROKER_ACK_OK` + `BROKER_REJECTED_*` 8건 (`_INSUFFICIENT_BALANCE`,
`_INSUFFICIENT_QUANTITY`, `_INVALID_PRICE`, `_MARKET_CLOSED`,
`_AUTH_REQUIRED`, `_DUPLICATE_ORDER`, `_TIMEOUT`, `_HTTP_ERROR`,
`_UNKNOWN`) 모듈-레벨 enum + `BROKER_ACK_REJECT_CODES` frozenset 정의.
`classify_broker_reject(message, status_code, error)` 함수 신규 — error
존재 → TIMEOUT, status_code 408/504 → TIMEOUT, 401/403 → AUTH_REQUIRED,
≥500 → HTTP_ERROR, fallback UNKNOWN. 한글 message → 의미 enum 매핑은
P0-03 backlog (Phase 6 supervised 자연 capture 후 점진 추가).

`_run_broker_create` 분기 정밀화: P1-01 의 `result.get("status")` 필드명
오타 수정 (실제 `_fetch_many` 응답은 `status_code`), timeout RuntimeError
try/except 추가, reject 분기에서 `classify_broker_reject` 결과 사용.

baseline 12 tests PASS.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
submit.py 의 broker_ack sanitize 화이트리스트를 9필드에서 15필드로 확장.
신규: broker_order_id, order_no, order_date, is_reserved (P0-02 capture
시 토스 broker create 응답 필드), http_status (P1-01 응답 status_code),
guard_reason (P1-01 OFF 분기 final_submit_guard_reason).

단위 테스트 2건 추가: 신규 6필드 sanitize 보존 검증 (test 데이터는
P0-02 SCHD capture 응답 그대로 — orderId="V56qyv7r", orderNo=3,
orderDate="2026-05-04") + OFF 분기 guard_reason 보존 검증.

전체 23 tests PASS (기존 forbidden field drop 동작 무영향).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
place_order submit_state="submitted" 직후 normalized.auto_verify=True 일 때
self.verify_order({"mutation_id": mutation_id}) 1회 호출 → response.data 에
verification_state + verify_snapshot 추가. verify 실패(MutationDomainError)
시에는 data.auto_verify_error = {code, message} 만 추가하고 ok=True 유지
(submit 은 이미 성공했으므로 boundary).

validate_place_order_params 가 params.auto_verify (bool, 디폴트 False)
를 수용하여 normalized dict 에 포함. CLI 측 place-order --auto-verify
flag 추가 (action_store_true).

디폴트 OFF — financier-v2 SKILL 측 60s verify 폴링과 중복 회피.
27 tests PASS (test_submit_runtime + test_submit_contract + test_cli_bootstrap).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
`_fetch_many` mock 패턴으로 P0-02 capture 응답 시뮬레이션 추가:
- classify_broker_reject 7개 status_code 분기 enum 매핑 검증
- place_order success path: orderId 존재 시 submit_state=submitted +
  broker_ack 의 broker_order_id/order_no/order_date/is_reserved/http_status
  6필드 보존 (P0-02 SCHD capture 데이터: V56qyv7r/orderNo=3/2026-05-04)
- place_order missing orderId: 200 OK 인데 orderId 부재 → broker_rejected
  + BROKER_REJECTED_UNKNOWN
- place_order RuntimeError timeout: _fetch_many raise → broker_rejected
  + BROKER_REJECTED_TIMEOUT + http_status=0
- auto_verify=True + submitted: runtime.verify_order 1회 호출 +
  verify_snapshot/verification_state 가 place_order 응답.data 에 합쳐짐

helper 신규: _enable_final_submit (env 두 개 set), _stub_place_order_dependencies
(order_preview + _run_prepare_preflight mock + MUTATION_JOURNAL_FILE patch).

test_submit_runtime.py 17 PASS (12→17). 전체 스위트 70 PASS / 1 skipped /
1 무관 fail (bridge_lib.PORT 환경 분리, P1-01부터 일관).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
importlib.metadata.version("toss-browser-bridge") 로 패키지 메타데이터에서
version 추출 → BRIDGE_VERSION 모듈 상수에 캐시 → health() 응답 top-level
에 노출. PackageNotFoundError 시 "0.0.0+unknown" fallback.

financier-v2 wrapper(Phase 4 P4-06) 가 startup 시 minimum 버전 검증을
할 수 있도록 bridge 측 자기 버전 노출. SSOT 는 pyproject.toml,
__init__.py.__version__ + daemon.BRIDGE_VERSION 모두 거기서 파생.

3중 동기 검증 단위 테스트 1건 추가 (BRIDGE_VERSION == __version__).
영향받는 23 tests PASS. live e2e 봉인은 그대로 유지.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
P1-01 ~ P1-06 으로 broker create POST 활성화 가능 (디폴트 OFF). semver
minor 의미 충족 — 신규 가드된 write 기능. pyproject.toml +
__init__.py.__version__ 동기 갱신, daemon.BRIDGE_VERSION 은
importlib.metadata 로 자동 추종.

3중 동기 테스트 (test_packaging_metadata) 3건 모두 PASS.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@pghoya2956

Copy link
Copy Markdown
Owner Author

Already merged via 53fd337 (502 race during initial gh pr merge, but commit went through)

@pghoya2956 pghoya2956 closed this May 4, 2026
@pghoya2956 pghoya2956 deleted the feat/phase1-final-create branch May 4, 2026 16:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant