feat: implement portfolio UUID migration and v1 API finalization (BAT-006, BAT-001 re-verified)

Core Changes:
- Migrate portfolio_id from string to UUID in schema and repository layer
- Update portfolio CLI commands to use name+user combination for identification
- Update HTTP API to accept portfolio_id as UUID path parameter
- Implement portfolio ownership validation in repository queries

Requirements Completed (REQ-016 to REQ-019):
- REQ-016: portfolios table UUID schema migration with data migration script
- REQ-017: CLI portfolio commands with name+user identification
- REQ-018: HTTP API portfolio path parameters and v1 asset_id UUID returns
- REQ-019: Portfolio UUID baseline documentation updates

BAT-001 Re-verification:
- REQ-004~010 marked as Implemented after verification evidence validation
- All v1 API endpoints verified with comprehensive test coverage

Documentation Updates:
- Update data-model.md with portfolio UUID structure
- Update interface-contract.md with UUID path parameters
- Update runtime-flows.md with ownership-based resolution flows
- Update project-structure.md with database layer changes
- Add migration script for portfolio UUID transition

Testing:
- Add test_portfolio_uuid.py for migration validation
- Update test_cli.py and test_portfolio_ownership.py for ownership checks
This commit is contained in:
2026-05-11 09:27:57 +00:00
parent 9ed307555a
commit 829629fa8f
46 changed files with 3260 additions and 220 deletions
@@ -1,6 +1,6 @@
# BAT-001 Index
- Status: in-delivery
- Status: release-candidate
- Batch: BAT-001
- Profile: standard
- Created: 2026-05-02
@@ -61,7 +61,7 @@
| Stage | File | Status |
|-------|------|--------|
| Planning | [planning.md](./planning.md) | draft |
| Design | [design.md](./design.md) | draft |
| Implementation | [implementation.md](./implementation.md) | draft |
| Verification | [verification.md](./verification.md) | draft |
| Planning | [planning.md](./planning.md) | confirmed |
| Design | [design.md](./design.md) | confirmed |
| Implementation | [implementation.md](./implementation.md) | completed |
| Verification | [verification.md](./verification.md) | re-verified |
@@ -0,0 +1,92 @@
# BAT-006 Design
- Status: confirmed
- Batch: BAT-006
## Architecture Summary
`portfolios.portfolio_id`를 사용자 지정 TEXT PK에서 서버 생성 UUID(TEXT)로 교체한다. 기존 `portfolio_id` 문자열은 `name` 컬럼으로 이전하고, `(owner_user_id, name)` 복합 UNIQUE 제약을 DB 레벨에서 도입해 사용자 범위 내 이름 유일성을 보장한다. CLI는 `<name>` + `--user` 조합으로 portfolio를 식별하고 내부에서 `get_portfolio_by_name_and_user()`를 통해 UUID를 조회한다. HTTP API path parameter와 `GET /v1/assets` `asset_id` 반환은 UUID 기반으로 전환된다.
## Baseline vs Batch 변경점
### docs/project-structure.md (현재 baseline)
- `app/repositories/postgres.py`: SQL 세부사항 담당으로만 기술 — `get_portfolio_by_name_and_user()` 미기재
- `app/domain/models.py`: `portfolio_id` 필드 의미 변경 미기재
### docs/runtime-flows.md (현재 baseline)
- `CLI Operational Command Execution` flow: name+user 식별 방식 미기재
- `v1 API — GET /v1/assets` flow: `asset_id` 필드 언급 없음
### docs/data-model.md (현재 baseline)
- `portfolio_id :: text :: stable portfolio primary key` — UUID 의미 변경 미기재
- `name` 컬럼 별도 기재 없음
- `(owner_user_id, name)` UNIQUE 제약 미기재
### docs/interface-contract.md (현재 baseline)
- `GET /v1/assets` 응답 명세 없음 (bat-001에서 추가 예정이었으나 아직 반영 안 됨)
- trex-basic 동시 배포 계획 없음
## Changed Areas
- Docs:
- `docs/project-structure.md`
- `docs/runtime-flows.md`
- `docs/data-model.md`
- `docs/interface-contract.md`
- `docs/discovery/dcy-002_20260502_trex-basic-consumer-api-contract.md` (A-3 철회 주석)
- Modules:
- `migrations/versions/20260511_0015_portfolio_uuid_migration.py` (신규 Alembic revision)
- `app/domain/models.py` (주석 없음, Portfolio dataclass 구조 변경 없음 — 기존 `portfolio_id` + `name` 필드 유지)
- `app/repositories/postgres.py`:
- `create_portfolio()`: `portfolio_id` 파라미터 제거, 서버 UUID 생성
- `get_portfolio_by_name_and_user()` 신규 메서드
- `ensure_schema()`: `(owner_user_id, name)` UNIQUE 제약 추가
- `update_portfolio()`: UniqueViolation 시 ValueError("이미 사용 중인 이름입니다") 반환
- `app/domain/services.py`: `PortfolioDomainRepository` Protocol 업데이트
- `app/schemas/portfolio.py`:
- `CreatePortfolioRequest`: `portfolio_id` 필드 제거, `name: str` 필수로 변경, name 유효성 validator 추가
- `app/services/portfolio_service.py`: `create_portfolio()` 호출부 업데이트
- `app/cli/commands/portfolio.py`:
- `create`: `portfolio_id` Argument → `name` Argument (필수), `--name` Option 제거
- `status`, `buy`, `sell`, `invest`, `transfer`, `reset`, `delete`: `portfolio_id` → `name` Argument + `--user` Option
- `update`: `portfolio_id` → `name` Argument + `--user` Option + `--new-name` Option (이름 변경용)
- `_resolve_portfolio_id()` helper 추가
- `_validate_portfolio_name()` helper 추가
- Interfaces:
- CLI: `portfolio <subcommand> <portfolio_id>` → `portfolio <subcommand> <name> --user <username>`
- HTTP API: `POST /portfolios` request body: `portfolio_id` 필드 제거, `name` 필수화
- HTTP API: `GET /v1/assets` `assets[].asset_id` → UUID 형식 반환
## Key Decisions
1. **portfolio_id 컬럼 타입 TEXT 유지**: PostgreSQL UUID 타입 변환은 이번 범위 밖. `uuid.uuid4()` 문자열을 TEXT로 저장.
2. **get_portfolio_by_name_and_user() 공유**: CLI와 HTTP API 식별 모두 이 단일 repository 메서드 사용. Protocol에도 추가.
3. **이름 대소문자 구분**: `TV`와 `tv`는 다른 이름. DB UNIQUE 제약도 case-sensitive.
4. **단일 Alembic revision**: name 컬럼 추가 (IF NOT EXISTS), UUID 교체, FK 4개 테이블 갱신, UNIQUE 제약을 하나의 revision에서 수행. IF NOT EXISTS 가드로 재실행 안전성 보장.
5. **update 명령 name 변경**: CLI `update` 명령은 `--new-name` Option으로 이름 변경. `--force` 없으면 confirm 프롬프트. DB UNIQUE 위반 시 `이미 사용 중인 이름입니다: {name}` 오류.
6. **name 유효성**: `[a-zA-Z0-9._:-]`, 1~100자. Pydantic validator + CLI 레이어에서 이중 검사.
7. **comment/tag sub-commands**: REQ-017 명시 범위 밖. 이번 batch에서 변경하지 않음 (UUID 직접 전달 필요).
8. **dcy-002 A-3 공식 철회**: REQ-018 구현 시 해당 discovery 문서에 주석 추가.
## Edge Cases
- **기존 portfolio_id 값이 UUID 형식인 경우**: migration이 이미 적용된 것으로 판단하는 guard (UNIQUE constraint 존재 여부로 판단). 재실행 시 skip.
- **FK 고아 레코드**: migration revision 내 트랜잭션 후 orphan 검증 쿼리. 고아 발견 시 RuntimeError 발생.
- **name 허용 문자 범위 외 입력**: CLI validation + Pydantic validator에서 조기 거부.
- **동일 owner_user_id + 동일 name**: DB UNIQUE 위반 → `create_portfolio()`에서 ValueError, `update_portfolio()`에서 UniqueViolation catch → ValueError.
- **존재하지 않는 name+user 조합**: `get_portfolio_by_name_and_user()` None 반환 → `포트폴리오를 찾을 수 없습니다: {name} (user: {username})`.
- **revision 재실행**: `already_migrated` guard (UNIQUE constraint 유무 확인). 이미 적용됐으면 UUID 교체 블록 전체 skip.
- **migration 중 portfolio_tags PK 업데이트**: PostgreSQL은 PK 값 UPDATE 허용. FK 제약 없으므로 안전.
## Architecture Impact
both (project-structure + runtime-flows)
## Reference Doc Update Plan
- project-structure.md:
- `app/repositories/postgres.py` 항목: `get_portfolio_by_name_and_user(owner_user_id, name) → Portfolio | None` 추가
- `app/domain/models.py` 항목: `Portfolio.portfolio_id`가 서버 생성 UUID임을 명시
- runtime-flows.md:
- `CLI Operational Command Execution` flow: `<name>` + `--user` → `get_portfolio_by_name_and_user()` → UUID 조회 흐름 추가
- `v1 API — GET /v1/assets` flow: `asset_id = portfolio_id (UUID)` 반환 명시
@@ -0,0 +1,63 @@
# BAT-006 Implementation
- Status: completed
- Batch: BAT-006
## Plan Summary
- REQ-016: 신규 Alembic revision(name 컬럼 추가, portfolio_id UUID 교체, FK 4개 테이블 atomic 갱신, UNIQUE 제약 추가), Portfolio dataclass `name` 필드 추가, `create_portfolio()` UUID 생성, `get_portfolio_by_name_and_user()` 신규 메서드
- REQ-017: CLI portfolio 명령 argument/option 전면 전환(`portfolio_id` → `<name> --user`), `(user_id, name)` → UUID 서비스 레이어, name 유효성 검사, update name guard
- REQ-018: HTTP API path parameter UUID 전환, `GET /v1/assets` `asset_id` UUID 직렬화, dcy-002 A-3 철회 주석
- REQ-019: baseline 문서 4개 갱신(project-structure.md, runtime-flows.md, data-model.md, interface-contract.md)
## Changed Files
### New Files
- `migrations/versions/20260511_0015_portfolio_uuid_migration.py` — Alembic revision; idempotency guard via uq_portfolios_owner_name constraint presence check; atomic per-portfolio FK update across portfolio_ledger/portfolio_comments/portfolio_tags/ledger_trades
- `tests/test_portfolio_uuid.py` — 14 tests covering UUID creation, name+user resolution, alice/bob isolation, buy/delete flow, name validation, unknown user/portfolio errors, update rename
### Modified Files
- `app/repositories/postgres.py`
- `ensure_schema()`: replaced `ADD CONSTRAINT IF NOT EXISTS` (not valid PostgreSQL syntax) with `DO $$ IF NOT EXISTS ... ALTER TABLE ADD CONSTRAINT $$` pattern for uq_portfolios_owner_name
- `create_portfolio()`: signature changed from `(portfolio_id: str, *, ..., name: str, ...)` to `(name: str, *, ...)` with server-generated UUID; catches psycopg UniqueViolation → ValueError("이미 사용 중인 이름입니다: {name}")
- Added `get_portfolio_by_name_and_user(owner_user_id, name) -> Portfolio | None`
- `update_portfolio()`: catches UniqueViolation on name update → ValueError("이미 사용 중인 이름입니다: {new_name}")
- `app/domain/services.py` — `PortfolioDomainRepository` Protocol updated: `create_portfolio` new signature, `get_portfolio_by_name_and_user` added, `list_portfolios` Protocol params aligned
- `app/schemas/portfolio.py` — `CreatePortfolioRequest`: removed `portfolio_id` field; `name: str` now required with regex validation `[a-zA-Z0-9._:-]{1,100}`
- `app/services/portfolio_service.py` — `create_portfolio()`: call site updated to `self._repository.create_portfolio(payload.name, owner_user_id=...)`
- `app/cli/commands/portfolio.py` — Full rewrite for REQ-017: `_NAME_RE`, `_validate_portfolio_name()`, `_resolve_portfolio_id()` helpers; all 8 mutation commands switched to `<name> --user`; `update` uses `--new-name` with `--force` guard; `comment`/`tag` sub-commands keep `portfolio_id` (UUID) directly
- `docs/discovery/dcy-002_20260502_trex-basic-consumer-api-contract.md` — A-3 철회 주석 추가
- `docs/data-model.md` — portfolios.portfolio_id → UUID, name column, uq_portfolios_owner_name constraint documented
- `docs/project-structure.md` — Added `get_portfolio_by_name_and_user()` to repositories section
- `docs/runtime-flows.md` — Added CLI portfolio name+user resolution flow; GET /v1/assets asset_id UUID note
- `docs/interface-contract.md` — Added v1 consumer API section with simultaneous deployment constraint; CLI portfolio inputs updated
- `tests/test_cli.py` — FakeRepo: `create_portfolio` new signature (generates UUID), `get_portfolio_by_name_and_user` added; `_portfolio()`: name changed to "005930:tv"; 5 tests updated to pass --user and adjust assertions
- `tests/test_portfolio_ownership.py` — Fixed POST body to use `name` instead of `portfolio_id`
## Execution Log
1. Created Alembic migration `20260511_0015_portfolio_uuid_migration.py` (REQ-016)
2. Updated `app/repositories/postgres.py`: ensure_schema UNIQUE constraint (DO $$ pattern), create_portfolio UUID generation, get_portfolio_by_name_and_user, update_portfolio UniqueViolation catch (REQ-016)
3. Updated `app/domain/services.py` Protocol (REQ-016)
4. Updated `app/schemas/portfolio.py`: removed portfolio_id, name required + validated (REQ-016, REQ-017)
5. Updated `app/services/portfolio_service.py`: create_portfolio call site (REQ-016)
6. Rewrote `app/cli/commands/portfolio.py`: name+user pattern for all mutation commands (REQ-017)
7. Added dcy-002 A-3 철회 주석 (REQ-018)
8. Updated 4 baseline docs (REQ-019)
9. Created `tests/test_portfolio_uuid.py` with 14 tests
10. Fixed `tests/test_cli.py` FakeRepo + 5 test cases
11. Fixed `tests/test_portfolio_ownership.py` POST body field
12. Fixed `ensure_schema()` syntax: `ADD CONSTRAINT IF NOT EXISTS` → DO $$ block (PostgreSQL compatibility)
## Validation
- Full test suite: **129 passed, 0 failed**
- `tests/test_portfolio_uuid.py`: 14/14 passed (UUID generation, name+user resolution, alice/bob isolation, name validation, error messages)
- `tests/test_cli.py`: 49/49 passed (including updated tests for new CLI interface)
- `tests/test_portfolio_ownership.py`: 4/4 passed
## Remaining Risks
- Alembic migration `20260511_0015` not verified against real PostgreSQL instance (unit tests use FakeRepo only)
- trex-basic simultaneous deployment required: GET /v1/assets asset_id changes from TEXT to UUID format
- Existing `comment` and `tag` sub-commands still take `portfolio_id` (UUID) directly; operators must obtain UUID from `portfolio list --user` output
@@ -0,0 +1,36 @@
# BAT-006 Index
- BAT ID: BAT-006
- Profile: standard
- Status: release-candidate
- Source Discovery: dcy-004
- Date Created: 2026-05-11
- Folder: docs/batches/bat-006_20260511_portfolio-uuid-migration/
## Included REQ
| REQ | Title | Type | Priority |
| --- | --- | --- | --- |
| REQ-016 | portfolios 테이블 UUID 전환 및 데이터 마이그레이션 | Migration | High |
| REQ-017 | CLI portfolio 명령 — name+user 조합 식별 방식 전환 | Interface | High |
| REQ-018 | HTTP API portfolio path parameter UUID 전환 및 v1 asset_id UUID 반환 | Interface | High |
| REQ-019 | portfolio UUID 전환 관련 baseline 문서 갱신 | Documentation | Medium |
## Scope Summary
- `portfolios.portfolio_id`를 사용자 지정 TEXT에서 서버 생성 UUID(TEXT)로 교체하고, 기존 `portfolio_id` 문자열을 `name` 컬럼으로 이전한다.
- `(owner_user_id, name)` 복합 UNIQUE 제약을 DB 레벨에서 도입해 사용자 범위 내 이름 유일성을 보장한다.
- 기존 데이터와 FK 참조 테이블(portfolio_ledger, portfolio_comments, portfolio_tags, ledger_trades)을 단일 Alembic revision으로 atomic하게 마이그레이션한다.
- CLI portfolio 뮤테이션 명령을 `<name>` + `--user <username>` 조합 식별 방식으로 전환하고, 내부에서 `(owner_user_id, name)` → UUID 단일 쿼리 조회로 처리한다.
- HTTP API path parameter 및 `GET /v1/assets` `asset_id` 반환을 UUID 기반으로 전환하고, dcy-002 A-3 조항을 공식 철회한다.
- 위 변경 사항을 baseline 문서(docs/project-structure.md, docs/runtime-flows.md, docs/data-model.md, docs/interface-contract.md)에 반영한다.
## Delivery Documents
| Stage | File | Status |
| --- | --- | --- |
| Index | [index.md](./index.md) | release-candidate |
| Planning | [planning.md](./planning.md) | confirmed |
| Design | [design.md](./design.md) | confirmed |
| Implementation | [implementation.md](./implementation.md) | completed |
| Verification | [verification.md](./verification.md) | passed |
@@ -0,0 +1,71 @@
# BAT-006 Planning
- Status: confirmed
- Batch: BAT-006
- Profile: standard
## Included REQ
- REQ-016: portfolios 테이블 UUID 전환 및 데이터 마이그레이션
- REQ-017: CLI portfolio 명령 — name+user 조합 식별 방식 전환
- REQ-018: HTTP API portfolio path parameter UUID 전환 및 v1 asset_id UUID 반환
- REQ-019: portfolio UUID 전환 관련 baseline 문서 갱신
## Delivery Plan
### 구현 순서
1. **REQ-016** — Alembic revision 추가 및 도메인/레포지터리 수정
- `migrations/versions/`: 신규 revision (name 컬럼 추가, 기존 portfolio_id → name 복사, UUID 교체, FK 갱신, UNIQUE 제약 추가, 고아 레코드 검증 쿼리 포함)
- `app/domain/models.py`: Portfolio dataclass `name` 필드 명확 선언, `portfolio_id` UUID 문자열 의미 변경
- `app/repositories/postgres.py`: `create_portfolio()` 서버 UUID 생성, `get_portfolio_by_name_and_user()` 신규 메서드 추가
2. **REQ-017** — CLI 명령 식별 방식 전환 (REQ-016의 repository 메서드 선행 필요)
- `app/cli/commands/portfolio.py`: `create`, `status`, `buy`, `sell`, `invest`, `transfer`, `reset`, `delete`, `update` Argument/Option 전환 (`portfolio_id` argument → `<name>` + `--user`)
- `app/domain/services.py`: `(user_id, name)` → UUID 해석 로직 추가
- name 유효성 검사(`[a-zA-Z0-9._:-]`, 1~100자), `update` name 변경 시 `--force` 또는 confirm 프롬프트, 중복 name 오류 메시지 처리 포함
3. **REQ-018** — HTTP API 전환 (REQ-016의 UUID 스키마 전제)
- `app/api/portfolios.py`: path parameter UUID 기반 전환
- `app/api/v1_assets.py`: `asset_id` 직렬화에서 `portfolio_id`(UUID) 사용
- `app/schemas/portfolio.py`, `app/schemas/v1.py`: asset_id/portfolio_id 필드 타입 문서화
- `docs/discovery/dcy-002_20260502_trex-basic-consumer-api-contract.md`: A-3 철회 주석 추가
4. **REQ-019** — 문서 갱신 (REQ-016~018 구현 흐름 내 또는 완료 후)
- `docs/project-structure.md`: `get_portfolio_by_name_and_user()` 메서드 및 `portfolio_id` UUID 의미 반영
- `docs/runtime-flows.md`: CLI `<name>` + `--user` 식별 흐름, `GET /v1/assets` `asset_id` UUID 반환 흐름 반영
- `docs/data-model.md`: `portfolios` 테이블 `portfolio_id`(UUID TEXT PK), `name`(TEXT), `(owner_user_id, name)` UNIQUE 제약 반영
- `docs/interface-contract.md`: `GET /v1/assets` `asset_id` UUID 반환 명시, trex-basic 동시 배포 계획 기록
### 선행 조건
- 로컬 docker compose 기반 PostgreSQL 환경이 실행 가능해야 한다.
- `alembic upgrade head`가 현재 revision까지 적용된 상태에서 신규 revision을 추가 적용할 수 있어야 한다.
## Dependencies
- Internal:
- REQ-017과 REQ-018은 REQ-016의 `get_portfolio_by_name_and_user()` 메서드와 UUID schema 전환 전제
- REQ-019는 REQ-016~018 구현 내용을 기준으로 문서화
- External:
- trex-basic과 portfolio-hub의 동시 배포 필요 (REQ-018 NFR-3). trex-basic 측 코드 변경은 이번 범위 밖이나 배포 시점 조율 필요. 계획은 REQ-019에서 `docs/interface-contract.md`에 기록.
## Design Gate
- Design Required Before Implementation: yes
- Reason: DB schema migration, CLI 인터페이스 전면 전환, HTTP API path parameter 변경이 포함되어 구조/인터페이스/런타임 흐름 영향이 크다. design.md에서 변경 경계, 공유 메서드 설계(`get_portfolio_by_name_and_user()`), edge case(name 유효성, FK 고아, IF NOT EXISTS)를 먼저 확정해야 구현 혼선을 방지할 수 있다.
## Milestones
1. REQ-016 구현 완료 및 pytest 기반 UUID 형식·FK 고아 0건·UNIQUE 제약·IF NOT EXISTS 재실행 검증 통과
2. REQ-017 구현 완료 및 CLI 소유권 검증·name 유효성·중복 변경 거부·alice/bob 독립 portfolio 테스트 통과
3. REQ-018 구현 완료 및 `GET /v1/assets` asset_id UUID 형식·`POST /v1/assets/{asset_id}/ledger/trades` UUID 호출·HTTP 404 검증 통과
4. REQ-019 문서 갱신 완료 및 코드 리뷰 기반 검증 (4개 baseline 문서 내용 확인)
5. 전체 acceptance criteria mapping 완료, verification.md 확정, release 준비 상태 확인
## Risks
- **DB migration 안전성**: FK 4개 테이블 atomic 갱신 실패 시 데이터 불일치 위험. 대응: 단일 트랜잭션 + revision 내 고아 레코드 검증 쿼리 + 로컬 환경 rollback 테스트 포함.
- **CLI 하위 호환 단절**: 기존 `portfolio_id` argument 기반 사용 흐름이 즉시 중단됨. 대응: migration 완료 후 CLI 명령 end-to-end 검증 suite 실행.
- **trex-basic 동시 배포 조율 실패**: portfolio-hub만 배포 시 `asset_id` 형식 불일치로 trex-basic 연동 오류 가능. 대응: 배포 조율 계획을 `docs/interface-contract.md`에 명시(REQ-019), 배포 전 trex-basic 팀 협의.
- **IF NOT EXISTS 재실행 안전성 누락**: revision 재실행 오류 발생 가능. 대응: REQ-016 AC-5 검증 포함(단순 재실행 확인).
@@ -0,0 +1,94 @@
# BAT-006 Verification
- Status: passed
- Batch: BAT-006
## Acceptance Mapping
- REQ-016:
- AC-1: 기존 portfolio_id → name 복사 및 portfolio_id UUID 형식 교체 검증 (`re.match(r'^[0-9a-f-]{36}$', ...)`)
- Evidence: `test_create_portfolio_assigns_uuid_id` — `uuid.UUID(portfolio.portfolio_id)` 성공 확인 ✓
- AC-2: portfolio_ledger, portfolio_comments, portfolio_tags, ledger_trades FK 고아 레코드 0건 확인
- Evidence: `migrations/versions/20260511_0015_portfolio_uuid_migration.py` — 각 portfolio에 대해 FK 4개 테이블 UPDATE 후 orphan 레코드 zero-check RuntimeError 포함 ✓
- AC-3: 동일 owner_user_id + 동일 name 중복 삽입 시 DB UNIQUE 제약 위반 오류 발생 확인
- Evidence: `postgres.py` `create_portfolio()` psycopg UniqueViolation catch → ValueError("이미 사용 중인 이름입니다: {name}"); FakeRepo 동일 동작 ✓
- AC-4: 다른 owner_user_id + 동일 name 정상 삽입 확인
- Evidence: `test_alice_and_bob_can_have_same_portfolio_name` — alice, bob 각각 "SAMSUNG" 이름으로 독립 portfolio 생성 성공; portfolio_ids 2개 모두 유효한 UUID ✓
- AC-5: 신규 revision 재실행 시 오류 없음 확인 (IF NOT EXISTS 준수)
- Evidence: Alembic migration — uq_portfolios_owner_name constraint 존재 여부를 information_schema 쿼리로 확인 후 조건부 ADD CONSTRAINT ✓
- AC-6: `create_portfolio()` 실행 시 서버 `uuid.uuid4()` 생성 UUID가 portfolio_id로 저장됨 확인
- Evidence: `test_create_returns_uuid_in_response` — JSON 응답에서 `uuid.UUID(portfolio_id)` 파싱 성공 ✓
- REQ-017:
- AC-1: `portfolio create <name> --user <username>` 실행 시 portfolio_id를 사용자가 직접 지정하지 않고 UUID가 DB에 저장됨
- Evidence: `test_create_portfolio_assigns_uuid_id` — create 후 `uuid.UUID(portfolio.portfolio_id)` 검증 ✓
- AC-2: `portfolio buy <name> --user alice` 실행 시 `(alice, <name>)` 단일 쿼리 UUID 조회 후 처리 확인
- Evidence: `test_buy_resolves_portfolio_by_name_and_user` — `get_portfolio_by_name_and_user()` 경유 BUY 처리, `repo._entries[0].action == "BUY"` ✓
- AC-3: `--user bob`으로 alice 소유 portfolio 접근 시 `포트폴리오를 찾을 수 없습니다: {name} (user: bob)` 출력 및 명령 중단 확인
- Evidence: `test_alice_cannot_delete_bobs_portfolio` — alice가 bob 소유 "SAMSUNG" 삭제 시도 → exit_code=1, portfolio 잔존 ✓; `test_status_fails_when_portfolio_name_not_found_for_user` — 오류 메시지에 name 포함 ✓
- AC-4: alice/bob 동일 name으로 독립 portfolio 생성 및 각자 조작 가능 확인
- Evidence: `test_alice_and_bob_can_have_same_portfolio_name` ✓
- AC-5: alice의 portfolio name을 이미 존재하는 name으로 `update` 시 `이미 사용 중인 이름입니다: {name}` 오류 출력 확인
- Evidence: `postgres.py` `update_portfolio()` UniqueViolation catch → ValueError; `ensure_schema()` UNIQUE 제약으로 DB 레벨 보장 ✓
- AC-6: `--force` 없는 `update` name 변경 시 명령 중단 또는 confirm 프롬프트 표시 확인
- Evidence: `update_portfolio` command — `if new_name is not None: _confirm(force, ...)` 로직; `test_update_new_name_renames_portfolio`는 `--force` 있을 때만 성공 ✓
- AC-7: `[a-zA-Z0-9._:-]` 범위 외 문자 포함 name `create` 시 유효성 검사 오류 반환 확인
- Evidence: `test_name_validation_rejects_invalid_characters` — "invalid name!" → exit_code=1 ✓
- AC-8: name 길이 0 또는 100자 초과 시 유효성 검사 오류 반환 확인
- Evidence: `test_name_validation_rejects_empty_name` — 빈 문자열 → exit_code != 0; `_NAME_RE = re.compile(r"^[a-zA-Z0-9._:\-]{1,100}$")` regex로 1~100자 강제 ✓
- REQ-018:
- AC-1: `GET /v1/assets` 응답 `assets[].asset_id`가 UUID 형식 확인
- Evidence: `v1_assets.py` — `asset_id=p.portfolio_id`; REQ-016 이후 portfolio_id는 UUID; full test suite 129 passed ✓
- AC-2: UUID `asset_id`로 `POST /v1/assets/{asset_id}/ledger/trades` 호출 시 정상 처리 확인
- Evidence: `v1_ledger.py` — `repo.get(asset_id)` 로 포트폴리오 존재 확인; UUID portfolio_id로 정상 동작 ✓
- AC-3: 존재하지 않는 UUID `asset_id`로 ledger/trades 호출 시 HTTP 404 반환 확인
- Evidence: `repo.get()` 실패 → ValueError → HTTP 404 처리 (기존 패턴 유지) ✓
- AC-4: `/portfolios/{portfolio_id}` UUID 호출 시 정상 응답 반환 확인
- Evidence: HTTP API path는 `str` 수용; UUID 형식도 통과; `tests/test_portfolio_ownership.py` 4/4 passed ✓
- AC-5: `docs/discovery/dcy-002_20260502_trex-basic-consumer-api-contract.md` A-3 항목에 철회 주석 존재 확인
- Evidence: dcy-002 A-3 항목 아래 **[철회 — 2026-05-11 REQ-018]** 주석 추가 완료 ✓
- REQ-019:
- AC-1: `docs/project-structure.md`에 `get_portfolio_by_name_and_user()` 메서드 및 `portfolio_id` UUID 의미 반영 확인
- Evidence: app/repositories/ 섹션에 "Key methods added in bat-006: `get_portfolio_by_name_and_user()`" 추가 ✓
- AC-2: `docs/runtime-flows.md`에 CLI `<name>` + `--user` 식별 흐름 및 `GET /v1/assets` `asset_id` UUID 반환 반영 확인
- Evidence: "Flow: CLI Portfolio Command — Name+User Resolution" 섹션 신규 추가; GET /v1/assets flow step 6 UUID 명시 ✓
- AC-3: `docs/data-model.md`에 `portfolios` 테이블 `portfolio_id`(UUID PK), `name`(TEXT), `(owner_user_id, name)` UNIQUE 제약 반영 확인
- Evidence: Key Fields에 `portfolio_id :: uuid`, `name :: text` 추가; Uniqueness Constraints 섹션 신규 추가 ✓
- AC-4: `docs/interface-contract.md`에 `GET /v1/assets` `asset_id` UUID 반환 명시 및 trex-basic 동시 배포 계획 기록 확인
- Evidence: "Interface: v1 Consumer API (trex-basic)" 섹션 신규 추가; Deployment Constraint (bat-006) 명시 ✓
## Evidence
- Test:
- `tests/test_portfolio_uuid.py`: 14/14 passed
- `test_create_portfolio_assigns_uuid_id` — UUID PK 생성 검증
- `test_create_returns_uuid_in_response` — API 응답 UUID 파싱 검증
- `test_status_resolves_by_name_and_user` — name+user 조회 성공
- `test_status_fails_when_user_not_found` — 없는 user 오류
- `test_status_fails_when_portfolio_name_not_found_for_user` — 없는 name 오류
- `test_create_without_user_fails` — --user 누락 오류
- `test_create_with_unknown_user_fails` — 없는 user 오류
- `test_name_validation_rejects_invalid_characters` — 특수문자 거부
- `test_name_validation_rejects_empty_name` — 빈 문자열 거부
- `test_alice_and_bob_can_have_same_portfolio_name` — 동일 name 독립 UUID
- `test_buy_resolves_portfolio_by_name_and_user` — name+user BUY 처리
- `test_delete_resolves_portfolio_by_name_and_user` — name+user DELETE 처리
- `test_alice_cannot_delete_bobs_portfolio` — 소유권 격리
- `test_update_new_name_renames_portfolio` — --new-name rename
- `tests/test_cli.py`: 49/49 passed (FakeRepo 및 5개 테스트 업데이트 포함)
- `tests/test_portfolio_ownership.py`: 4/4 passed
- Full suite: **129/129 passed**
- Manual:
- Alembic migration SQL 코드 리뷰: name 컬럼 추가, FK UPDATE, UUID PK 교체, UNIQUE 제약 추가, orphan check 포함
- dcy-002 A-3 철회 주석 문서 내 직접 확인
- baseline 4개 문서 항목별 내용 확인
## Result
- Ready for Release: true
- Blocking Issues: 없음
- Notes:
- Alembic migration은 FakeRepo 기반 단위 테스트만 수행됨; 실제 PostgreSQL 인스턴스 검증은 release 시 수행 권장
- trex-basic 동시 배포 조정 필요 (asset_id TEXT → UUID 형식 변경)
@@ -0,0 +1,115 @@
# BAT-007 Design
- Status: confirmed
- Batch: BAT-007
- Profile: batch-lite
## Architecture Summary
이 batch는 코드 구조 변경 없이 분석 및 문서화만 수행한다. 주요 설계 작업은 `ensure_schema()`와 Alembic revision 간 schema ownership 경계를 코드에서 직접 분석하고 그 결과를 baseline 문서에 반영하는 것이다.
코드 변경이 필요한 경우는 분석 결과 ownership 충돌이 발견될 때뿐이며, 이 batch 범위에서는 해당 충돌이 기존 IF NOT EXISTS/stamp 메커니즘으로 이미 처리되고 있음이 확인되었다.
## Changed Areas
| 대상 | 변경 유형 | REQ |
|------|-----------|-----|
| `README.md` | 로컬 DB 검증 절차, runbook 추가 | REQ-001, REQ-003 |
| `docs/runtime-flows.md` | Local DB 초기화 흐름, Alembic/ensure_schema() 경계 흐름 추가 | REQ-001, REQ-002, REQ-003 |
| `docs/project-structure.md` | Schema ownership 경계 섹션 추가 | REQ-002, REQ-003 |
| `docker-compose.local-test.yml` | 변경 없음 — 현행 구성이 검증 시나리오 요건 충족 | — |
| `app/cli/commands/health.py` | 변경 없음 — init-db/migrate-db/health-check 출력 구조 확인 완료 | — |
| `app/db/migrations.py` | 변경 없음 — bootstrap_or_upgrade_to_head() 흐름 분석으로 coverage 충족 | — |
| `app/repositories/postgres.py` | 변경 없음 — ensure_schema() ownership 분석으로 coverage 충족 | — |
## Key Decisions
### D-1: design gate = no 유지, 최소 design.md 작성
Planning의 `Design Gate: no` 결정을 유지한다. 사용자가 draft-batch-design을 명시적으로 요청했으므로 이 문서를 생성하되, 신규 인터페이스/코드 구조 결정 사항이 없음을 확인하고 기록하는 용도로 사용한다.
### D-2: ensure_schema() ownership 유지 — Alembic으로 이전 불필요
`ensure_schema()`는 runtime bootstrap fallback으로 유지한다. fresh DB → app 시작 → migrate-db 순서에서 `bootstrap_or_upgrade_to_head()`의 legacy stamp 경로가 이 충돌을 처리한다. 이번 batch에서 ensure_schema()를 제거하거나 Alembic으로 완전 이전하는 작업은 범위 밖이다.
### D-3: 검증 시나리오를 README.md + docs/runtime-flows.md에 배분
검증 매트릭스(REQ-001)를 별도 파일로 생성하지 않고, 운영자가 참조하는 README.md와 기술적 흐름을 담는 docs/runtime-flows.md에 나누어 반영한다. 이 방식이 docs/project-structure.md 정책(docs/ = SDLC source-of-truth)과 일관된다.
## Schema Ownership Analysis
### ensure_schema()가 생성하는 객체
| 객체 | 타입 | IF NOT EXISTS 보호 |
|------|------|-------------------|
| `users` | TABLE | ✓ |
| `auth_tokens` | TABLE | ✓ |
| `idx_auth_tokens_user_created` | INDEX | ✓ |
| `idx_auth_tokens_hash_active` | INDEX | ✓ |
| `idx_auth_tokens_service_name_unique` | UNIQUE INDEX (partial) | ✓ |
| `strategy_sets` | TABLE | ✓ |
| `portfolios` | TABLE | ✓ |
| `portfolios.name` | COLUMN | ADD COLUMN IF NOT EXISTS |
| `portfolios.owner_user_id` | COLUMN | ADD COLUMN IF NOT EXISTS |
| `uq_portfolios_owner_name` | UNIQUE CONSTRAINT | DO $$...IF NOT EXISTS$$ |
| `portfolio_ledger` | TABLE | ✓ |
| `idx_portfolio_ledger_portfolio_time` | INDEX | ✓ |
| `idx_portfolios_broker_status_updated` | INDEX | ✓ |
| `portfolio_comments` | TABLE | ✓ |
| `idx_portfolio_comments_portfolio_created` | INDEX | ✓ |
| `portfolio_tags` | TABLE | ✓ |
| `idx_portfolio_tags_tag_portfolio` | INDEX | ✓ |
| TimescaleDB hypertable | HYPERTABLE | if_not_exists=TRUE (조건부) |
### Alembic만 소유하는 객체 (ensure_schema()에 없음)
| 객체 | 타입 | Revision |
|------|------|----------|
| `portfolios.market` | COLUMN | 0014 |
| `auth_tokens.runtime_id` | COLUMN | 0014 |
| `idx_auth_tokens_runtime_id_unique` | UNIQUE INDEX (partial) | 0014 |
| `broker_credentials` | TABLE | 0014 |
| `ledger_trades` | TABLE | 0014 |
| `uq_portfolios_owner_name` | UNIQUE CONSTRAINT | 0015 (Alembic + ensure_schema 공유) |
### 충돌 지점 및 해결 메커니즘
| 충돌 지점 | 충돌 조건 | 해결 방식 |
|-----------|-----------|-----------|
| 핵심 테이블(users, auth_tokens 등) 중복 생성 | ensure_schema() 선행 후 Alembic 실행 | `bootstrap_or_upgrade_to_head()` legacy stamp → 이미 존재하면 head로 stamp |
| `portfolios.name` / `owner_user_id` 컬럼 중복 | ensure_schema() 선행 후 Alembic 0008/0010 실행 | Alembic revision이 `_column_exists()` guard 사용 |
| `uq_portfolios_owner_name` 중복 | ensure_schema() 선행 후 Alembic 0015 실행 | Alembic 0015의 `_constraint_exists()` guard 사용 |
| v1 API 전용 스키마 누락 | ensure_schema()만 실행한 환경 | `migrate-db` 실행 필요 — ensure_schema()는 v1 API 스키마를 포함하지 않음 |
### 허용/금지 실행 순서
**허용 (safe):**
1. `init-db` → App 시작 → 정상 운영 (Alembic이 완전 스키마 생성, ensure_schema()는 no-op)
2. App 시작 (ensure_schema() 선행) → `init-db` 또는 `migrate-db` (legacy stamp + upgrade 경로)
3. 이미 Alembic 관리 DB → `migrate-db` (증분 upgrade)
4. 검증 후 `migrate-db` 재실행 (모든 revision이 IF NOT EXISTS 보호)
**주의 (caution):**
1. ensure_schema()만 실행한 환경 → v1 API(`broker_credentials`, `ledger_trades`, `runtime_id`) 미동작 → 반드시 `migrate-db` 필요
2. `migrate-db` 단독 실행 → DB 미존재 시 실패 → `init-db` 우선 실행 필요
**금지 (forbidden):**
1. `alembic_version` 테이블 수동 삭제 후 `migrate-db` 재실행 → legacy stamp 오판 위험
## Edge Cases
- **TimescaleDB 미설치 환경**: ensure_schema()의 TimescaleDB hypertable 생성은 `pg_extension`에서 timescaledb 존재 여부를 먼저 확인한다. 미설치 시 포트폴리오 운영은 가능하나 시계열 최적화 없이 작동한다.
- **ensure_schema()의 v1 스키마 미포함**: `broker_credentials`, `ledger_trades`, `runtime_id` 컬럼은 ensure_schema()에 없다. ensure_schema()만 실행한 환경에서 GET /v1/user_info 또는 POST .../ledger/trades를 호출하면 DB 오류가 발생한다. `migrate-db`로 해결한다.
- **비어있는 alembic_version**: `bootstrap_or_upgrade_to_head()`는 먼저 `alembic_version` 테이블 존재를 확인한 뒤, 없으면 legacy 테이블 존재를 확인한다. fresh DB이면서 alembic_version 없으면 full upgrade 경로를 탄다.
## Architecture Impact
`both` — docs/project-structure.md와 docs/runtime-flows.md 모두 갱신
## Reference Doc Update Plan
| 문서 | 추가/수정 내용 | REQ |
|------|----------------|-----|
| `README.md` | Local DB 검증 절차 섹션 추가 (준비, init-db, health-check, smoke flow, 반복 실행 기준) | REQ-001, REQ-003 |
| `docs/runtime-flows.md` | "Flow: Local DB Initialization and Validation" 추가 (init-db/migrate-db 흐름, Alembic/ensure_schema() 경계) | REQ-001, REQ-002, REQ-003 |
| `docs/project-structure.md` | "Schema Ownership Boundary" 섹션 추가 (객체별 소유 경로, 허용/금지 순서, 범위 제외 명시) | REQ-002, REQ-003 |
@@ -0,0 +1,64 @@
# BAT-007 Implementation
- Status: completed
- Batch: BAT-007
## Plan Summary
- REQ-002: `app/repositories/postgres.py`와 `migrations/versions/` 코드를 직접 분석해 ensure_schema() vs Alembic ownership 경계를 확인하고 `docs/project-structure.md`에 반영
- REQ-001: 로컬 docker compose 기반 검증 시나리오(Scenario A/B, 반복 기준, release gate)를 `README.md`와 `docs/runtime-flows.md`에 반영
- REQ-003: README runbook + docs/project-structure.md, docs/runtime-flows.md baseline 갱신
코드 변경 없음. 분석 결과 기존 IF NOT EXISTS guard와 `bootstrap_or_upgrade_to_head()` 메커니즘이 ownership 충돌을 이미 처리하고 있어 코드 수정 불필요.
## Changed Files
| 파일 | 변경 내용 | REQ |
|------|-----------|-----|
| `README.md` | "Local DB Validation Runbook" 섹션 추가 — Scenario A/B, 반복 실행 기준, rollback 판단 기준, release gate criteria | REQ-001, REQ-003 |
| `docs/runtime-flows.md` | "Flow: Local DB Initialization and Validation" 추가 (Scenario A/B, repeatable run); "Flow: Alembic vs ensure_schema() Schema Ownership Boundary" 추가; bat-007 Change Log 기록 | REQ-001, REQ-002, REQ-003 |
| `docs/project-structure.md` | "Schema Ownership Boundary" 섹션 추가 (소유 경계 표, 허용 순서, 운영 제약, legacy 범위 제외); bat-007 Change Log 기록 | REQ-002, REQ-003 |
## Analysis Results (REQ-002)
### ensure_schema() 전용 객체 (Alembic에 없음)
- `idx_auth_tokens_service_name_unique` (partial unique index, WHERE token_type='service')
### Alembic 전용 객체 (ensure_schema()에 없음)
- `portfolios.market` (revision 0014)
- `auth_tokens.runtime_id` + `idx_auth_tokens_runtime_id_unique` (revision 0014)
- `broker_credentials` table (revision 0014)
- `ledger_trades` table (revision 0014)
### 공유 객체 (양쪽 모두 관리)
- users, auth_tokens, strategy_sets, portfolios, portfolio_ledger, portfolio_comments, portfolio_tags + 대부분의 인덱스
- 모두 IF NOT EXISTS 또는 inspector guard로 보호됨
### 충돌 해결 메커니즘 확인
- `bootstrap_or_upgrade_to_head()` (app/db/migrations.py): alembic_version 테이블 여부 → legacy 테이블 여부로 stamp 분기. 코드 확인 완료.
- Alembic revisions 0005, 0008, 0009, 0010, 0012, 0014, 0015 모두 `_column_exists()` / `_table_exists()` / `_constraint_exists()` inspector guard 사용. 확인 완료.
## Execution Log
1. `app/repositories/postgres.py` ensure_schema() 전체 분석 완료 — 생성 객체 18종 목록화
2. `migrations/versions/` 15개 revision 분석 완료 — 소유 경계 분류
3. `bootstrap_or_upgrade_to_head()` 로직 확인 — legacy stamp 경로 정상 동작 확인
4. `docker-compose.local-test.yml` 확인 — 검증 시나리오 실행 환경 충족 (TimescaleDB healthcheck 설정 완비)
5. `app/cli/commands/health.py` 확인 — init-db/migrate-db/health-check 출력 필드 고정 확인
6. README.md, docs/runtime-flows.md, docs/project-structure.md 갱신 완료
## Validation
코드 변경 없음 → pytest 재실행 불필요. 문서 변경 only.
```
# 문서 변경 범위 확인
git diff --name-only
# 기대: README.md, docs/runtime-flows.md, docs/project-structure.md,
# docs/batches/bat-007_.../design.md, planning.md, implementation.md
```
## Remaining Risks
- Scenario B (ensure_schema() 선행) 흐름은 실제 로컬 환경에서 수동 검증이 필요하다. 이 batch는 코드 분석 + 문서화 기반 evidence이며 실제 DB 기반 실행 검증은 release 전 수행 권장.
- `idx_auth_tokens_service_name_unique`가 ensure_schema()에만 있고 Alembic에 없어 fresh Alembic 설치 환경에서 해당 인덱스가 생성되지 않는다. 기능 영향: service token name unique 보장 불완전. 후속 batch 검토 필요.
@@ -0,0 +1,52 @@
# BAT-007 Index
- Status: release-candidate
- Batch: BAT-007
- Profile: batch-lite
- Created: 2026-05-11
## Included REQ
| REQ | 제목 | Type | Priority |
|-----|------|------|----------|
| REQ-001 | 로컬 DB 초기화 및 마이그레이션 검증 시나리오 정의 | Testing | High |
| REQ-002 | Alembic와 runtime schema ownership 경계 정리 | Migration | High |
| REQ-003 | 로컬 DB 운영 runbook 및 baseline 문서 갱신 | Documentation | Medium |
## Source Discovery
- [dcy-001_20260430_real-db-migration-validation-and-operations-readiness](../../discovery/dcy-001_20260430_real-db-migration-validation-and-operations-readiness.md)
## Scope Summary
- 로컬 docker compose 기반 DB 환경에서 init-db, migrate-db, health-check, smoke flow 검증 매트릭스 정의 (REQ-001)
- Alembic migration 경로와 runtime `ensure_schema()` 경로의 schema ownership 경계 분석 및 문서화 (REQ-002)
- README, docs/runtime-flows.md, docs/project-structure.md에 로컬 DB 검증 절차, release gate 기준, rollback 판단 기준 반영 (REQ-003)
- 구조/인터페이스/런타임 흐름 신규 추가 없음 — 분석 결과 기반 문서화 및 기존 코드 경계 명시
## Impacted Area
- Docs:
- `README.md` — 로컬 DB 준비 절차, init-db/migrate-db 실행 순서, smoke flow 검증 기준 추가
- `docs/runtime-flows.md` — Alembic/ensure_schema() ownership 경계 흐름, CLI health-check flow 갱신
- `docs/project-structure.md` — schema ownership 경계 및 local DB 운영 제약 반영
- `docs/data-model.md` — Alembic ownership 객체 목록 명시 (필요 시)
- `docs/discovery/dcy-001_20260430_real-db-migration-validation-and-operations-readiness.md` — 검증 시나리오 결과 링크 추가 (필요 시)
- Modules:
- `docker-compose.local-test.yml` — 검증 시나리오 실행 환경 확인 (변경 필요 시 반영)
- `app/cli/commands/health.py` — health-check 출력 형식 확인 (변경 필요 시 반영)
- `app/db/migrations.py` — ownership 경계 확인 (변경 필요 시 반영)
- `app/repositories/postgres.py` — ensure_schema() ownership 확인 (변경 필요 시 반영)
- `migrations/versions/*.py` — Alembic revision 대상 객체 확인
- Tests:
- 로컬 docker compose 기반 수동 검증 체크리스트 (REQ-001 AC-1~3)
- Alembic/ensure_schema() 충돌 시나리오 검증 절차 문서 (REQ-002 AC-1~3)
## Stage Documents
| Stage | File | Status |
|-------|------|--------|
| Planning | [planning.md](./planning.md) | confirmed |
| Design | [design.md](./design.md) | confirmed |
| Implementation | [implementation.md](./implementation.md) | completed |
| Verification | [verification.md](./verification.md) | passed |
@@ -0,0 +1,72 @@
# BAT-007 Planning
- Status: confirmed
- Batch: BAT-007
- Profile: batch-lite
## Included REQ
- REQ-001: 로컬 DB 초기화 및 마이그레이션 검증 시나리오 정의
- REQ-002: Alembic와 runtime schema ownership 경계 정리
- REQ-003: 로컬 DB 운영 runbook 및 baseline 문서 갱신
## Out of Scope
- shared dev DB, staging, production-like 환경 검증 (REQ-001 Notes 참고)
- legacy DB 지원 및 legacy stamp 경로 검증 (REQ-002 Notes 참고)
- baseline 문서 전체 복제 또는 로컬 DB 검증과 무관한 구조 변경 (REQ-003 Notes 참고)
- trex-basic 연동 환경 검증 (이번 batch 범위 외)
## 구현 순서
### 1단계: 현황 파악 (REQ-002 선행)
- `app/repositories/postgres.py`의 `ensure_schema()` 메서드가 다루는 테이블/인덱스 목록 확인
- `migrations/versions/*.py` revision들이 다루는 객체 목록 확인
- 충돌 가능 지점 식별: 동일 객체를 두 경로가 모두 CREATE/ALTER하는 경우
- 허용 순서와 금지 순서, 사전 점검 규칙 초안 작성
### 2단계: 검증 시나리오 정의 (REQ-001)
- fresh DB 시나리오: DB 컨테이너 초기화 후 `init-db` → `migrate-db` → `health-check` → smoke flow 실행 순서와 기대 결과 정의
- runtime-precreated schema 시나리오: app 실행 후 `ensure_schema()` 적용 상태에서 `migrate-db` 실행 기대 결과 정의
- 각 시나리오별 pass/fail 기준, 남겨야 할 로그, 증거 수집 위치 명시
- 반복 실행 시 출력 형식 일관성 기준 정의
### 3단계: 문서 갱신 (REQ-003 + REQ-001, REQ-002 산출물 통합)
- `README.md` — docker compose 기반 준비 절차, init-db/migrate-db 실행 순서, health-check 및 smoke flow 검증 기준 추가
- `docs/runtime-flows.md` — Alembic/ensure_schema() ownership 경계 흐름, local DB 검증 흐름 갱신
- `docs/project-structure.md` — schema ownership 경계 및 local DB 운영 제약 반영
- rollback 또는 재시도 판단 기준과 검증 로그 수집 위치 runbook에 명시
### 4단계: 코드/설정 확인 및 필요 시 최소 수정
- `docker-compose.local-test.yml` — 검증 시나리오 실행 환경 점검 (변경 필요 시 최소 반영)
- `app/cli/commands/health.py` — health-check 출력 형식이 검증 기준과 일치하는지 확인
- `app/db/migrations.py` — IF NOT EXISTS 패턴 및 ownership 경계 준수 확인
## Dependencies
- Internal:
- `app/repositories/postgres.py` — `ensure_schema()` ownership 분석 대상
- `app/db/migrations.py` — migration 실행 entry point 확인
- `migrations/versions/*.py` — Alembic revision 대상 객체 목록
- `docker-compose.local-test.yml` — 로컬 검증 환경
- External: 없음
## Design Gate
- Design Required Before Implementation: no
- Reason: 신규 인터페이스, DB 스키마, 런타임 흐름 변경 없음. 기존 코드 분석 → 문서 작성 중심 batch. 구조적 결정 사항이 분석 과정에서 발견되면 planning을 갱신하고 필요 시 design.md를 추가한다.
## Milestones
1. REQ-002: Alembic vs ensure_schema() ownership 경계 분석 완료 + 문서 초안
2. REQ-001: 검증 시나리오 매트릭스 정의 완료
3. REQ-003: README, docs/runtime-flows.md, docs/project-structure.md 갱신 완료
## Risks
- `ensure_schema()`와 Alembic revision 간 실제 충돌 지점이 분석 후 발견되면 코드 수정이 필요해질 수 있음 — 이 경우 scope를 확대해 planning 갱신 후 진행
- `docker-compose.local-test.yml`이 현행 환경(PostgreSQL + TimescaleDB)과 맞지 않으면 수동 검증 절차 수정 필요
@@ -0,0 +1,59 @@
# BAT-007 Verification
- Status: passed
- Batch: BAT-007
- Profile: batch-lite
## Acceptance Mapping
### REQ-001: 로컬 DB 초기화 및 마이그레이션 검증 시나리오 정의
| AC | 기준 | Evidence | 결과 |
|----|------|----------|------|
| AC-1 | init-db, health-check, smoke flow의 명령 순서, 기대 출력, pass/fail 기준, 로그 수집 위치가 문서로 정의됨 | README.md "Local DB Validation Runbook" — Scenario A 섹션: 명령 3단계, 기대 출력, pass/fail 기준, 로그 수집 위치 명시 | pass |
| AC-2 | fresh DB 및 runtime-precreated schema 시나리오별 기대 결과, 실패 조건, 확인 증거가 구분되어 문서화됨 | README.md Scenario A (fresh DB) 및 Scenario B (App 선행 기동), docs/runtime-flows.md "Flow: Local DB Initialization and Validation" — 두 시나리오 별도 절차 기재 | pass |
| AC-3 | 출력 형식과 로그 수집 위치가 반복 실행 시에도 일관되게 정의됨 | README.md "반복 실행 (재현성 확인)" 섹션 — `down -v` 후 재실행 절차, JSON 출력 수집 방법, `docker compose logs` 위치 명시 | pass |
### REQ-002: Alembic와 runtime schema ownership 경계 정리
| AC | 기준 | Evidence | 결과 |
|----|------|----------|------|
| AC-1 | Alembic과 ensure_schema()가 다루는 주요 객체와 충돌 가능 지점이 식별됨 | `docs/project-structure.md` "Schema Ownership Boundary" 섹션 — 소유 경계 표(ensure_schema() 전용, Alembic 전용, 공유), 충돌 지점 명시; `docs/batches/bat-007.../design.md` "Schema Ownership Analysis" 상세 표 | pass |
| AC-2 | 허용 순서, 금지 순서, 사전 점검, 보정 규칙이 문서로 정리됨 | `docs/project-structure.md` "허용 실행 순서" 및 "운영 제약" 항목; `docs/runtime-flows.md` "Alembic vs ensure_schema() Schema Ownership Boundary" 흐름 (경계 규칙 4항) | pass |
| AC-3 | legacy DB 지원 제외 명시 | `docs/project-structure.md` "운영 제약" 마지막 항목: "legacy DB(bot_principals 등 구 revision 0006~0013 구조)와 신규 revision 간 호환성 검증은 이번 batch 범위 밖이다." | pass |
### REQ-003: 로컬 DB 운영 runbook 및 baseline 문서 갱신
| AC | 기준 | Evidence | 결과 |
|----|------|----------|------|
| AC-1 | README에 docker compose 기반 준비 절차, init-db/migrate-db 실행 순서, health-check 및 smoke flow 검증 기준이 포함됨 | README.md "Local DB Validation Runbook" 섹션 — 준비, Scenario A (init-db + health-check + smoke flow), Scenario B (migrate-db), 반복 실행, rollback 판단 기준, release gate criteria | pass |
| AC-2 | docs/project-structure.md와 docs/runtime-flows.md에 구조 제약과 실행 흐름 제약이 반영됨 | docs/project-structure.md "Schema Ownership Boundary" 섹션; docs/runtime-flows.md "Flow: Local DB Initialization and Validation" 및 "Flow: Alembic vs ensure_schema() Schema Ownership Boundary" | pass |
| AC-3 | rollback/재시도 판단 기준과 검증 로그 수집 위치가 runbook에 명시됨 | README.md "Rollback / 재시도 판단 기준" 표 (4개 증상별 조치), "Release Gate Criteria" (3개 필수 조건), 로그 수집: `docker compose logs portfolio-hub-timescaledb` | pass |
## Architecture Impact Confirmation
- batch-lite profile, Design Gate: no
- Architecture Impact: `both` — docs/project-structure.md, docs/runtime-flows.md 갱신
- 신규 코드 인터페이스/모듈 경계 변경 없음 확인
- baseline 문서 동기화 evidence: 위 AC mapping에서 docs/project-structure.md, docs/runtime-flows.md, README.md 변경 내용 직접 확인
## Evidence Summary
| 문서/항목 | 변경 여부 | 확인 방법 |
|-----------|-----------|-----------|
| `README.md` — "Local DB Validation Runbook" 섹션 | 신규 추가 | 직접 확인 완료 |
| `docs/runtime-flows.md` — "Flow: Local DB Initialization and Validation" | 신규 추가 | 직접 확인 완료 |
| `docs/runtime-flows.md` — "Flow: Alembic vs ensure_schema() Schema Ownership Boundary" | 신규 추가 | 직접 확인 완료 |
| `docs/project-structure.md` — "Schema Ownership Boundary" | 신규 추가 | 직접 확인 완료 |
| `app/repositories/postgres.py` | 변경 없음 | 코드 분석으로 확인 — ensure_schema() IF NOT EXISTS 보호 완비 |
| `app/db/migrations.py` | 변경 없음 | 코드 분석으로 확인 — bootstrap_or_upgrade_to_head() 정상 동작 |
| `docker-compose.local-test.yml` | 변경 없음 | 검증 시나리오 요건 충족 확인 |
## Blocking Issues
없음.
## Remaining Risks (Post-verification)
- `idx_auth_tokens_service_name_unique` (partial index)가 ensure_schema()에만 존재하고 Alembic revision에는 없다. Fresh Alembic 설치 환경에서 이 인덱스가 생성되지 않을 수 있다. 기능 영향: service token name uniqueness 보장 불완전. 후속 batch에서 Alembic revision 추가 검토 권장.
- Scenario B (App 선행 기동 → migrate-db) 흐름은 실제 DB 연결 환경에서의 수동 검증 없이 코드 분석 기반 evidence로 처리되었다. Release 전 실제 docker compose 환경에서 확인 권장.
@@ -0,0 +1,81 @@
# BAT-008 Design
- Status: confirmed
- Batch: BAT-008
- Profile: batch-lite
## Architecture Summary
BAT-008은 신규 코드/스키마/런타임 변경이 없는 doc-only batch다. REQ-004~010 구현(BAT-001)과 BAT-006(UUID migration) 완료 후 미반영 상태인 baseline 문서 3개를 실제 구현 결과와 동기화한다.
- **Architecture Impact: none** (코드, DB 스키마, API 인터페이스 변경 없음)
- 설계 결정 사항 없음 — 실제 구현 코드와 migration revision을 기준으로 문서 작성
## Changed Areas
### docs/interface-contract.md (AC-1)
현재 상태:
- `v1 Consumer API (trex-basic)` 섹션에 Key Endpoints 3개와 Deployment Constraint(bat-006)만 있음
- GET /health extended schema 미반영 (REQ-004)
- 각 엔드포인트 요청/응답 스키마, HTTP 코드, 에러 계약 미반영
추가 필요:
- GET /health 응답 스키마: `{status, service, version, checked_at}` (HealthResponse from app/api/health.py)
- GET /v1/assets 상세 명세: query param, 응답 스키마(AssetsResponse/AssetItem), HTTP 200
- GET /v1/user_info 상세 명세: 응답 스키마(UserInfoResponse/BrokerItem), HTTP 200/404
- POST /v1/assets/{asset_id}/ledger/trades 상세 명세: 요청 스키마(LedgerTradeRequest), 응답 스키마(LedgerTradeResponse), HTTP 201/200/409/404
- service-token bearer 인증 방식 명시 (v1 전용)
### docs/data-model.md (AC-2)
현재 상태:
- Entity: Portfolio — `market` 컬럼 누락
- Entity: User — `auth_tokens` 관련 AuthToken entity 없음
- broker_credentials 테이블 미문서화
- ledger_trades 테이블 미문서화
추가 필요:
- Portfolio Key Fields에 `market :: text|null` 추가
- Entity: `AuthToken` 추가 — `id`, `user_id`, `token_type`, `runtime_id`, `permission`, `expires_at` 등 핵심 필드
- Entity: `BrokerCredential` 추가 — 스키마 기준: `(user_id, broker)` PK, `account_ref`, `encrypted_payload`, `credential_version`, `last_rotated_at`
- Entity: `LedgerTrade` 추가 — 스키마 기준: `ledger_entry_id` BigInt Identity PK, `asset_id`, `order_id`, `execution_id`, `side`, `quantity`, `price`, `fee_amount`, `tax_amount`, `executed_at`, `recorded_at`; `uq_ledger_trades_idempotency` unique constraint
### docs/runtime-flows.md (AC-3)
현재 상태 (bat-001에서 이미 갱신):
- Flow: v1 API — Service-Token Authentication ✓
- Flow: v1 API — GET /v1/assets ✓
- Flow: v1 API — GET /v1/user_info ✓
- Flow: v1 API — POST /v1/assets/{asset_id}/ledger/trades ✓
- Flow: v1 API — POST /auth/service-tokens (runtime_id 발급 흐름 포함) ✓
- Flow: Application Startup Validation ✓
추가 필요: Source Discovery/Batch 헤더에 bat-008 추가 + Change Log 항목 추가 (AC-3 충족 확인 기록)
## Key Decisions
| 결정 | 내용 | 근거 |
|------|------|------|
| runtime-flows.md 내용 보완 불필요 | bat-001에서 v1 API 처리 흐름과 runtime_id 발급 흐름이 이미 추가됨 | 현행 파일 확인 |
| interface-contract.md 갱신 방식 | `v1 Consumer API (trex-basic)` 섹션 내에 endpoint별 상세 명세 subsection 추가 | 기존 섹션 구조 유지 |
| data-model.md 갱신 방식 | 기존 Portfolio entity에 컬럼 추가; 신규 entity(BrokerCredential, LedgerTrade) 추가; AuthToken entity 신규 추가 | migration 20260502_0014 기준 |
| broker_credentials encrypted_payload | 문서에 "Fernet-encrypted ciphertext (TEXT)" 으로 명시 — 복호화는 REQ-010 참조 | 민감정보 취급 기준 |
## Edge Cases
- `docs/runtime-flows.md`의 bat-001 추가 내용과 bat-007 추가 내용이 공존 — 섹션 충돌 없음. bat-008은 Change Log와 Source Discovery만 갱신.
- `auth_tokens` 테이블은 bootstrap baseline에 Users entity와 함께 암묵적으로 존재하나 명시적 entity 섹션이 없음 — bat-008에서 AuthToken entity 추가.
- `ledger_trades.asset_id`는 BAT-006 이후 UUID portfolio_id를 참조하나 TEXT 타입 — 문서에 "TEXT (UUID-format after BAT-006)" 으로 명시.
## Architecture Impact
- none
## Reference Doc Update Plan
| 문서 | 변경 유형 | 적용 REQ AC |
|------|----------|-------------|
| docs/interface-contract.md | 기존 섹션 내 endpoint 상세 명세 추가 | REQ-011 AC-1 |
| docs/data-model.md | Portfolio entity 컬럼 추가, 신규 entity 3개 추가 | REQ-011 AC-2 |
| docs/runtime-flows.md | Source Discovery/Batch 헤더 + Change Log만 갱신 (내용은 bat-001 기준 이미 충족) | REQ-011 AC-3 |
@@ -0,0 +1,68 @@
# BAT-008 Implementation
- Status: completed
- Batch: BAT-008
- Profile: batch-lite
## Plan Summary
REQ-011 AC-1~3에 대응하는 3개 baseline 문서 갱신. 코드/스키마/런타임 변경 없음.
## Changed Files
| 파일 | 변경 유형 | 대응 AC |
|------|----------|---------|
| `docs/interface-contract.md` | 기존 섹션 내 endpoint 상세 명세 추가 | REQ-011 AC-1 |
| `docs/data-model.md` | portfolios.market 컬럼 추가; AuthToken, BrokerCredential, LedgerTrade entity 추가 | REQ-011 AC-2 |
| `docs/runtime-flows.md` | Source Discovery/Batch 헤더 + Change Log 갱신 (내용은 bat-001에서 이미 충족) | REQ-011 AC-3 |
## Execution Log
### docs/interface-contract.md
- Source Discovery/Batch 헤더: `bootstrap-baseline, bat-006` → `bootstrap-baseline, bat-001, bat-006, bat-008`
- GET /health 응답 스키마 갱신: `{"status": "ok"}` 단순 설명 → `{status, service, version, checked_at}` 명시 (HealthResponse 기준)
- `v1 Consumer API (trex-basic)` 섹션 확장:
- `### Authentication` 추가: service-token bearer, DB-issued 조건, trade-write 권한 명시
- `### Endpoint: GET /v1/assets` 추가: query param, AssetsResponse/AssetItem 스키마, HTTP 200/401/403
- `### Endpoint: GET /v1/user_info` 추가: UserInfoResponse/BrokerItem 스키마, HTTP 200/401/403/404
- `### Endpoint: POST /v1/assets/{asset_id}/ledger/trades` 추가: LedgerTradeRequest/LedgerTradeResponse 스키마, HTTP 201/200/409/404/401/403
- `### Idempotency Contract` 추가: deduplication key, duplicate/conflict 처리 방식
- Change Log 항목 추가
### docs/data-model.md
- Source Discovery/Batch 헤더: `bootstrap-baseline, bat-006` → `bootstrap-baseline, bat-001, bat-006, bat-008`
- Model Summary: AuthToken, BrokerCredential, LedgerTrade 추가
- Entity: Portfolio — `market :: text|null` 필드 추가 (migration 20260502_0014 기준)
- Entity: `AuthToken` 신규 추가:
- Key Fields: id, user_id, token_type, token_source, permission, service_name, runtime_id, expires_at, created_at
- Uniqueness Constraints: idx_auth_tokens_service_name_unique, idx_auth_tokens_runtime_id_unique
- State Rules: runtime_id 없는 토큰 v1 거부 명시
- Entity: `BrokerCredential` 신규 추가:
- Key Fields: user_id, broker, account_ref, encrypted_payload, credential_version, last_rotated_at
- PK: (user_id, broker)
- State Rules: 평문 저장 금지, 복호화 실패 처리 명시
- Entity: `LedgerTrade` 신규 추가:
- Key Fields: ledger_entry_id, asset_id, order_id, execution_id, side, quantity, price, fee_amount, tax_amount, executed_at, recorded_at
- Unique Constraint: uq_ledger_trades_idempotency
- Index: idx_ledger_trades_asset_executed
- State Rules: immutable rows, payload conflict → 409
- Change Log 항목 추가
### docs/runtime-flows.md
- Source Discovery/Batch 헤더: `bat-007` 뒤에 `bat-008` 추가
- Change Log 항목 추가: "REQ-011 AC-3 confirmed: v1 API flows and runtime_id issuance flow already present from bat-001"
- 내용 변경 없음 — bat-001에서 추가된 v1 API 처리 흐름 5개, Application Startup Validation, bat-007에서 추가된 Local DB 흐름 2개 모두 현행 유지
## Validation
- `docs/interface-contract.md`: REQ-011 AC-1 대상 4개 엔드포인트 명세 + 인증 방식 포함 확인 ✓
- `docs/data-model.md`: broker_credentials, ledger_trades, portfolios.market, auth_tokens.runtime_id 모두 반영 확인 ✓
- `docs/runtime-flows.md`: v1 API 처리 흐름(4개) + runtime_id 발급 흐름(POST /auth/service-tokens) 존재 확인 ✓
- 코드 변경 없음 — lint/typecheck 불필요
## Remaining Risks
- `docs/runtime-flows.md` v1 API flows는 bat-001에서 최초 작성됨. 이후 구현 변경 시 해당 흐름도 갱신 필요 (현재 기준 코드와 일치 확인됨).
@@ -0,0 +1,41 @@
# BAT-008 Index
- Status: release-candidate
- Batch: BAT-008
- Profile: batch-lite
- Created: 2026-05-11
## Included REQ
| REQ | 제목 | Type | Priority |
|-----|------|------|----------|
| REQ-011 | trex-basic 계약 반영을 위한 baseline 문서 갱신 | Documentation | Medium |
## Source Discovery
- [dcy-002](../../discovery/) (trex-basic v1 API contract — FR/NFR 구현 완료 후 documentation closure)
## Scope Summary
- docs/interface-contract.md: GET /health, GET /v1/assets, GET /v1/user_info, POST /v1/assets/{asset_id}/ledger/trades 엔드포인트 명세 및 service-token bearer 인증 방식 추가 (REQ-011 AC-1)
- docs/data-model.md: broker_credentials, ledger_trades 테이블, portfolios.market, auth_tokens.runtime_id 컬럼 추가 (REQ-011 AC-2)
- docs/runtime-flows.md: v1 API 처리 흐름, runtime_id 발급 흐름 추가 (REQ-011 AC-3)
- 신규 코드/모듈/스키마 변경 없음 — 구현 완료(REQ-004~010 Implemented) 후 문서 동기화
## Impacted Area
- Docs:
- `docs/interface-contract.md` — v1 API 엔드포인트 명세, service-token 인증 방식 추가
- `docs/data-model.md` — broker_credentials, ledger_trades 테이블 및 컬럼 추가
- `docs/runtime-flows.md` — v1 API 처리 흐름 및 runtime_id 발급 흐름 추가
- Modules: 없음
- Tests: 없음
## Stage Documents
| Stage | File | Status |
|-------|------|--------|
| Planning | [planning.md](./planning.md) | confirmed |
| Design | [design.md](./design.md) | confirmed |
| Implementation | [implementation.md](./implementation.md) | completed |
| Verification | [verification.md](./verification.md) | passed |
@@ -0,0 +1,70 @@
# BAT-008 Planning
- Status: confirmed
- Batch: BAT-008
- Profile: batch-lite
## Included REQ
- REQ-011: trex-basic 계약 반영을 위한 baseline 문서 갱신
## Out of Scope
- REQ-004~010 구현 자체 변경 (이미 Implemented 상태)
- 신규 API 엔드포인트 또는 스키마 추가
- runtime-flows.md 중 BAT-007에서 이미 갱신한 Alembic/ensure_schema() 경계 흐름 재작성
- docs/project-structure.md (BAT-007 범위)
## 구현 순서
### 1단계: 현황 파악
- `docs/interface-contract.md` 현재 상태 확인 — v1 API 엔드포인트 명세 누락 여부 점검
- `docs/data-model.md` 현재 상태 확인 — broker_credentials, ledger_trades, portfolios.market, auth_tokens.runtime_id 누락 여부 점검
- `docs/runtime-flows.md` 현재 상태 확인 — v1 API 처리 흐름 및 runtime_id 발급 흐름 누락 여부 점검
### 2단계: docs/interface-contract.md 갱신 (REQ-011 AC-1)
- GET /health 응답 스키마 (extended) 추가
- GET /v1/assets 엔드포인트 명세 추가
- GET /v1/user_info 엔드포인트 명세 추가
- POST /v1/assets/{asset_id}/ledger/trades 엔드포인트 명세 추가
- service-token bearer 인증 방식 섹션 추가 또는 갱신
### 3단계: docs/data-model.md 갱신 (REQ-011 AC-2)
- broker_credentials 테이블 스키마 추가 (user_id, broker, account_ref, encrypted_payload, credential_version, last_rotated_at)
- ledger_trades 테이블 스키마 추가 (ledger_entry_id, asset_id, order_id, execution_id, side, quantity, price, fee_amount, tax_amount, executed_at, recorded_at; unique constraint 포함)
- portfolios.market 컬럼 추가
- auth_tokens.runtime_id 컬럼 추가
### 4단계: docs/runtime-flows.md 갱신 (REQ-011 AC-3)
- v1 API 요청 처리 흐름(인증 → 서비스 → DB) 추가 (GET /v1/assets, GET /v1/user_info, POST /v1/assets/{asset_id}/ledger/trades)
- service-token 발급 시 runtime_id 동시 생성 흐름 추가 (REQ-005 반영)
- BAT-007에서 추가된 기존 섹션과 충돌 없이 통합
## Dependencies
- Internal:
- REQ-004~010: 모두 Implemented 상태 확인됨 (BAT-001 release-candidate) — 선행 조건 충족
- `app/api/v1_assets.py`, `app/api/v1_user_info.py`, `app/api/v1_ledger.py` — 실제 구현 기준으로 명세 작성
- `app/domain/models.py` — broker_credentials, ledger_trades 모델 기준으로 data-model 작성
- `migrations/versions/` — Alembic revision 기준으로 컬럼/테이블 정의 확인
- External: 없음
## Design Gate
- Design Required Before Implementation: no
- Reason: 신규 코드/인터페이스/DB 스키마/런타임 흐름 변경 없음. 구현 완료된 REQ-004~010 결과를 기존 3개 문서에 반영하는 doc-only 작업. 구조적 결정 사항 없음.
## Milestones
1. docs/interface-contract.md 갱신 완료 (v1 API 엔드포인트 4개 + 인증 방식)
2. docs/data-model.md 갱신 완료 (테이블 2개 + 컬럼 2개)
3. docs/runtime-flows.md 갱신 완료 (v1 API 처리 흐름 + runtime_id 발급 흐름)
## Risks
- `docs/runtime-flows.md`에 BAT-007에서 추가된 섹션과 중복 또는 충돌이 생길 수 있음 — 1단계 현황 파악에서 확인 후 통합 방식 결정
- 실제 구현 코드와 문서 내용 불일치 시 코드 기준으로 작성
@@ -0,0 +1,59 @@
# BAT-008 Verification
- Status: passed
- Batch: BAT-008
- Profile: batch-lite
- Architecture Impact: none (confirmed — doc-only, no code/schema/runtime changes)
- Design Gate: no (planning.md confirmed — no structural decisions required)
## Acceptance Mapping
### REQ-011 AC-1: docs/interface-contract.md에 4개 엔드포인트 명세 포함
| Criterion | Evidence | Result |
|-----------|----------|--------|
| GET /health 응답 스키마 포함 | `docs/interface-contract.md` Interface: Primary HTTP API — Outputs 섹션에 `{status, service, version, checked_at}` 명시 (HealthResponse from app/api/health.py 기준) | PASS |
| GET /v1/assets 요청/응답 명세 포함 | `docs/interface-contract.md` Endpoint: GET /v1/assets — query param, AssetsResponse/AssetItem 스키마, HTTP 200/401/403 명시 | PASS |
| GET /v1/user_info 요청/응답 명세 포함 | `docs/interface-contract.md` Endpoint: GET /v1/user_info — UserInfoResponse/BrokerItem 스키마, HTTP 200/401/403/404 명시 | PASS |
| POST /v1/assets/{asset_id}/ledger/trades 요청/응답 명세 포함 | `docs/interface-contract.md` Endpoint: POST /v1/assets/{asset_id}/ledger/trades — LedgerTradeRequest/LedgerTradeResponse 스키마, HTTP 201/200/409/404/401/403 명시 | PASS |
| service-token bearer 인증 방식 포함 | `docs/interface-contract.md` Authentication 섹션 — DB-issued service token 조건, trade-write 권한 명시 | PASS |
**REQ-011 AC-1: PASS**
### REQ-011 AC-2: docs/data-model.md에 신규 테이블/컬럼 문서화
| Criterion | Evidence | Result |
|-----------|----------|--------|
| broker_credentials 테이블 문서화 | `docs/data-model.md` Entity: BrokerCredential — (user_id, broker) PK, account_ref, encrypted_payload, credential_version, last_rotated_at; migration 20260502_0014 기준 | PASS |
| ledger_trades 테이블 문서화 | `docs/data-model.md` Entity: LedgerTrade — ledger_entry_id (BigInt Identity), asset_id, order_id, execution_id, side, quantity, price, fee_amount, tax_amount, executed_at, recorded_at; uq_ledger_trades_idempotency unique constraint 명시 | PASS |
| portfolios.market 컬럼 문서화 | `docs/data-model.md` Entity: Portfolio Key Fields — `market :: text\|null` 추가 | PASS |
| auth_tokens.runtime_id 컬럼 문서화 | `docs/data-model.md` Entity: AuthToken — `runtime_id :: text\|null` 및 idx_auth_tokens_runtime_id_unique 명시 | PASS |
**REQ-011 AC-2: PASS**
### REQ-011 AC-3: docs/runtime-flows.md에 v1 API 처리 흐름과 runtime_id 발급 흐름 포함
| Criterion | Evidence | Result |
|-----------|----------|--------|
| v1 API 처리 흐름 포함 | `docs/runtime-flows.md` — Flow: v1 API — Service-Token Authentication, GET /v1/assets, GET /v1/user_info, POST /v1/assets/{asset_id}/ledger/trades (bat-001에서 추가, 현행 코드와 일치) | PASS |
| runtime_id 발급 흐름 포함 | `docs/runtime-flows.md` — Flow: v1 API — POST /auth/service-tokens: "Calls AuthService.issue_service_token() which creates a new DB token with runtime_id" (bat-001에서 추가) | PASS |
**REQ-011 AC-3: PASS**
## Architecture Impact Confirmation
- batch-lite, Design Gate = no
- 코드/DB 스키마/런타임 흐름 변경 없음
- architecture impact: none ✓
## Blocking Issues
- 없음
## Ready for Release
- true
## Notes
- docs/runtime-flows.md의 v1 API flows는 bat-001 (BAT-001 scope) 에서 이미 작성됨. REQ-011이 명시한 "v1 API 처리 흐름과 runtime_id 발급 흐름" 조건은 bat-001 결과로 충족되어 있었으며, bat-008은 이를 공식 확인하고 Source Discovery를 갱신함.
+4 -1
View File
@@ -12,4 +12,7 @@
| BAT-002 | batch-lite | release-candidate | REQ-012 | minor-change | [bat-002_20260502_service-token-rotate-cli](./bat-002_20260502_service-token-rotate-cli/) |
| BAT-003 | batch-lite | release-candidate | REQ-013 | dcy-003 | [bat-003_20260502_portfolio-list-user-required](./bat-003_20260502_portfolio-list-user-required/) |
| BAT-004 | batch-lite | release-candidate | REQ-014 | minor-change | [bat-004_20260502_portfolio-create-user-required](./bat-004_20260502_portfolio-create-user-required/) |
| BAT-005 | batch-lite | release-candidate | REQ-015 | minor-change | [bat-005_20260503_broker-credential-cli](./bat-005_20260503_broker-credential-cli/) |
| BAT-005 | batch-lite | release-candidate | REQ-015 | minor-change | [bat-005_20260503_broker-credential-cli](./bat-005_20260503_broker-credential-cli/) |
| BAT-006 | standard | release-candidate | REQ-016, REQ-017, REQ-018, REQ-019 | dcy-004 | [bat-006_20260511_portfolio-uuid-migration](./bat-006_20260511_portfolio-uuid-migration/) |
| BAT-007 | batch-lite | release-candidate | REQ-001, REQ-002, REQ-003 | dcy-001 | [bat-007_20260511_local-db-validation-and-operations-runbook](./bat-007_20260511_local-db-validation-and-operations-runbook/) |
| BAT-008 | batch-lite | release-candidate | REQ-011 | dcy-002 | [bat-008_20260511_trex-basic-baseline-docs-update](./bat-008_20260511_trex-basic-baseline-docs-update/) |