Deliver BAT-002: slot-core daemon + HTTP intent API + PostgreSQL

Implement REQ-005~009 (dcy-002 slice 1) as an onion stack on top of the
BAT-001 pure domain:
- application: IntentService (admission + single-writer transition +
  intent_id idempotency via durable intent_log)
- adapters/persistence: PostgreSQLSlotRepository + PostgreSQLIntentStore +
  trex-core-owned migrations (daily_slot per Appendix A, daily_symbol_entries,
  slot_transition_log, intent_log) + in-memory intent store
- adapters/api: FastAPI /v1 (healthz, intents/buy|sell|{id}, slots, capacity)
- runtime: trex-core-daemon entrypoint with lifespan migrate+preallocate

Verification (against local PostgreSQL 18): 74 passed, ruff clean, domain
import sweep shows zero web/db deps (NFR-1), 20-thread concurrency admits
exactly the idle count (NFR-2), idempotent resubmit causes no double
transition (NFR-3), EXPECTED_SLOT_FIELDS<->information_schema drift test
(realizes dcy-001 OQ-2's deferred DB-side check), and a daemon E2E smoke.

Align all four baseline docs to the new daemon/API/persistence layers.
BAT-002 -> release-candidate; REQ-005~009 -> Implemented. Full async outbox,
broker execution, safety floor, and strategy daemons remain deferred to a
follow-up Discovery.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-04 13:46:08 +09:00
co-authored by Claude Opus 4.8
parent a44f9fddb7
commit a4e263c8e7
41 changed files with 1778 additions and 38 deletions
+27 -4
View File
@@ -4,9 +4,9 @@
- Baseline Seed: .stage-pilot/bootstrap/baseline.yaml
- Status: draft
- Owner: project maintainers
- Last Updated (KST): 2026-06-03
- Source Discovery / Batch: bootstrap-baseline (pre-Discovery)
- Primary Runtime: library
- Last Updated (KST): 2026-06-03 (BAT-002)
- Source Discovery / Batch: bootstrap-baseline (pre-Discovery); HTTP API = dcy-002 / BAT-002
- Primary Runtime: daemon (HTTP API) + library (slot 도메인)
## Project Summary
- trex 제품군의 공용 베이스로, 첫 도메인으로 trex-orbit 의 일일 거래 슬롯 생명주기(slot) 순수 도메인을 storage-agnostic 하게 이식한 코어다.
@@ -19,15 +19,37 @@
- It is a cross-cutting reference for REQ drafting, batch design, implementation, verification, and handoff.
## Scope
- Covers in-process domain-service APIs and the persistence boundary Protocol (library 형태라 외부 HTTP/CLI 표면은 아직 없음).
- Covers the HTTP `/v1` API(슬롯 코어 데몬, BAT-002), in-process domain-service APIs, and the persistence boundary Protocol.
- Does not duplicate full business requirements already owned by REQ documents.
- Does not replace low-level code references or generated API artifacts.
## Interface Summary
- HTTP `/v1` API :: 슬롯 코어 데몬이 노출, 매수·매도 전략 데몬이 호출(REQ-005/007/008)
- SlotService :: 슬롯 생명주기 도메인 서비스(전이/사전할당/가용성/재진입 카운트)
- SlotRepository (Protocol) :: 도메인 ↔ 저장소 경계 계약
## Interface: `HTTP /v1` (슬롯 코어 데몬)
- Type: http-api
- Consumers / Producers: 매수·매도 전략 데몬(클라이언트) → trex-core 슬롯 코어 데몬(서버). FastAPI+uvicorn.
- Purpose: 전략이 intent 를 제출하고 holding 스냅샷/capacity 를 조회. 코어가 단독 admission + 슬롯 전이(단일 writer).
- Stability: experimental (v1, 슬라이스 1)
| 엔드포인트 | 입력 | 출력 | 에러/거부 |
|---|---|---|---|
| `GET /v1/healthz` | — | `{status:"ok"}` 200 | — |
| `POST /v1/intents/buy` | intent_id, trading_date, symbol, qty?, allocated_capital?, entry_price?, strategy_id?, sell_strategy_id?, portfolio_id?, forced_close_at? | `{intent_id, status:admitted|rejected, slot_id?, reason?}` | reject: `no_idle_slot`, `idle_slot_contended` |
| `POST /v1/intents/sell` | intent_id, slot_id, reason?, sell_strategy_id? | `{intent_id, status, slot_id?, reason?}` | reject: `slot_not_found`, `slot_not_holding:<state>`, `slot_state_changed` |
| `GET /v1/intents/{intent_id}` | path intent_id | `{intent_id, status, slot_id?, reason?}` | 404 intent not found |
| `GET /v1/slots?status=&trading_date=` | query | `[SlotSnapshot]` | — |
| `GET /v1/capacity?trading_date=&total_slots=` | query | `{idle_count, holding_count, pending_count, total_slots, scarcity_ratio}` | — |
### Compatibility / Invariants
- 멱등: `intent_id` dedup(intent_log UNIQUE gate). at-least-once 재전송에도 슬롯 1회만 전이; 재조회는 `GET /v1/intents/{id}`.
- 단일 writer: 모든 슬롯 쓰기는 코어의 SlotService(advisory lock)만. 전략은 제안만, 슬롯/브로커 직접 접근 금지.
- 동기 커밋: 수락 시 admission+전이를 동기 커밋 후 admit/reject 응답(완전 비동기 outbox 는 브로커 Discovery Deferred).
## Interface: `SlotService`
- Type: internal-service
@@ -111,3 +133,4 @@
## Change Log
- 2026-06-03 :: bootstrap-baseline 으로 초기 인터페이스 계약 baseline 생성(SlotService + SlotRepository Protocol).
- 2026-06-03 :: BAT-002 — HTTP `/v1` API 계약(데몬, 6 엔드포인트) + 멱등/단일writer 불변식 추가(REQ-007/008/009).