feat(multi-instance): dcy-009 BAT-013 — RabbitMQ 다중 인스턴스 토폴로지 (REQ-048~051)

dcy-009(다중 인스턴스 RabbitMQ 토폴로지) 전체 SDLC: draft-req → confirm-req →
draft-batch(BAT-013) → run-batch-delivery → confirm-req-implemented.

- REQ-048 (Configuration): DaemonInstanceConfig.from_toml + DaemonConfig.from_config
  (env 병합, portfolio_hub_token 주입) + 데몬 --config 필수 argparse + .gitignore 인스턴스
  TOML + config/instance.example.toml
- REQ-049 (Interface): topology 동적 큐명 빌더 intent/snapshot/control_queue(instance_key)
  — default=리터럴 큐명(하위호환), named=`{base}.{instance_key}` + daemon per-instance 구독
- REQ-050 (Integration): trex.direct(topic)/trex.broadcast(fanout) exchange +
  declare_topology(instance_key) per-instance binding + control dual binding + direct_routing_key
- REQ-051 (Documentation): interface-contract/runtime-flows/project-structure 정합

하위호환: instance_key="default" → 기존 리터럴 큐명·RPC 경로 무회귀(FR-5).
split-brain 방지(NFR-3): 인스턴스 전용 큐. 민감값(NFR-4): TOML + gitignore.

검증: 신규 9 단위 + RabbitMQ 라우팅 2, 단위 271 passed,
인프라 게이트 301 passed(directed 격리 S-2 + broadcast dual S-3 + default 무회귀 S-4),
ruff clean. REQ-048~051 Implemented, BAT-013 release-candidate.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-07 15:10:36 +09:00
co-authored by Claude Opus 4.8
parent 7e23648489
commit 9f83c64a40
24 changed files with 1223 additions and 85 deletions
@@ -0,0 +1,51 @@
# BAT-013 Design
- Status: confirmed
- Batch: BAT-013
## Architecture Summary
다중 인스턴스가 동일 RabbitMQ 에서 split-brain 없이 운영되도록 토폴로지·설정을 인스턴스화한다.
1. **동적 큐명**(`topology.py`): `intent_queue/snapshot_queue/control_queue(instance_key)`. `instance_key in (None,"default")` → 기존 리터럴(`trex.intent.req` 등), 그 외 → `{base}.{instance_key}`. 기존 상수는 default 값으로 보존(하위호환).
2. **exchange**(`topology.py`+`rabbitmq.py`): `trex.direct`(topic,durable) + `trex.broadcast`(fanout,durable). `declare_topology(instance_key)` 가 인스턴스 큐를 direct 에 `{instance_key}.{intent|control|snapshot}` 로 바인딩 + control 큐를 broadcast 에 dual 바인딩.
3. **설정**(`config.py`): `DaemonInstanceConfig(instance_key, portfolio_hub_token)` + `from_toml(path)`(tomllib) + `DaemonConfig.from_config(toml_path)` 가 `from_env()` 결과에 TOML 을 병합(`portfolio_hub_token` → PortfolioHubConfig.token).
4. **진입점**(`daemon.main`): argparse `--config` 필수. instance_key 를 declare/consume 에 일관 전달.
## Changed Areas
- Docs: interface-contract.md(routing key)·runtime-flows.md(TOML 기동/라우팅)·project-structure.md(동적 큐명/설정/exchange) — REQ-051.
- Modules:
- `trex_core/adapters/messaging/topology.py` — 큐명 빌더 + DIRECT/BROADCAST exchange 상수 + `direct_routing_key`
- `trex_core/adapters/messaging/rabbitmq.py` — `declare_topology(instance_key="default")` + exchange 선언 + binding(direct per-instance + control dual) + (필요시) broadcast publish helper
- `trex_core/runtime/config.py` — `DaemonInstanceConfig` + `from_toml` + `from_config`
- `trex_core/runtime/daemon.py` — `--config` argparse(main) + instance_key 보관 + consumer 큐명에 instance_key 적용 + declare_topology(instance_key)
- `.gitignore` — 인스턴스 TOML 패턴
- Interfaces:
- `intent_queue(instance_key) -> str` 등 (default 하위호환)
- `declare_topology(instance_key="default")`
- `direct_routing_key(instance_key, channel_type) -> "{instance_key}.{channel_type}"`
- `DaemonInstanceConfig.from_toml(path)`, `DaemonConfig.from_config(toml_path)`
## Key Decisions
1. **default = 리터럴 큐명(특수 처리)**: FR-2(suffix)와 FR-5(default 무회귀)의 긴장을 해소 — named instance 만 suffix, default 는 기존 이름 유지. 기존 messaging 테스트/배포 무변경. (planning R-1)
2. **declare_topology 기본 인자 `instance_key="default"`**: 기존 호출부(`transport.declare_topology()`)가 인자 없이도 default 동작 → 호출부 광범위 수정 회피.
3. **exchange 는 항상 선언(default 포함)**: direct/broadcast 는 default 인스턴스에서도 선언·바인딩하되, 기존 직접 큐 reply-to RPC 경로는 그대로 동작(추가 바인딩은 비파괴적). 기존 RPC 회귀 없음(FR-5/AC-6).
4. **TOML 우선 병합**: `from_config` 는 `from_env()` 로 베이스 구성 후 TOML 의 `portfolio_hub_token` 을 PortfolioHubConfig.token 으로 override. `instance_key` 는 TOML 전용.
5. **`--config` 필수**: argparse required. 누락 시 argparse 가 usage 출력 + exit≠0(OQ-2). 단, `from_env()` 단독 호출(테스트/라이브러리)은 계속 가능.
6. **pika per-thread**: 바인딩 추가만, consumer 스레드/커넥션 수 불변(NFR-2).
## Edge Cases
- default 큐명 충돌 없음: `*.default` 를 쓰지 않고 리터럴 사용.
- TOML 누락 필드/파일 부재 → 명확한 예외(FR-1/AC-4).
- broadcast 중복 control 전달 → 핸들러 idempotency(REQ-036/advisory lock, R-3).
- 기존 old 큐 잔류 → 자연 소멸(OQ-1), 코드 무관.
- snapshot/intent reply-to RPC: 클라이언트가 direct routing key 로 보내든 직접 큐로 보내든, 인스턴스 큐가 direct 바인딩 + 직접 publish 둘 다 수신 가능(하위호환).
## Architecture Impact
- runtime-flows: TOML 기동 + directed/broadcast 라우팅 + split-brain 방지 — REQ-051.
- project-structure: topology 동적화 + 설정 계층 + exchange — REQ-051.
- interface-contract: routing key 계약 — REQ-051.
## Reference Doc Update Plan
- interface-contract.md: routing key(`{instance_key}.{channel_type}`)+broadcast+큐명 규칙 (REQ-051 AC-1)
- runtime-flows.md: `--config` 기동 + directed/broadcast + split-brain (REQ-051 AC-2)
- project-structure.md: 동적 큐명/DaemonInstanceConfig/`--config`/exchange (REQ-051 AC-3)
@@ -0,0 +1,45 @@
# BAT-013 Implementation
- Status: completed
- Batch: BAT-013
## Plan Summary
- 순서: REQ-049 동적 큐명/declare_topology → REQ-050 exchange/binding/routing → REQ-048 TOML 로더/--config → REQ-051 baseline. 각 단계 단위 green 후 진행. default 하위호환 회귀 우선.
## Changed Files
- (구현 시 기록)
### 신규 (예정)
- `tests/runtime/test_instance_config.py` (REQ-048)
- `tests/adapters/messaging/test_instance_topology.py` (REQ-049/050)
- 인스턴스 TOML 예시(`config/instance.example.toml`, 추적) (REQ-048)
### 수정 (예정)
- `trex_core/adapters/messaging/topology.py` (동적 큐명 + DIRECT/BROADCAST + routing key)
- `trex_core/adapters/messaging/rabbitmq.py` (declare_topology(instance_key) + exchange/binding)
- `trex_core/runtime/config.py` (DaemonInstanceConfig + from_toml + from_config)
- `trex_core/runtime/daemon.py` (--config argparse + instance_key 큐 적용)
- `.gitignore` (인스턴스 TOML)
- baseline 3문서 (REQ-051)
## Changed Files (실제)
- 신규: `config/instance.example.toml`(추적), `tests/runtime/test_instance_config.py`, `tests/adapters/messaging/test_instance_topology.py`.
- 수정: `topology.py`(동적 큐명 빌더 + DEFAULT_INSTANCE_KEY/DIRECT_EXCHANGE/BROADCAST_EXCHANGE + direct_routing_key), `rabbitmq.py`(declare_topology(instance_key) + exchange/binding/control dual + import 정리), `config.py`(DaemonInstanceConfig+from_toml+from_config, DaemonConfig.instance_key), `daemon.py`(--config argparse main + per-instance 큐 구독 + declare_topology(instance_key)), `.gitignore`(인스턴스 TOML), baseline 3문서(REQ-051).
## Execution Log
1. REQ-049 동적 큐명(default 리터럴) + declare_topology(instance_key) + daemon per-instance consume → 빌더 단위 + messaging 회귀 green.
2. REQ-050 trex.direct/trex.broadcast exchange + binding + routing key → 인프라 게이트(directed 격리 + broadcast dual) green.
3. REQ-048 DaemonInstanceConfig.from_toml + from_config 병합 + --config argparse + .gitignore + example TOML → 로더 단위 + `--config` 누락 subprocess 종료 green.
4. REQ-051 baseline 3문서 정합.
5. 전체 회귀: 단위 271 passed / 30 skipped, ruff clean.
6. 인프라 게이트(PG+RabbitMQ): 301 passed (directed 라우팅 격리 S-2 + broadcast dual S-3 + default 무회귀 S-4 포함).
## Validation
- 단위: `pytest -q --deselect ...::test_sandbox_token_and_submit` → 271 passed / 30 skipped.
- 인프라 게이트: `TREX_RABBITMQ_TEST_URL=... TREX_POSTGRESQL_TEST_DSN=... pytest -q` → 301 passed.
- lint: `ruff check trex_core tests` → All passed.
## Remaining Risks
- R-1(declare_topology 변경 회귀): default=리터럴 큐명 하위호환으로 기존 messaging 테스트 무변경 — 301 passed 로 확인(FR-5/NFR-1).
- R-2(old 큐 잔류): 운영 자연 소멸(OQ-1), 코드 무관.
- (이월, 비차단) 실 2-인스턴스 동시 기동 manual e2e: directed 격리·broadcast 는 인프라 게이트 통합 테스트로 검증됨. 실 배포 시 다중 인스턴스 운영 시점 확인.
@@ -0,0 +1,33 @@
# BAT-013: 다중 인스턴스 RabbitMQ 토폴로지 — TOML config + instance_key 큐 + direct/broadcast exchange
- Status: release-candidate
- Profile: standard
- Source Discovery: dcy-009
- Included REQ: REQ-048, REQ-049, REQ-050, REQ-051
## Summary
- dcy-009(다중 인스턴스 RabbitMQ 토폴로지)를 단일 standard batch 로 delivery 한다. per-instance TOML 설정 로더 + `--config` 필수(REQ-048), `instance_key` 기반 동적 큐명 + default 하위호환(REQ-049), `trex.direct`(topic)/`trex.broadcast`(fanout) exchange + 바인딩(REQ-050), baseline 3문서 정합(REQ-051). 다중 인스턴스를 동일 RabbitMQ 에 붙여도 인스턴스 전용 큐로 split-brain(WatchdogStore/SafetySellService 경쟁 소비)을 구조적으로 방지한다.
## Scope
- In Scope:
- `DaemonInstanceConfig.from_toml` + `--config` 필수 진입점 + env 병합 + `.gitignore` TOML (REQ-048)
- `topology.py` 동적 큐명 빌더(`intent_queue/snapshot_queue/control_queue(instance_key)`, default=리터럴 하위호환) + `declare_topology(instance_key)` + daemon per-instance consume (REQ-049)
- `trex.direct`(topic) + `trex.broadcast`(fanout) exchange + per-instance/control dual binding + routing key 빌더 (REQ-050)
- baseline 3문서(interface-contract/runtime-flows/project-structure) (REQ-051)
- Out of Scope:
- strategy 모듈(orb-v1) publish 코드 변경 — 계약만 정의
- WatchdogStore Redis 외부화 / leader election / failover
- 전체 env 의 TOML 일원화(KIS/DSN/RABBITMQ_URL 은 `.env` 유지)
- old 큐 자동 삭제 스크립트(운영 절차)
## Documents
- Planning: [planning.md](./planning.md)
- Design: [design.md](./design.md)
- Implementation: [implementation.md](./implementation.md)
- Verification: [verification.md](./verification.md)
## Notes
- Source discovery 단일(dcy-009). OQ-1(old 큐 자연 소멸)·OQ-2(`--config` 필수) Answered.
- 하위호환 핵심(REQ-049): `instance_key="default"` → 리터럴 큐명 → 기존 테스트/배포 무회귀(FR-5/NFR-1).
- NFR-2(pika per-thread) 유지, NFR-3(split-brain) 인스턴스 전용 큐로 달성, NFR-4(민감값) TOML+gitignore.
- multi-instance 라우팅(directed/broadcast)·split-brain 격리는 인프라 게이트(RabbitMQ) 통합/e2e 로 검증.
@@ -0,0 +1,48 @@
# BAT-013 Planning
- Status: confirmed
- Batch: BAT-013
- Profile: standard
## Included REQ
- REQ-048 (Configuration): `DaemonInstanceConfig.from_toml` (tomllib) + `--config` 필수 진입점 + `from_config` env 병합(`portfolio_hub_token` 주입) + `.gitignore` TOML + 토큰 로그 미노출.
- REQ-049 (Interface): `topology.py` 동적 큐명 빌더(default=리터럴 하위호환) + `declare_topology(instance_key)` + daemon intent/snapshot/control consumer per-instance 큐 구독.
- REQ-050 (Integration): `trex.direct`(topic)/`trex.broadcast`(fanout) exchange + per-instance direct binding + control dual binding + `direct_routing_key` 빌더.
- REQ-051 (Documentation): interface-contract(routing key)/runtime-flows(TOML 기동·라우팅)/project-structure(동적 큐명·설정 계층·exchange) 정합.
## Delivery Plan
1. REQ-049: `topology.py` 큐명 빌더(default 하위호환) → `rabbitmq.declare_topology(instance_key)` → daemon consumer per-instance. 큐명 단위 + 기존 회귀.
2. REQ-050: `trex.direct`/`trex.broadcast` exchange 선언 + binding(direct per-instance, control dual) + routing key 빌더. 토폴로지 단위 + 인프라 게이트(라우팅 격리/broadcast).
3. REQ-048: `DaemonInstanceConfig.from_toml` + `--config` argparse 진입점 + `from_config` 병합 + `.gitignore` + 토큰 미노출. 로더 단위.
4. REQ-051: baseline 3문서 정합.
5. 전체 회귀 + ruff + 인프라 게이트(default 무회귀 + 라우팅) + (가능 시) 2-인스턴스 e2e.
## Dependencies
- Internal:
- dcy-006/BAT-007 messaging(topology/rabbitmq/daemon consumer) — 변경 대상 기반.
- BAT-012 control 채널(`trex.control.req`) — control 큐가 broadcast dual binding 대상.
- BAT-008 PortfolioHubConfig.token — TOML 주입 지점.
- External:
- RabbitMQ(topic/fanout exchange). stdlib `tomllib`(py3.12).
## Design Gate
- Design Required Before Implementation: yes
- Reason: (a) `declare_topology` signature 변경 + 큐명 동적화는 **모든 messaging consumer/test 표면**에 영향 — default 하위호환 규칙(리터럴 vs suffix)을 설계에서 고정해야 회귀를 막는다. (b) topic/fanout exchange + per-instance/dual binding 의 정확한 binding 매트릭스와 routing key 계약을 설계에서 확정해야 strategy 계약과 일치한다. (c) TOML+env 병합 우선순위(`portfolio_hub_token` TOML 우선) + `--config` 진입점 구조를 설계에서 명시. → design.md 동일 턴 scaffold.
## Milestones
1. M1 — REQ-049 동적 큐명 + declare_topology(instance_key) + default 회귀 green
2. M2 — REQ-050 exchange + binding + routing key + 인프라 게이트(라우팅) green
3. M3 — REQ-048 TOML 로더 + --config + 병합 + .gitignore 단위 green
4. M4 — REQ-051 baseline 3문서
5. M5 — 전체 회귀 + ruff + 인프라 게이트 + 2-인스턴스 e2e(가능 시)
## Risks
- R-1 (Medium): `declare_topology`/큐명 변경이 기존 messaging 테스트 다수에 영향. 대응: default=리터럴 큐명 하위호환으로 기존 테스트 무변경 유지(REQ-049 핵심). 빌더에 default 특수 처리.
- R-2 (Medium): old durable 큐 잔류(dcy-009 R-2). 대응: 자연 소멸 허용(OQ-1), 배포 절차 외. 코드 무관.
- R-3 (Low): broadcast 중복 제어(dcy-009 R-3). 대응: kill-switch/pause idempotency(REQ-036/advisory lock).
- R-4 (Low): TOML 토큰 커밋 노출(dcy-009 R-4). 대응: `.gitignore` + 로그 미노출(REQ-048).
- R-5 (Low): pika BlockingConnection thread-safety. 대응: per-thread connection 유지(NFR-2), 바인딩만 추가.
## Notes
- 하위호환 우선: default 인스턴스는 기존 리터럴 큐명·RPC 경로 그대로 → 기존 277+ 테스트 무회귀가 1차 게이트.
- 2-인스턴스 e2e(라우팅 격리/broadcast)는 RabbitMQ 인프라 게이트 또는 manual; 단일 인스턴스 기본 검증은 단위로 충분.
@@ -0,0 +1,64 @@
# BAT-013 Verification
- Status: verified
- Batch: BAT-013
## Acceptance Mapping
> 구현 완료 후 각 AC 에 evidence 매핑. 아래는 계획 골격.
### REQ-048 (TOML 로더 + --config)
| AC | Evidence (예정) |
|----|---------|
| AC-1 from_toml 파싱 | 로더 단위 |
| AC-2 --config 누락 종료 | 진입점 단위/검사 |
| AC-3 token 주입 | 로더 단위(S-5) |
| AC-4 필수 필드 누락 오류 | 로더 단위 |
| AC-5 .gitignore TOML | gitignore 확인 |
| AC-6 token 로그 미노출 | 코드/로그 검사 |
### REQ-049 (동적 큐명 + 하위호환)
| AC | Evidence (예정) |
|----|---------|
| AC-1 named suffix | 큐명 빌더 단위 |
| AC-2 default 리터럴 | 동일 |
| AC-3 declare named 큐 | 인프라 게이트 |
| AC-4 default 회귀 없음 | 전체 pytest |
| AC-5 per-thread/스레드 수 | daemon 검사 |
| AC-6 인스턴스 격리 | 인프라 게이트/e2e |
### REQ-050 (exchange + binding)
| AC | Evidence (예정) |
|----|---------|
| AC-1 direct/broadcast 선언 | 인프라 게이트 |
| AC-2 direct binding | 인프라 게이트 |
| AC-3 routing key 빌더 | 단위 |
| AC-4 directed 격리 | 인프라 게이트(2큐) |
| AC-5 broadcast dual 전달 | 인프라 게이트(2큐) |
| AC-6 default 회귀 | 전체 pytest |
### REQ-051 (baseline 정합)
| AC | Evidence (예정) |
|----|---------|
| AC-1 interface-contract routing key | 문서 diff |
| AC-2 runtime-flows 기동/라우팅 | 문서 diff |
| AC-3 project-structure 동적큐/설정/exchange | 문서 diff |
## Evidence
- Test:
- REQ-048 로더 6 단위(from_toml/필수 누락/token 주입 S-5/default ik/`--config` 누락 subprocess 종료/gitignore) green.
- REQ-049/050 토폴로지 3 단위(builders default vs named/routing key) + 인프라 게이트 2(directed 격리 S-2 / broadcast dual S-3) green.
- 단위 전체 회귀: `271 passed / 30 skipped`(`::test_sandbox_token_and_submit` deselect).
- 인프라 게이트(PG trex_core_test + RabbitMQ): `301 passed`(directed 라우팅 격리 + broadcast dual + default 무회귀 S-4 + 마이그레이션 round-trip 포함).
- `ruff check trex_core tests` → All passed.
- Logs: `271 passed, 30 skipped` / `301 passed ... ~15s`.
## Carried-Over / e2e
- S-1(named 큐 선언)·S-2(directed 격리)·S-3(broadcast dual): RabbitMQ 인프라 게이트 통합 테스트로 확인.
- S-4(default 무회귀): 전체 pytest 301 passed.
- S-5(TOML token 주입): 로더 단위.
- (이월, 비차단) 실 2-인스턴스 동시 기동 manual e2e: 실 다중 인스턴스 운영 시점 확인.
## Result
- Ready for Release: true
- Blocking Issues:
- 없음. 제어 큐 rename(BAT-012 R-1)·old 큐 자연 소멸(OQ-1)·strategy publish 변경(Out of Scope)·2-인스턴스 manual e2e 는 비차단(인프라 게이트 통합으로 핵심 라우팅 검증됨).
+1
View File
@@ -20,3 +20,4 @@
| BAT-010 | standard | release-candidate | REQ-034, REQ-035, REQ-036, REQ-041 | dcy-007 | [bat-010](./bat-010_20260607_safety-sell-foundation/) |
| BAT-011 | standard | release-candidate | REQ-037, REQ-038, REQ-039, REQ-040, REQ-042 | dcy-007 | [bat-011](./bat-011_20260607_safety-sell-triggers/) |
| BAT-012 | standard | release-candidate | REQ-043, REQ-044, REQ-045, REQ-046, REQ-047 | dcy-008 | [bat-012](./bat-012_20260607_kill-switch-control/) |
| BAT-013 | standard | release-candidate | REQ-048, REQ-049, REQ-050, REQ-051 | dcy-009 | [bat-013](./bat-013_20260607_multi-instance-topology/) |
@@ -1,13 +1,13 @@
# 0. 문서 상태
- 상태: draft
- 상태: confirmed
- 문서 ID: dcy-009_20260607_multi-instance-rabbitmq-topology
- 이슈명: 인스턴스별 독립 RabbitMQ 채널 토폴로지 — trex-core 다중 인스턴스 운영을 위한 Topic Exchange + Fanout Exchange per-instance 큐 분리
- 작성 시각(KST): 2026-06-07
- 마지막 갱신 시각(KST): 2026-06-07 (instance_id → instance_key, TOML config 설계 반영)
- 마지막 갱신 시각(KST): 2026-06-07 (draft-req: REQ-048~051 생성)
- 대체됨: 없음
- 후속 Discovery 참조: 없음
- 생성된 REQ 참조: 없음
- 생성된 REQ 참조: REQ-048, REQ-049, REQ-050, REQ-051
# 1. 계획 상태 요약
@@ -45,7 +45,7 @@
- trex-core 데몬 운영자: `--config {path}.toml` CLI 인자로 인스턴스별 설정 파일 지정 + 큐 모니터링
- 제약사항
- pika `BlockingConnection`은 thread-safe하지 않다 — 현재 consumer 스레드별 독립 Connection 패턴은 변경 없이 유지해야 한다.
- 기존 durable 큐(`trex.intent.req` 등)가 이미 RabbitMQ 서버에 선언되어 있을 수 있다 → 새 큐명으로 전환 시 old 큐 처리 절차가 필요하다(OQ-1).
- 기존 durable 큐(`trex.intent.req` 등)가 이미 RabbitMQ 서버에 선언되어 있을 수 있다 → 새 큐명으로 전환 후 자연 소멸을 허용하며, RabbitMQ admin console에서 적절한 시점에 수동 삭제한다(OQ-1 Answered).
- strategy 모듈(orb-v1)이 routing key 변경에 맞게 publish 코드를 업데이트해야 한다 — 인터페이스 계약 정의가 선행되어야 한다.
- `portfolio_hub_token`과 `instance_key`는 `.env`에 넣지 않는다 — 기존 `config.py`의 `from_env()` 패턴 단독으로는 이 원칙을 지킬 수 없으므로 TOML 로더가 필수다.
@@ -77,14 +77,14 @@
- `docs/interface-contract.md`: routing key 계약 갱신 — `{instance_key}.{channel_type}` 규칙 추가
- 운영 영향:
- 인스턴스별 TOML 파일 생성 및 관리 필요(`instance_key`, `portfolio_hub_token` 포함)
- 기존 `trex.intent.req` 등 old 큐가 RabbitMQ에 잔류할 경우 배포 절차에 old 큐 정리 단계 필요(OQ-1)
- 기존 `trex.intent.req` 등 old 큐는 새 큐 전환 후 자연 소멸을 허용한다. RabbitMQ admin console에서 적절한 시점에 수동 삭제(OQ-1 Answered).
- `TREX_PORTFOLIO_HUB_TOKEN` env var는 TOML로 이관 후 `.env`에서 제거 필요
# 5. 요구사항 목록
## 기능 요구사항
- FR-1: 데몬 기동 시 `--config <path>.toml` CLI 인자를 지원한다. TOML 파일에서 최소 `instance_key`와 `portfolio_hub_token`을 읽는다. 이 두 값은 `.env`에서 읽지 않는다.
- FR-1: 데몬 기동 시 `--config <path>.toml` CLI 인자는 필수다. 인자 없이 기동하면 오류 메시지를 출력하고 즉시 종료한다. TOML 파일에서 최소 `instance_key`와 `portfolio_hub_token`을 읽는다. 이 두 값은 `.env`에서 읽지 않는다.
- FR-2: `instance_key` 값을 기반으로 모든 수신 큐명을 `{base}.{instance_key}` 형식으로 suffix화한다.
- FR-3: `trex.direct`(type: topic, durable) exchange를 선언하고, 각 인스턴스의 intent/control/snapshot 큐를 routing key `{instance_key}.intent`, `{instance_key}.control`, `{instance_key}.snapshot`으로 바인딩한다.
- FR-4: `trex.broadcast`(type: fanout, durable) exchange를 선언하고, 각 인스턴스의 control 큐를 이 exchange에 추가 바인딩(dual binding)한다. 전체 인스턴스 대상 제어 명령은 이 exchange에 publish한다.
@@ -124,7 +124,7 @@
- R-1 (Medium): strategy 모듈이 routing key를 변경하지 않으면 다중 인스턴스 환경에서 메시지가 올바른 인스턴스에 도달하지 않는다.
- 완화 방안: 인터페이스 계약에 routing key 규칙을 명시한다. `instance_key = "default"` 단일 인스턴스 시에는 routing key `default.intent` 등으로 publish하면 하위 호환 유지 가능.
- R-2 (Medium): 기존 durable 큐(`trex.intent.req` 등)가 RabbitMQ에 잔류하면 새 큐로 전환 후 old 큐에 consumer가 없어진다.
- 완화 방안: 배포 절차에 old 큐 삭제 단계를 명시한다(OQ-1). old 큐에 새로 publish되는 메시지는 없으므로 즉시 삭제해도 메시지 손실 없음.
- 완화 방안: 자연 소멸 허용. 배포 후 old 큐에 새로 publish되는 메시지가 없으므로 메시지 누적 없음. 적절한 시점에 RabbitMQ admin console에서 수동 삭제(OQ-1 Answered).
- R-3 (Low): `trex.broadcast` exchange 메시지가 모든 인스턴스에 동시 전달되므로 인스턴스별로 동일 제어 명령을 중복 처리한다.
- 완화 방안: kill-switch/pause 핸들러에 idempotency 보장(REQ-036 패턴, DB advisory lock). 중복 처리는 무해하게 동작.
- R-4 (Low): TOML 설정 파일이 실수로 git에 커밋되면 `portfolio_hub_token` 등 민감값이 노출된다.
@@ -136,18 +136,18 @@
- A-2: strategy 모듈(orb-v1)은 인터페이스 계약 변경에 맞춰 routing key를 업데이트할 수 있다.
- A-3: 인스턴스별 담당 portfolio 매핑은 TOML 설정 또는 strategy 모듈의 routing 설정으로 관리한다. trex-core 내부에 레지스트리 DB를 두지 않는다.
- A-4: `portfolio_hub_token`과 `instance_key`를 `.env`에서 TOML로 이관하면 기존 `.env` 기반 배포 스크립트를 수정해야 한다. KIS 자격증명 등 나머지 env var는 이번 범위에서 이관하지 않는다.
- A-5: TOML 파일 경로는 `--config` CLI 인자로 지정한다. 경로 지정 없이 기동하는 경우 동작에 대한 처리 방침은 OQ-2에서 결정한다.
- A-5: TOML 파일 경로는 `--config` CLI 인자로 지정한다. `--config` 인자는 필수이며, 지정하지 않으면 데몬이 오류 메시지를 출력하고 즉시 종료한다(OQ-2 Answered). 기본값 fallback은 제공하지 않는다.
## 오픈 질문
- OQ-1: 기존 durable 큐(`trex.intent.req` 등) 처리 방식 — 자연 소멸(수동 삭제) vs 배포 절차에 명시적 삭제 포함?
- 상태: Open
- 처리 방안: REQ 단계에서 배포 절차로 결정. old 큐에 publisher가 없어지면 메시지 누적은 없으나 RabbitMQ admin console에 불필요한 큐가 잔류한다.
- 종료 조건: REQ 초안 작성 시 배포 절차 섹션에 old 큐 처리 방식 명시.
- 상태: Answered
- 결정: 자연 소멸 허용. 배포 후 old 큐에 새로 publish되는 메시지는 없으므로 메시지 누적 없음. RabbitMQ admin console에서 적절한 시점에 수동 삭제. 배포 절차에 명시적 삭제 단계를 추가하지 않는다.
- 반영 위치: R-2 완화 방안
- OQ-2: `--config` 인자 없이 데몬 기동 시 동작 — 오류 종료(필수) vs `instance_key = "default"` 기본값으로 기동?
- 상태: Open
- 처리 방안: REQ 단계에서 결정. 다중 인스턴스 안전성을 위해 TOML 필수(오류 종료)를 권장하나, 기존 배포와의 호환성을 위해 기본값 허용도 고려 가능.
- 종료 조건: confirm-discovery 완료 전 Justin Ko 결정.
- 상태: Answered
- 결정: `--config` 필수. 인자 없이 기동 시 오류 메시지 출력 후 즉시 종료. 기본값 fallback 없음. 인스턴스 식별이 보장되지 않은 상태로 기동하는 것은 다중 인스턴스 환경에서 split-brain 위험을 내포하므로 명시적 실패가 안전하다.
- 반영 위치: A-5, FR-1
### 오픈 질문 처리 기준
@@ -172,16 +172,16 @@
# 8. REQ로 넘기기 전 확인 체크
- [ ] 범위(In Scope/Out of Scope) 확정
- 기준: OQ-1(old 큐 처리), OQ-2(TOML 필수 여부) Answered
- [ ] 성공 지표의 측정 방식/데이터 출처 확정
- 기준: S-1~S-5 각각 테스트 또는 e2e 근거 확정
- [ ] High 영향 리스크 우선순위 및 해소 책임자 지정
- 기준: R-1(routing key contract), R-2(old 큐), R-4(TOML git 노출) 완화 방안 REQ 단계 인계 여부 확인
- [ ] 모든 오픈 질문(OQ) `Open` 상태 없음 확인
- 기준: OQ-1, OQ-2 결정 완료
- [ ] Freeze 확정 담당자(CONFIRMED_BY) 지정
- 기준: Justin Ko 확인
- [x] 범위(In Scope/Out of Scope) 확정
- 기준: OQ-1(old 큐 처리) Answered, OQ-2(TOML 필수) Answered
- [x] 성공 지표의 측정 방식/데이터 출처 확정
- 기준: S-1~S-5 각각 테스트 또는 e2e 근거 확정 — S-1~S-5 측정 방식 및 데이터 출처 섹션 7에 전부 기입됨
- [x] High 영향 리스크 우선순위 및 해소 책임자 지정
- 기준: R-1~R-4 전부 Medium/Low — High 영향 리스크 없음. 모든 리스크에 완화 방안 기입됨. REQ 단계 인계 확정.
- [x] 모든 오픈 질문(OQ) `Open` 상태 없음 확인
- 기준: OQ-1, OQ-2 모두 Answered
- [x] Freeze 확정 담당자(CONFIRMED_BY) 지정
- 기준: Justin Ko 확인 — Section 11 Confirmed By: Justin Ko
# 9. 파일 처리 결과
@@ -198,20 +198,15 @@
# 10. 사용자 결정 필요 항목 요약
## DECIDE
- {{DECIDE_OLD_QUEUE_MIGRATION}}: 기존 durable 큐(`trex.intent.req` 등) 처리 방식 — 자연 소멸(수동 삭제) vs 배포 절차에 명시적 삭제 포함 (OQ-1)
- {{DECIDE_TOML_REQUIRED}}: `--config` 인자 없이 기동 시 동작 — 오류 종료(TOML 필수) vs `instance_key = "default"` 기본값으로 기동 허용 (OQ-2)
## CONFIRM
- {{CONFIRM_STRATEGY_MODULE_OUT_OF_SCOPE}}: strategy 모듈(orb-v1)의 routing key 변경은 이번 Discovery 범위 밖(trex-core는 계약 정의만, 구현은 strategy 모듈 담당)인지 확인
- {{CONFIRM_ENV_RESIDUAL}}: 나머지 env var(KIS 자격증명, PostgreSQL DSN 등)는 이번 범위에서 TOML로 이관하지 않고 `.env`에 유지하는 것으로 확정하는지 확인
- CONFIRM_STRATEGY_MODULE_OUT_OF_SCOPE: 확인됨. strategy 모듈(orb-v1)의 routing key 구현 변경은 이번 Discovery 범위 밖. trex-core는 exchange·바인딩 계약만 정의하며, strategy 모듈의 publish 코드 변경은 별도 Discovery 또는 REQ에서 다룬다(Section 5 Out of Scope 반영).
- CONFIRM_ENV_RESIDUAL: 확인됨. KIS 자격증명(`TREX_KIS_*`), PostgreSQL DSN, TREX_RABBITMQ_URL 등 기존 env var는 이번 범위에서 `.env` 유지. TOML 이관 대상은 `portfolio_hub_token`과 `instance_key`만(Section 5 Out of Scope + A-4 반영).
## DATA
- {{DATA_TOML_SCHEMA_FULL}}: TOML 파일에 포함할 전체 필드 목록(`instance_key`, `portfolio_hub_token` 외 추가 필드 유무) — REQ 단계 스키마 정의에 필요
- {{DATA_MULTI_INSTANCE_TRIGGER_TIMING}}: 실제 다중 인스턴스 운영이 필요한 시점(배포 계획) — NFR 우선순위 조정에 필요
- DATA_TOML_SCHEMA_FULL: REQ 단계 확정. 최소 필드: `instance_key` (string), `portfolio_hub_token` (string). 추가 필드는 REQ 단계 스키마 정의에서 확정한다.
- DATA_MULTI_INSTANCE_TRIGGER_TIMING: REQ/배포 단계 계측 항목. Discovery 단계 범위 밖. 실제 다중 인스턴스 운영 계획은 배포 시점 REQ에서 반영한다.
# 11. Discovery Freeze
@@ -225,8 +220,8 @@
## Freeze 플래그
- Handoff Decision: 보류
- Handoff Rationale: TOML 설정 파일 도입 + instance_key 기반 토폴로지 설계 완료. OQ-1(old 큐 처리), OQ-2(TOML 필수 여부) 미결 — confirm-discovery에서 결정 후 REQ 초안 작성 가능.
- Ready for REQ Drafting: false
- Confirmed By: TBD
- Confirmed At (KST): TBD
- Handoff Decision: REQ Drafting 진행 가능
- Handoff Rationale: TOML 설정 파일 도입 + instance_key 기반 토폴로지 설계 완료. OQ-1(old 큐 자연 소멸), OQ-2(TOML 필수) 모두 Answered. CONFIRM/DATA 항목은 REQ 단계에서 확정 가능 — confirm-discovery 후 REQ 초안 작성 진행 가능.
- Ready for REQ Drafting: true
- Confirmed By: Justin Ko
- Confirmed At (KST): 2026-06-07
+1 -1
View File
@@ -14,4 +14,4 @@
| dcy-006_20260605_strategy-module-rabbitmq-transport | 20260605 | trex-core ↔ 전략 모듈 통신 transport — RabbitMQ 도입 (스냅샷 read + intent 제출 + 이벤트) | [Discovery](./dcy-006_20260605_strategy-module-rabbitmq-transport.md) | confirmed |
| dcy-007_20260606_core-autonomous-safety-sell | 20260606 | core 자율 안전 매도 — stop-loss·시간 만기 자동 트리거 | [Discovery](./dcy-007_20260606_core-autonomous-safety-sell.md) | confirmed |
| dcy-008_20260607_emergency-stop-kill-switch | 20260607 | 비상 정지/강제 청산(kill-switch) — portfolio/전역 슬롯 청산 + 당일 전략 제외 | [Discovery](./dcy-008_20260607_emergency-stop-kill-switch.md) | confirmed |
| dcy-009_20260607_multi-instance-rabbitmq-topology | 20260607 | 인스턴스별 독립 RabbitMQ 채널 토폴로지 — trex-core 다중 인스턴스 운영을 위한 Topic Exchange + Fanout Exchange per-instance 큐 분리 | [Discovery](./dcy-009_20260607_multi-instance-rabbitmq-topology.md) | draft |
| dcy-009_20260607_multi-instance-rabbitmq-topology | 20260607 | 인스턴스별 독립 RabbitMQ 채널 토폴로지 — trex-core 다중 인스턴스 운영을 위한 Topic Exchange + Fanout Exchange per-instance 큐 분리 | [Discovery](./dcy-009_20260607_multi-instance-rabbitmq-topology.md) | confirmed |
+10 -1
View File
@@ -4,7 +4,7 @@
- Baseline Seed: .stage-pilot/bootstrap/baseline.yaml
- Status: draft
- Owner: project maintainers
- Last Updated (KST): 2026-06-07 (BAT-012)
- Last Updated (KST): 2026-06-07 (BAT-013)
- Source Discovery / Batch: bootstrap-baseline (pre-Discovery); HTTP API = dcy-002 / BAT-002
- Primary Runtime: daemon (HTTP API) + library (slot 도메인)
@@ -189,6 +189,15 @@ resume_kill_switch 요청·응답 (REQ-046):
- snapshot masking 소스(REQ-046): `mask = pending | warning | pause | killswitch_today`. kill-switch 당일 제외는 `(portfolio_id, trading_date)` day-scoped — 다음 거래일 자동 복귀. `portfolio_id="*"` sentinel 은 전역(전체 universe) 마스킹.
- 하위호환: `trex.pause.req` 는 legacy alias. 운영 배포 시 제어 클라이언트는 `trex.control.req` 로 이전한다(rollout 결정, REQ-043 Notes).
### Multi-instance routing (BAT-013 / REQ-049/050)
다중 인스턴스 운영 시 큐명과 라우팅을 `instance_key` 로 분리한다(dcy-009).
- **큐명 규칙**: `{base}.{instance_key}`(예: `trex.intent.req.inst-1`). `instance_key = "default"` 는 기존 리터럴 큐명(`trex.intent.req` 등)을 그대로 사용한다(하위호환, FR-5).
- **`trex.direct`(topic exchange)**: 특정 인스턴스 지정 전달. routing key = `{instance_key}.{channel_type}`(`channel_type ∈ {intent, snapshot, control}`). 각 인스턴스 큐가 자신의 routing key 로 바인딩 → `inst-1.intent` publish 는 `trex.intent.req.inst-1` 에만 도달.
- **`trex.broadcast`(fanout exchange)**: 전역 제어. 각 인스턴스의 **control 큐가 dual 바인딩**되어, broadcast publish 는 모든 인스턴스 control 큐에 동시 전달(global kill-switch 등). 중복 전달은 핸들러 idempotency(REQ-036/advisory lock)로 무해.
- **기동**: 데몬은 `--config <path>.toml`(필수)로 `instance_key`/`portfolio_hub_token` 을 읽는다(`.env` 아님, REQ-048).
## Interface: `PortfolioHubReadPort` (Protocol, BAT-008)
- Type: external-service (HTTP, portfolio-hub)
+6 -5
View File
@@ -4,7 +4,7 @@
- Baseline Seed: .stage-pilot/bootstrap/baseline.yaml
- Status: draft
- Owner: project maintainers
- Last Updated (KST): 2026-06-07 (BAT-012)
- Last Updated (KST): 2026-06-07 (BAT-013)
- Source Discovery / Batch: bootstrap-baseline (pre-Discovery, 3-phase lifecycle 밖의 bootstrap 산출물)
## Project Summary
@@ -23,7 +23,7 @@
## Primary Runtime
- Type: daemon (sync + thread, no HTTP) + library (slot 도메인)
- Entry Points:
- `trex-core-daemon` (console_script → `trex_core.runtime.daemon:main`) :: 슬롯 코어 데몬 — migrate+preallocate+reconcile 후 7 백그라운드 스레드(intent RPC consumer / snapshot RPC consumer / control RPC consumer / snapshot scheduler / execution / ledger / safety-sell) 기동, RabbitMQ messaging 으로 전략 모듈과 통신 (BAT-002 + BAT-007 + BAT-008 + BAT-011 + BAT-012)
- `trex-core-daemon --config <path>.toml` (console_script → `trex_core.runtime.daemon:main`) :: 슬롯 코어 데몬 — `--config` 필수(per-instance TOML, BAT-013). migrate+preallocate+reconcile 후 7 백그라운드 스레드(intent RPC consumer / snapshot RPC consumer / control RPC consumer / snapshot scheduler / execution / ledger / safety-sell)를 `instance_key` 큐로 기동, RabbitMQ messaging 으로 전략 모듈과 통신 (BAT-002 + BAT-007 + BAT-008 + BAT-011 + BAT-012 + BAT-013)
- `trex-core` (console_script → `trex_core.runtime.cli:main`) :: 운영 CLI (`pause` / `resume` / `paused`) (BAT-008)
- RabbitMQ `trex.snapshot.req` / `trex.intent.req` / `trex.pause.req` / `trex.events` :: 매수·매도 전략 데몬과의 transport (docs/interface-contract.md)
- SlotService / SlotRepository (Protocol) :: in-process 도메인 서비스 + 저장소 경계(라이브러리 사용)
@@ -83,12 +83,12 @@
│ ├── adapters/ # 인프라 어댑터 (BAT-002, BAT-004, BAT-006, BAT-007, BAT-008)
│ │ ├── broker/ # paper.py(결정적 async 더블 + 현재가 stub) · kis.py(KIS Open API + 잔고조회 + 현재가 polling) (BAT-004/BAT-008/BAT-011)
│ │ ├── ledger/ # in_memory.py(stub) · portfolio_hub.py(HTTP 어댑터) (BAT-006)
│ │ ├── messaging/ # topology.py · in_memory.py(stub) · rabbitmq.py(pika 동기) (BAT-007)
│ │ ├── messaging/ # topology.py(동적 큐명 빌더 + trex.direct/trex.broadcast exchange, BAT-013) · in_memory.py(stub) · rabbitmq.py(pika 동기, declare_topology(instance_key)) (BAT-007/BAT-013)
│ │ ├── portfolio/ # hub_read.py(HTTP) · stub_read.py(stub) · pause_store.py(Postgres) · killswitch_store.py(day-scoped, BAT-012) (BAT-008)
│ │ └── persistence/ # postgresql.py(+PostgreSQLLedgerOutbox) · in_memory_intent_store.py · migrate.py · sql/(001_…, 002_create_ledger_outbox.sql, 003_create_snapshot_pause.sql, 004_add_price_thresholds.sql, 005_create_killswitch_exclusion.sql)
│ └── runtime/ # 데몬 (BAT-002, BAT-004, BAT-006, BAT-007, BAT-008)
│ ├── config.py # DaemonConfig + KISConfig + PortfolioHubConfig + RabbitMQConfig + SnapshotScheduleConfig + SafetySellConfig(TREX_WATCHDOG_*/FORCED_CLOSE/STOP_LOSS_POLL/MARKET_CLOSE) (env)
│ ├── daemon.py # Daemon class: setup(migrate/preallocate/reconcile/declare-topology) + 7 worker threads (execution·ledger·intent-rpc·snapshot-rpc·control-rpc·snapshot-scheduler·safety-sell) + WatchdogStore + KillSwitchService + graceful shutdown
│ ├── config.py # DaemonConfig(+instance_key) + KISConfig + PortfolioHubConfig + RabbitMQConfig + SnapshotScheduleConfig + SafetySellConfig + DaemonInstanceConfig.from_toml/from_config (env + per-instance TOML, BAT-013)
│ ├── daemon.py # Daemon class: `--config` TOML 진입점(BAT-013) + setup(migrate/preallocate/reconcile/declare-topology(instance_key)) + 7 worker threads(instance_key 큐 구독) + WatchdogStore + KillSwitchService + graceful shutdown
│ └── cli.py # trex-core CLI: pause/resume/paused subcommands (BAT-008 / REQ-032)
└── tests/
├── domain/slot/ # test_state / test_service / test_contract
@@ -149,3 +149,4 @@
- 2026-06-07 :: BAT-010 — safety-sell 기반 인프라(`application/watchdog.py` WatchdogStore 신설, `Slot` 에 `stop_loss_price`/`take_profit_price` 필드 + `004_add_price_thresholds.sql` additive 마이그레이션, `SlotService.sell()` 자율 매도 내부 경로, `make_snapshot_rpc_handler` 워치독 갱신, `SafetySellConfig` env 5종 파싱) (REQ-034~036, 041).
- 2026-06-07 :: BAT-011 — safety-sell 트리거 레이어(`application/safety_sell.py` SafetySellService 신설 — forced_close/stop-loss·take-profit/장 마감, `BrokerPort.get_current_price()` + KIS `inquire-price`(`FHKST01010100`, CHG-04 정정)·paper stub, `SlotService.update_thresholds()` + intent RPC `update_thresholds` 액션, runtime/daemon 을 7 스레드로 확장(`trex-safety-sell`) + WatchdogStore 배선) (REQ-037~040, 042).
- 2026-06-07 :: BAT-012 — 비상 정지/kill-switch(`trex.pause.req`→`trex.control.req` 제어 채널 + action dispatch, `application/killswitch.py` KillSwitchService + `adapters/portfolio/killswitch_store.py` + `005_create_killswitch_exclusion.sql` day-scoped 제외, snapshot masking 에 killswitch_today 추가, `make_control_rpc_handler` + kill_switch/resume_kill_switch, daemon control consumer 전환) (REQ-043~047).
- 2026-06-07 :: BAT-013 — 다중 인스턴스 RabbitMQ 토폴로지(`topology.py` 동적 큐명 빌더 `intent/snapshot/control_queue(instance_key)` default 리터럴 하위호환 + `trex.direct`(topic)/`trex.broadcast`(fanout) exchange + `direct_routing_key`, `rabbitmq.declare_topology(instance_key)` per-instance 큐/바인딩+control dual, `config.DaemonInstanceConfig.from_toml`+`DaemonConfig.from_config`+`instance_key` 필드, daemon `--config` 필수 진입점 + instance_key 큐 구독, `.gitignore` 인스턴스 TOML + `config/instance.example.toml`) (REQ-048~051).
+12 -2
View File
@@ -4,7 +4,7 @@
- Baseline Seed: .stage-pilot/bootstrap/baseline.yaml
- Status: draft
- Owner: project maintainers
- Last Updated (KST): 2026-06-07 (BAT-012)
- Last Updated (KST): 2026-06-07 (BAT-013)
- Source Discovery / Batch: bootstrap-baseline (pre-Discovery); 데몬/HTTP 흐름 = dcy-002 / BAT-002
- Primary Runtime: daemon (HTTP API) + library (slot 도메인)
@@ -31,7 +31,7 @@
## Flow: `데몬 lifecycle` (BAT-002, BAT-007)
1. `trex-core-daemon` 기동 → `DaemonConfig.from_env()`(TREX_POSTGRESQL_DSN, `TREX_RABBITMQ_URL` 등).
1. `trex-core-daemon --config <path>.toml` 기동 → `DaemonConfig.from_config(toml)` = `from_env()`(TREX_POSTGRESQL_DSN/`TREX_RABBITMQ_URL` 등) + TOML(`instance_key`, `portfolio_hub_token`) 병합. `--config` 필수(없으면 오류 종료, BAT-013/REQ-048). `instance_key` 로 큐명·라우팅 분리.
2. setup: `apply_migrations(dsn)`(멱등) → 거래일 슬롯 `preallocate_daily_slots`(멱등) → `execution.reconcile()`(REQ-014) → 이벤트 publisher 의 `declare_topology()`.
3. start: 7개 백그라운드 스레드 동시 기동
- `trex-execution` :: 브로커 dispatch/poll 루프 (REQ-013/014)
@@ -156,6 +156,16 @@
6. 수동 해제: `{action:"resume_kill_switch", scope, portfolio_id?}` → `KillSwitchService.resume()` 가 day-scoped row 삭제 → 다음 응답부터 정상 노출. **이미 청산된 슬롯은 비가역**(매도 되돌리지 않음).
7. 독립성: kill-switch 는 자율 안전 매도 worker(dcy-007)와 스레드/상태 공유 없음. 동일 슬롯 동시 sell() 은 REQ-036 idempotency 로 1회만 전이.
## Flow: `multi-instance 라우팅` (BAT-013 / REQ-049/050)
다중 인스턴스를 동일 RabbitMQ 에 붙여도 split-brain 없이 운영하기 위한 큐/exchange 분리.
1. 각 인스턴스는 `--config inst-N.toml`(`instance_key`)로 기동 → 수신 큐명이 `{base}.{instance_key}` 로 분리(`default` 는 리터럴 유지, 하위호환).
2. `declare_topology(instance_key)`: `trex.direct`(topic) + `trex.broadcast`(fanout) 선언 + 인스턴스 intent/snapshot/control 큐를 `trex.direct` 에 `{instance_key}.{channel}` 로 바인딩 + control 큐를 `trex.broadcast` 에 dual 바인딩.
3. **directed**: strategy/운영자가 `trex.direct` 에 `inst-1.intent` 로 publish → `trex.intent.req.inst-1` 에만 전달(다른 인스턴스 큐 미수신). 인스턴스별 WatchdogStore/SafetySellService 가 자기 큐만 소비 → split-brain 방지(NFR-3).
4. **broadcast**: `trex.broadcast` 에 publish → 모든 인스턴스 control 큐 동시 전달(global kill-switch 등). 중복 처리는 idempotency 로 무해.
5. 기존 reply-to RPC 직접 큐 경로(default)는 그대로 동작 — 추가 바인딩은 비파괴적(FR-5 무회귀).
## Flow: `broker degrade 분기` (BAT-007 / REQ-024)
1. transport connection drop → 어댑터가 `_mark_disconnected()` + 백오프(1s→2s→4s→8s→16s→30s cap) 재연결.
@@ -0,0 +1,148 @@
# req-048: per-instance TOML 설정 로더 + `--config` 필수
- Status: Implemented
- Type: Configuration
- Priority: High
- Owner: project maintainers
## Intent
다중 인스턴스 운영을 위해 인스턴스별 TOML 설정 파일을 도입한다. 데몬 기동 시 `--config <path>.toml` 인자를 **필수**로 받고, TOML 에서 `instance_key` 와 `portfolio_hub_token` 을 읽는다. 이 두 값은 `.env` 에 넣지 않는 것을 원칙으로 하며(민감값 분리), 기존 `DaemonConfig.from_env()` 와 병합해 최종 설정을 구성한다(dcy-009 FR-1, NFR-4, OQ-2/A-5).
## Requirement
- `config.py` 에 `DaemonInstanceConfig` dataclass(`instance_key: str`, `portfolio_hub_token: str`)와 `from_toml(path)` 로더를 추가한다(stdlib `tomllib` 사용).
- `DaemonConfig` 구성 시 `--config <path>.toml` 의 TOML 값을 `from_env()` 결과와 병합하는 팩토리(예: `from_config(toml_path)`)를 추가한다. `portfolio_hub_token` 은 TOML 값을 사용해 `PortfolioHubConfig.token` 에 주입한다.
- 데몬 기동 진입점은 `--config` 를 **필수 인자**로 받는다. 인자 없이 기동하면 오류 메시지를 출력하고 비정상 종료(exit code ≠ 0)한다. 기본값 fallback 없음(OQ-2 Answered).
- `instance_key` 와 `portfolio_hub_token` 은 `.env`/OS env 에서 읽지 않는다. TOML 미존재/필수 필드 누락 시 명확한 오류로 종료한다.
- `.gitignore` 에 인스턴스 TOML 경로 패턴(예: `*.toml` 또는 `config/*.toml`)을 추가해 설정 파일이 커밋되지 않게 한다. 토큰/instance_key 는 로그에 평문 출력하지 않는다(NFR-4).
## Acceptance Criteria
1. Given `inst-1.toml`(`instance_key="inst-1"`, `portfolio_hub_token="tok"`), When `DaemonInstanceConfig.from_toml("inst-1.toml")`, Then `instance_key="inst-1"`, `portfolio_hub_token="tok"` 로 파싱된다.
2. Given `--config` 인자 없이 데몬 기동, When 진입점 실행, Then 오류 메시지 출력 + exit code ≠ 0 으로 종료한다(기본값 fallback 없음).
3. Given TOML 에 `portfolio_hub_token` 이 있고 `.env`/OS env 에 `TREX_PORTFOLIO_HUB_TOKEN` 미설정, When 최종 설정 구성, Then `PortfolioHubConfig.token` 에 TOML 값이 주입된다(S-5).
4. Given TOML 에 필수 필드(`instance_key`) 누락, When `from_toml`/`from_config`, Then 명확한 오류로 실패한다(조용한 기본값 금지).
5. Given 저장소, When `.gitignore` 확인, Then 인스턴스 TOML 경로 패턴이 무시 대상이다.
6. Given 데몬 기동 로그, When 출력 확인, Then `portfolio_hub_token` 평문이 로그에 노출되지 않는다.
## Impacted Area
- Docs:
- `docs/project-structure.md` — 설정 파일 계층(TOML + env) (REQ-051 처리)
- Modules:
- `trex_core/runtime/config.py` — `DaemonInstanceConfig` + `from_toml` + `from_config` 병합
- 데몬 진입점(`trex_core/runtime/daemon.py:main` 또는 `__main__`) — `--config` 필수 파싱(argparse)
- `.gitignore` — 인스턴스 TOML 패턴
- Tests:
- `tests/runtime/` — TOML 로더 파싱/필수 인자/토큰 주입/누락 오류
## Notes
- dcy-009 OQ-2 Answered: `--config` 필수, fallback 없음(split-brain 위험 회피). A-5.
- NFR-4(민감값 비노출)는 본 REQ 의 gitignore + 로그 미노출 AC 로 흡수.
- DATA_TOML_SCHEMA_FULL(dcy-009 §10): 최소 필드 `instance_key`/`portfolio_hub_token`. 추가 필드는 본 REQ 구현 스키마에서 확장 가능.
- KIS 자격증명/DSN/RABBITMQ_URL 등 나머지 env 는 이번 범위에서 `.env` 유지(CONFIRM_ENV_RESIDUAL).
## Change Log
### CHG-20260607-01
- Date: 2026-06-07
- Author: Justin Ko
#### Change Summary
- dcy-009(confirmed) → REQ-048 초안 생성.
#### Intent
- per-instance TOML 로더 + `--config` 필수(FR-1, NFR-4)를 Configuration REQ 로 정규화.
#### Acceptance Criteria Delta
- Added: AC-1~6 전체
#### Impacted Area Delta
- Docs: project-structure.md (REQ-051)
- Modules: config.py, daemon 진입점, .gitignore
- Tests: tests/runtime TOML 로더
#### Delivery Trace Delta
- Batches: none (미할당)
- Releases: none
#### Revalidation Impact
- Reverification Needed: no
- Existing Implementation Invalidated: no
- Status Recommendation: keep-current
- Follow-up Action: confirm-req 로 승인.
### CHG-20260607-02
- Date: 2026-06-07
- Author: Justin Ko
#### Change Summary
- Proposed → Approved (confirm-req).
#### Intent
- 승인 게이트 통과: 필수 필드·구체 AC·Impacted Area·Change Log 완비. dcy-009(confirmed) 범위·결정(OQ-2 TOML 필수)과 모순 없음. TOML 추가 필드 스키마는 구현 단계 확장 가능 비핵심.
#### Acceptance Criteria Delta
- (변경 없음)
#### Impacted Area Delta
- (변경 없음)
#### Delivery Trace Delta
- Batches: none (미할당)
- Releases: none
#### Revalidation Impact
- Reverification Needed: no
- Existing Implementation Invalidated: no
- Status Recommendation: keep-current
- Follow-up Action: suggest-batch-reqs / draft-batch 로 delivery 편성.
### CHG-20260607-03
- Date: 2026-06-07
- Author: Justin Ko
#### Change Summary
- Status Approved → Implemented (confirm-req-implemented, 근거 batch BAT-013 release-candidate).
#### Intent
- BAT-013 verification 의 AC↔evidence(로더 6 단위: from_toml/필수 누락/token 주입 S-5/default ik/`--config` 누락 subprocess 종료 AC-2/gitignore)로 AC-1~6 충족. NFR-4(민감값) 흡수.
#### Acceptance Criteria Delta
- (변경 없음)
#### Impacted Area Delta
- (변경 없음)
#### Delivery Trace Delta
- Batches: BAT-013 (release-candidate)
- Releases: none
#### Revalidation Impact
- Reverification Needed: no
- Existing Implementation Invalidated: no
- Status Recommendation: keep-current (Implemented)
- Follow-up Action: draft-release 로 BAT-013 배포 편성. token .env 제거는 배포 rollout.
@@ -0,0 +1,144 @@
# req-051: multi-instance topology baseline 문서 정합
- Status: Implemented
- Type: Documentation
- Priority: Medium
- Owner: project maintainers
## Intent
dcy-009(다중 인스턴스 RabbitMQ 토폴로지) 구현(REQ-048~050) 완료 후 `docs/runtime-flows.md`, `docs/project-structure.md`, `docs/interface-contract.md` 세 baseline 문서를 multi-instance topology + TOML 기반 기동 기준으로 정합시킨다(dcy-009 FR-6).
## Requirement
- `docs/interface-contract.md` 에 반영한다:
- routing key 계약: directed = `{instance_key}.{channel_type}`(`trex.direct` topic), broadcast = `trex.broadcast`(fanout, 전역 control)
- 인스턴스별 큐명 규칙(`{base}.{instance_key}`, default = 리터럴 하위호환)
- `docs/runtime-flows.md` 에 반영한다:
- TOML(`--config`) 기반 기동 절차(instance_key/portfolio_hub_token 로드)
- multi-instance directed vs broadcast 라우팅 흐름, split-brain 방지(인스턴스 전용 큐) 설명
- `docs/project-structure.md` 에 반영한다:
- `topology.py` 역할 변경(고정 상수 → 동적 큐명/exchange 빌더)
- 설정 계층(TOML + env 병합), `DaemonInstanceConfig`, `--config` 진입점
- `trex.direct`/`trex.broadcast` exchange
## Acceptance Criteria
1. Given `docs/interface-contract.md`, When routing key 규칙을 찾으면, Then directed(`{instance_key}.{channel_type}`)와 broadcast(fanout) 계약 + 인스턴스 큐명 규칙이 명시되어 있다.
2. Given `docs/runtime-flows.md`, When 기동/라우팅 흐름을 찾으면, Then TOML(`--config`) 기동 + directed/broadcast 라우팅 + split-brain 방지가 서술되어 있다.
3. Given `docs/project-structure.md`, When topology/설정 항목을 찾으면, Then 동적 큐명 빌더 + `DaemonInstanceConfig`/`--config` + `trex.direct`/`trex.broadcast` 가 반영되어 있다.
## Impacted Area
- Docs:
- `docs/interface-contract.md`
- `docs/runtime-flows.md`
- `docs/project-structure.md`
- Modules: 없음 (문서 전용)
- Tests: 없음
## Notes
- REQ-048~050 구현 완료 후 batch verification 단계에서 최신 구현 상태를 반영해 수행한다(REQ-042/047 선례).
- baseline 갱신이므로 `bootstrap-baseline` 선행 불필요.
## Change Log
### CHG-20260607-01
- Date: 2026-06-07
- Author: Justin Ko
#### Change Summary
- dcy-009(confirmed) → REQ-051 초안 생성.
#### Intent
- multi-instance 구현 후 세 baseline 문서 정합(FR-6)을 Documentation REQ 로 정규화.
#### Acceptance Criteria Delta
- Added: AC-1~3 전체
#### Impacted Area Delta
- Docs: interface-contract.md, runtime-flows.md, project-structure.md
#### Delivery Trace Delta
- Batches: none (미할당)
- Releases: none
#### Revalidation Impact
- Reverification Needed: no
- Existing Implementation Invalidated: no
- Status Recommendation: keep-current
- Follow-up Action: confirm-req 로 승인 (REQ-048~050 구현 batch 이후 연계).
### CHG-20260607-02
- Date: 2026-06-07
- Author: Justin Ko
#### Change Summary
- Proposed → Approved (confirm-req).
#### Intent
- 승인 게이트 통과: 필수 필드·구체 AC·Impacted Area·Change Log 완비. baseline 정합 문서 REQ로 REQ-048~050 구현 후 verification 단계 수행.
#### Acceptance Criteria Delta
- (변경 없음)
#### Impacted Area Delta
- (변경 없음)
#### Delivery Trace Delta
- Batches: none (미할당)
- Releases: none
#### Revalidation Impact
- Reverification Needed: no
- Existing Implementation Invalidated: no
- Status Recommendation: keep-current
- Follow-up Action: suggest-batch-reqs / draft-batch 로 delivery 편성(REQ-048~050 이후 연계).
### CHG-20260607-03
- Date: 2026-06-07
- Author: Justin Ko
#### Change Summary
- Status Approved → Implemented (confirm-req-implemented, 근거 batch BAT-013 release-candidate).
#### Intent
- BAT-013 에서 baseline 3문서 정합 완료: interface-contract(multi-instance routing: 큐명 규칙·trex.direct/trex.broadcast·routing key), runtime-flows(`--config` 기동 + multi-instance 라우팅 흐름 + split-brain 방지), project-structure(동적 큐명 빌더·DaemonInstanceConfig/`--config`·exchange). AC-1~3 충족.
#### Acceptance Criteria Delta
- (변경 없음)
#### Impacted Area Delta
- (변경 없음)
#### Delivery Trace Delta
- Batches: BAT-013 (release-candidate)
- Releases: none
#### Revalidation Impact
- Reverification Needed: no
- Existing Implementation Invalidated: no
- Status Recommendation: keep-current (Implemented)
- Follow-up Action: draft-release 로 BAT-013 배포 편성.
@@ -0,0 +1,147 @@
# req-050: trex.direct(topic) + trex.broadcast(fanout) exchange + 바인딩
- Status: Implemented
- Type: Integration
- Priority: High
- Owner: project maintainers
## Intent
인스턴스 지정 라우팅과 전역 브로드캐스트를 위해 두 exchange 를 도입한다. `trex.direct`(topic)는 strategy/운영자가 특정 인스턴스에만 메시지를 보낼 때 routing key `{instance_key}.{channel_type}` 으로 라우팅하고, `trex.broadcast`(fanout)는 global kill-switch 등 전체 인스턴스 대상 제어를 모든 control 큐에 동시 전달한다(dcy-009 FR-3, FR-4). routing key 계약을 인터페이스로 정의한다.
## Requirement
- `topology.py` 에 `DIRECT_EXCHANGE = "trex.direct"`(type topic, durable), `BROADCAST_EXCHANGE = "trex.broadcast"`(type fanout, durable) 상수와 routing key 빌더(`direct_routing_key(instance_key, channel_type)` → `{instance_key}.{channel_type}`)를 추가한다.
- `declare_topology(instance_key)` 가 두 exchange 를 선언하고, 각 인스턴스의 intent/control/snapshot 큐를 `trex.direct` 에 routing key `{instance_key}.intent`/`.control`/`.snapshot` 으로 바인딩한다(FR-3).
- 각 인스턴스의 **control 큐**를 `trex.broadcast`(fanout)에 추가 바인딩(dual binding)한다(FR-4). 전역 제어 메시지는 `trex.broadcast` 로 publish 하면 모든 인스턴스 control 큐에 전달된다.
- `default` 인스턴스 하위호환: default 의 큐(리터럴명, REQ-049)도 동일 규칙으로 `trex.direct`(`default.intent` 등) + control dual binding 된다. 기존 직접 큐 publish 경로(reply-to RPC)는 유지되어 회귀가 없어야 한다(FR-5).
- routing key 계약은 `docs/interface-contract.md` 에 명시한다(REQ-051): directed = `{instance_key}.{channel_type}`, broadcast = fanout(키 무시).
## Acceptance Criteria
1. Given `declare_topology("inst-1")`, When 선언, Then `trex.direct`(topic) + `trex.broadcast`(fanout) exchange 가 durable 로 선언된다.
2. Given `declare_topology("inst-1")`, When 바인딩 확인, Then `trex.intent.req.inst-1`/`...control...`/`...snapshot...` 가 `trex.direct` 에 routing key `inst-1.intent`/`inst-1.control`/`inst-1.snapshot` 으로 바인딩된다(FR-3).
3. Given `direct_routing_key("inst-1","control")`, When 호출, Then `"inst-1.control"` 을 반환한다.
4. Given `inst-1`/`inst-2` 두 인스턴스 큐가 선언됨, When `trex.direct` 에 `inst-1.intent` 로 publish, Then `trex.intent.req.inst-1` 에만 전달되고 `...inst-2` 에는 전달되지 않는다(S-2).
5. Given control 큐 dual binding, When `trex.broadcast` 에 publish, Then `trex.control.req.inst-1` 과 `...inst-2` control 큐 모두에 전달된다(FR-4, S-3).
6. Given `instance_key="default"`, When 전체 회귀, Then 기존 RPC(reply-to) 경로 동작 동일·회귀 없음(FR-5).
## Impacted Area
- Docs:
- `docs/interface-contract.md` — routing key 계약(`{instance_key}.{channel_type}`, broadcast) (REQ-051 처리)
- `docs/runtime-flows.md` — directed vs broadcast 라우팅 흐름 (REQ-051 처리)
- Modules:
- `trex_core/adapters/messaging/topology.py` — DIRECT/BROADCAST exchange + routing key 빌더
- `trex_core/adapters/messaging/rabbitmq.py` — exchange 선언 + 큐 바인딩(direct per-instance + control dual) + broadcast publish 경로
- Tests:
- `tests/adapters/messaging/` — exchange 선언 + 바인딩 + directed 라우팅 격리 + broadcast 전달(인프라 게이트)
## Notes
- R-3(broadcast 중복 처리): kill-switch/pause 핸들러는 idempotent(REQ-036 패턴/advisory lock)라 중복 전달 무해.
- strategy 모듈(orb-v1) publish 코드 변경은 Out of Scope — 본 REQ 는 exchange/binding/routing key 계약만 정의(CONFIRM_STRATEGY_MODULE_OUT_OF_SCOPE).
- broadcast publish helper 는 transport 에 추가하거나 기존 publish 경로 재사용 — 구현에서 결정.
## Change Log
### CHG-20260607-01
- Date: 2026-06-07
- Author: Justin Ko
#### Change Summary
- dcy-009(confirmed) → REQ-050 초안 생성.
#### Intent
- trex.direct/trex.broadcast exchange + 바인딩 + routing key 계약(FR-3/FR-4)을 Integration REQ 로 정규화.
#### Acceptance Criteria Delta
- Added: AC-1~6 전체
#### Impacted Area Delta
- Docs: interface-contract.md, runtime-flows.md (REQ-051)
- Modules: topology.py, rabbitmq.py
- Tests: tests/adapters/messaging (인프라 게이트)
#### Delivery Trace Delta
- Batches: none (미할당)
- Releases: none
#### Revalidation Impact
- Reverification Needed: no
- Existing Implementation Invalidated: no
- Status Recommendation: keep-current
- Follow-up Action: confirm-req 로 승인.
### CHG-20260607-02
- Date: 2026-06-07
- Author: Justin Ko
#### Change Summary
- Proposed → Approved (confirm-req).
#### Intent
- 승인 게이트 통과: 필수 필드·구체 AC·Impacted Area·Change Log 완비. dcy-009(confirmed) FR-3/FR-4 와 일치. strategy publish 변경은 Out of Scope(계약만 정의) — blocker 아님.
#### Acceptance Criteria Delta
- (변경 없음)
#### Impacted Area Delta
- (변경 없음)
#### Delivery Trace Delta
- Batches: none (미할당)
- Releases: none
#### Revalidation Impact
- Reverification Needed: no
- Existing Implementation Invalidated: no
- Status Recommendation: keep-current
- Follow-up Action: suggest-batch-reqs / draft-batch 로 delivery 편성.
### CHG-20260607-03
- Date: 2026-06-07
- Author: Justin Ko
#### Change Summary
- Status Approved → Implemented (confirm-req-implemented, 근거 batch BAT-013 release-candidate).
#### Intent
- BAT-013 verification 의 AC↔evidence(routing key 단위 + 인프라 게이트: trex.direct/trex.broadcast 선언 + directed 라우팅 격리 S-2 + broadcast dual S-3, 301 passed)로 AC-1~6 충족.
#### Acceptance Criteria Delta
- (변경 없음)
#### Impacted Area Delta
- (변경 없음)
#### Delivery Trace Delta
- Batches: BAT-013 (release-candidate)
- Releases: none
#### Revalidation Impact
- Reverification Needed: no
- Existing Implementation Invalidated: no
- Status Recommendation: keep-current (Implemented)
- Follow-up Action: draft-release 로 BAT-013 배포 편성. strategy publish 변경은 별도(Out of Scope).
@@ -0,0 +1,147 @@
# req-049: instance_key 기반 동적 큐명 + default 하위호환
- Status: Implemented
- Type: Interface
- Priority: High
- Owner: project maintainers
## Intent
다중 인스턴스가 동일 RabbitMQ 에서 경쟁 소비(round-robin)로 split-brain 을 일으키지 않도록, 수신 큐명을 `instance_key` 기반으로 인스턴스별 분리한다. `topology.py` 의 고정 큐명 상수를 동적 함수로 교체하고, `declare_topology(instance_key)` + 각 consumer 가 인스턴스 전용 큐를 구독하게 한다. `instance_key="default"` 는 기존 리터럴 큐명을 그대로 사용해 하위호환을 보장한다(dcy-009 FR-2, FR-5, NFR-1/2/3).
## Requirement
- `topology.py` 의 고정 큐명 상수(`SNAPSHOT_REQ_QUEUE` 등)를 동적 빌더 함수로 교체/보강한다: `intent_queue(instance_key)`, `snapshot_queue(instance_key)`, `control_queue(instance_key)`.
- **하위호환 규칙**: `instance_key in (None, "default")` 이면 기존 리터럴 큐명(`trex.intent.req` / `trex.snapshot.req` / `trex.control.req`)을 반환한다. 그 외에는 `{base}.{instance_key}` 를 반환한다(FR-5: default 무회귀).
- `rabbitmq.py` `declare_topology(instance_key=...)` 가 인스턴스 전용 큐를 선언한다. `daemon.py` 의 intent/snapshot/control consumer 가 동일 `instance_key` 의 전용 큐를 `consume_rpc_forever` 로 구독한다.
- `daemon.py` 는 설정(REQ-048 TOML)에서 읽은 `instance_key` 를 큐 선언/구독에 일관되게 전달한다.
- 기존 pika `BlockingConnection` per-thread 패턴을 유지한다 — 큐 분리로 인해 추가 스레드/커넥션을 만들지 않는다(NFR-2). 워커 스레드 수 불변.
## Acceptance Criteria
1. Given `instance_key="inst-1"`, When `intent_queue("inst-1")`/`snapshot_queue`/`control_queue`, Then 각각 `trex.intent.req.inst-1`/`trex.snapshot.req.inst-1`/`trex.control.req.inst-1` 를 반환한다.
2. Given `instance_key="default"`(또는 None), When 동일 빌더, Then 기존 리터럴 `trex.intent.req`/`trex.snapshot.req`/`trex.control.req` 를 반환한다(하위호환).
3. Given `declare_topology("inst-1")`, When 선언, Then `*.inst-1` 인스턴스 전용 큐가 선언된다(S-1 일부).
4. Given `instance_key="default"` 데몬, When 전체 pytest 스위트 실행, Then 회귀 없음(FR-5/NFR-1, 기존 큐명 테스트 통과).
5. Given 데몬 기동, When 워커 스레드 확인, Then per-thread connection 패턴 유지 + 스레드 수 불변(NFR-2).
6. Given `instance_key="inst-1"` 와 `inst-2` 동시 운영(전용 큐 분리), When 각자 큐만 소비, Then 한 인스턴스의 snapshot/intent 소비가 다른 인스턴스 큐에 영향을 주지 않는다(NFR-3 split-brain 방지 기반).
## Impacted Area
- Docs:
- `docs/runtime-flows.md`, `docs/project-structure.md` — 동적 큐명/토폴로지 (REQ-051 처리)
- Modules:
- `trex_core/adapters/messaging/topology.py` — 동적 큐명 빌더(default 하위호환)
- `trex_core/adapters/messaging/rabbitmq.py` — `declare_topology(instance_key)`
- `trex_core/runtime/daemon.py` — consumer 가 instance_key 전용 큐 구독
- Tests:
- `tests/adapters/messaging/` — 큐명 빌더(default vs named) + declare/consume 분리 + 기존 회귀
## Notes
- 하위호환 핵심: default = 리터럴 큐명(특수 처리)이라 기존 테스트/배포 무영향. FR-2 의 suffix 규칙은 named instance 에 적용.
- NFR-3(split-brain 방지)는 인스턴스 전용 큐로 구조적으로 달성 — 다중 인스턴스 e2e 는 verification 에서 확인.
- exchange/binding(트래픽 라우팅)은 REQ-050.
## Change Log
### CHG-20260607-01
- Date: 2026-06-07
- Author: Justin Ko
#### Change Summary
- dcy-009(confirmed) → REQ-049 초안 생성.
#### Intent
- instance_key 동적 큐명 + default 하위호환(FR-2/FR-5)을 Interface REQ 로 정규화.
#### Acceptance Criteria Delta
- Added: AC-1~6 전체
#### Impacted Area Delta
- Docs: runtime-flows.md, project-structure.md (REQ-051)
- Modules: topology.py, rabbitmq.py, daemon.py
- Tests: tests/adapters/messaging
#### Delivery Trace Delta
- Batches: none (미할당)
- Releases: none
#### Revalidation Impact
- Reverification Needed: no
- Existing Implementation Invalidated: no
- Status Recommendation: keep-current
- Follow-up Action: confirm-req 로 승인.
### CHG-20260607-02
- Date: 2026-06-07
- Author: Justin Ko
#### Change Summary
- Proposed → Approved (confirm-req).
#### Intent
- 승인 게이트 통과: 필수 필드·구체 AC·Impacted Area·Change Log 완비. dcy-009(confirmed) FR-2/FR-5 와 일치. default 하위호환 규칙 명확.
#### Acceptance Criteria Delta
- (변경 없음)
#### Impacted Area Delta
- (변경 없음)
#### Delivery Trace Delta
- Batches: none (미할당)
- Releases: none
#### Revalidation Impact
- Reverification Needed: no
- Existing Implementation Invalidated: no
- Status Recommendation: keep-current
- Follow-up Action: suggest-batch-reqs / draft-batch 로 delivery 편성.
### CHG-20260607-03
- Date: 2026-06-07
- Author: Justin Ko
#### Change Summary
- Status Approved → Implemented (confirm-req-implemented, 근거 batch BAT-013 release-candidate).
#### Intent
- BAT-013 verification 의 AC↔evidence(빌더 단위 named suffix/default 리터럴 + declare_topology(instance_key) + 전체 301 passed 로 default 무회귀)로 AC-1~6 충족. NFR-1/2/3 흡수.
#### Acceptance Criteria Delta
- (변경 없음)
#### Impacted Area Delta
- (변경 없음)
#### Delivery Trace Delta
- Batches: BAT-013 (release-candidate)
- Releases: none
#### Revalidation Impact
- Reverification Needed: no
- Existing Implementation Invalidated: no
- Status Recommendation: keep-current (Implemented)
- Follow-up Action: draft-release 로 BAT-013 배포 편성.
+8 -4
View File
@@ -74,7 +74,7 @@
## Next Requirement ID
- Current: `REQ-048`
- Current: `REQ-052`
- Rule: `Requirement Register`에 있는 가장 큰 REQ 번호보다 정확히 1 커야 한다.
## Requirement Register
@@ -128,11 +128,15 @@
| REQ-045 | kill-switch 실행 — 범위별 holding 즉시 청산 + 이력 | Interface | Implemented | High | project maintainers | [req-045](./Interface/req-045_kill-switch-execution.md) |
| REQ-046 | kill-switch 당일 제외 masking + 자동해제 + resume | Interface | Implemented | High | project maintainers | [req-046](./Interface/req-046_killswitch-day-exclusion-masking-and-resume.md) |
| REQ-047 | kill-switch baseline 문서 정합 | Documentation | Implemented | Medium | project maintainers | [req-047](./Documentation/req-047_killswitch-baseline-alignment.md) |
| REQ-048 | per-instance TOML 설정 로더 + --config 필수 | Configuration | Implemented | High | project maintainers | [req-048](./Configuration/req-048_per-instance-toml-config.md) |
| REQ-049 | instance_key 기반 동적 큐명 + default 하위호환 | Interface | Implemented | High | project maintainers | [req-049](./Interface/req-049_instance-key-dynamic-queue-naming.md) |
| REQ-050 | trex.direct(topic) + trex.broadcast(fanout) exchange + 바인딩 | Integration | Implemented | High | project maintainers | [req-050](./Integration/req-050_direct-broadcast-exchanges-and-binding.md) |
| REQ-051 | multi-instance topology baseline 문서 정합 | Documentation | Implemented | Medium | project maintainers | [req-051](./Documentation/req-051_multi-instance-baseline-alignment.md) |
## Recent Change Log Summary
- 2026-06-07 :: REQ-048~051 Approved → Implemented (confirm-req-implemented): BAT-013 verify 완료(신규 9 단위 + RabbitMQ 라우팅 2, 단위 271 passed, 인프라 게이트 301 passed(directed 격리 S-2 + broadcast dual S-3 + default 무회귀 S-4), ruff clean). per-instance TOML 로더+`--config`, instance_key 동적 큐명(default 리터럴 하위호환), trex.direct/trex.broadcast exchange+바인딩, baseline 3문서 정합. (confirm-req-implemented)
- 2026-06-07 :: REQ-048~051 Proposed → Approved (confirm-req): dcy-009 multi-instance 4개 전원 승인 게이트 통과. dcy-009(confirmed) FR/OQ 와 모순 없음, 필수 필드·구체 AC 완비. strategy publish 변경·TOML 추가필드는 비핵심 후속. (confirm-req)
- 2026-06-07 :: REQ-048~051 생성 (draft-req): dcy-009(다중 인스턴스 RabbitMQ 토폴로지, confirmed) → 4개 REQ 초안. per-instance TOML 로더+`--config` 필수(048), instance_key 동적 큐명+default 하위호환(049), trex.direct/trex.broadcast exchange+바인딩(050), baseline 정합(051). NFR-1/2/3/4 는 048/049 AC 로 흡수. (draft-req)
- 2026-06-07 :: REQ-043~047 Approved → Implemented (confirm-req-implemented): BAT-012 verify 완료(신규 26 단위 + killswitch PG 5, 단위 262 passed, 인프라 게이트 290 passed(마이그레이션 005 round-trip), ruff clean, paper 데몬 control RPC e2e PASS). `trex.control.req` dispatch, KillSwitchService(범위별 청산+이력), killswitch_exclusion store/005, day-scoped masking+resume, baseline 3문서 정합. (confirm-req-implemented)
- 2026-06-07 :: REQ-043~047 Proposed → Approved (confirm-req): kill-switch 5개 전원 승인 게이트 통과. dcy-008(confirmed) 범위·결정(OQ-1/2/4 Answered, OQ-3 Deferred)과 모순 없음, 필수 필드·구체 AC·Impacted Area·Change Log 완비. 큐 rename 호환·전역 sentinel·pending_buy 는 비핵심 후속으로 blocker 아님. (confirm-req)
- 2026-06-07 :: REQ-043~047 생성 (draft-req): dcy-008(비상 정지/kill-switch, confirmed) → 5개 REQ 초안. 제어 채널 `trex.control.req`+action dispatch(043), day-scoped 제외 store+마이그레이션(044), kill-switch 실행=범위별 holding 순차 sell()+이력(045), 당일 제외 masking+자동복귀+resume_kill_switch(046), baseline 정합(047). NFR-1/2/3 은 045/046 AC 로 흡수, OQ-3(pending_buy) REQ Deferred. (draft-req)
- 2026-06-07 :: REQ-038 CHG-20260607-05 Approved → Implemented (confirm-req-implemented): tr_id 정정(`FHKST01010100`) 구현·재검증 — kis.py 수정, AC-7 테스트 정정, 단위 236 passed/ruff clean, 실 KIS LIVE `get_current_price=329000`(rt_cd=0) 확인.
- 2026-06-07 :: REQ-038 CHG-20260607-04 Implemented → Approved (change-req): 현재가 시세 tr_id 정정 `TTTC8397R`/`VTTC8397R` → `FHKST01010100`. 배포 전 실 KIS LIVE 진단에서 구 tr_id 가 "없는 서비스 코드"(rt_cd=1)로 거부 → LIVE 에서 손절/익절 silent 미발동 확인. 표준 시세 tr 정상(stck_prpr=329000). (change-req)