feat: apply rel-001 feedback cleanup
This commit is contained in:
@@ -0,0 +1,32 @@
|
||||
# BAT-015 Design
|
||||
|
||||
- Status: confirmed
|
||||
- Batch: BAT-015
|
||||
|
||||
## Architecture Summary
|
||||
- REL-001 잔여 정합화 3건을 단일 batch로 묶어 처리한다. DB schema 마이그레이션 1건(REQ-033), Pydantic 스키마 강화 1건(REQ-034), Health 응답 + baseline 문서 갱신 1건(REQ-035). 변경 면적은 좁지만 cross-cutting nature(데이터 + 인증 + 운영)로 한 batch에 응집된다.
|
||||
|
||||
## Changed Areas
|
||||
- Docs: `docs/data-model.md`(ledger_trades 컬럼), `docs/interface-contract.md`(/health 응답), `docs/runtime-flows.md`(health probe 의미 명문화 — baseline 갱신)
|
||||
- Modules: `app/repositories/postgres.py`(ledger_trades SQL), `app/domain/models.py`(LedgerTrade dataclass, 있다면), `app/api/v1_portfolios.py`(ledger handler 응답 매핑, 있다면), `app/schemas/auth.py`(LoginRequest model_config), `app/api/health.py`(HealthResponse + handler), `migrations/versions/`(신규 revision)
|
||||
- Interfaces: `POST /v1/portfolios/{id}/ledger/trades` 응답 schema 일관성, `POST /auth/login` 422 거부 동작, `GET /health` 응답 확장
|
||||
|
||||
## Key Decisions
|
||||
1. **REQ-033 마이그레이션 전략**: 단일 `ALTER TABLE ledger_trades RENAME COLUMN asset_id TO portfolio_id` 트랜잭션 사용. 별도 컬럼 추가 + backfill + drop 패턴은 채택하지 않음(데이터 의미가 이미 portfolio_id로 일관되어 backfill 불요, dcy-005 A-1 가정 + 사용자 confirm).
|
||||
2. **REQ-034 enforcement 위치**: `LoginRequest.model_config = ConfigDict(extra="forbid")` 스키마 단계에서 처리. `AuthService.login`에 별도 검증 로직 추가 안 함(스키마 단계에서 Pydantic 표준 422 응답이 더 명확).
|
||||
3. **REQ-035 db_enabled 의미**: `settings.db_enabled`(환경변수) 값 노출. actual DB ping은 본 batch 범위 밖이며 `docs/runtime-flows.md`에 의미 명문화로 해소.
|
||||
4. **baseline 갱신 위치**: `docs/runtime-flows.md`의 "Health Probe Flow"(또는 유사 섹션)에 `db_enabled` 의미 노트 1단락 추가. `docs/project-structure.md`는 영향 없음.
|
||||
5. **마이그레이션 revision 번호**: 기존 head(`20260521_0023`) 다음으로 `20260601_NNNN_ledger_trades_portfolio_id_rename.py`. revision 본문은 `op.execute("ALTER TABLE ... RENAME COLUMN ...")` 또는 `op.alter_column(...)` 패턴.
|
||||
|
||||
## Edge Cases
|
||||
- REQ-034: 기존 `LoginRequest`에 정의된 필드만 보내는 client는 영향 없음. `username`/`password` 외 어떤 키든 422로 거부됨.
|
||||
- REQ-035: `PORTFOLIOHUB_DB_ENABLED` 환경변수가 미설정(default `false`)인 경우 `db_enabled: false` 응답. 본 워크스페이스 `.env`는 `true`로 설정되어 있음.
|
||||
- REQ-033: `ledger_trades`에 데이터가 없는 환경(개발 초기)에서도 ALTER COLUMN RENAME은 즉시 완료. 대량 데이터 환경 staging은 dry-run에서 측정.
|
||||
- 3건 통합: 동일 release에 묶일 예정이지만 각 변경이 서로 의존하지 않으므로 implementation 순서 자유. planning에서 권장 순서(REQ-034 → REQ-035 → REQ-033) 명시.
|
||||
|
||||
## Architecture Impact
|
||||
- runtime-flows
|
||||
|
||||
## Reference Doc Update Plan
|
||||
- project-structure.md: none (구조 변경 없음, 기존 모듈에 한정된 변경)
|
||||
- runtime-flows.md: REQ-035에서 `/health` probe flow 섹션에 "`db_enabled`는 환경변수 `PORTFOLIOHUB_DB_ENABLED` 설정값 노출이며 actual DB connectivity ping이 아니다. production-grade readiness probe는 별도 `/readiness` endpoint 분리로 후속 처리"의 의미 명문화 노트 1단락 추가
|
||||
@@ -0,0 +1,53 @@
|
||||
# BAT-015 Implementation
|
||||
|
||||
- Status: completed
|
||||
- Batch: BAT-015
|
||||
|
||||
## Plan Summary
|
||||
- 변경 순서: REQ-034 → REQ-035 → REQ-033 (planning 권장 순서대로 진행)
|
||||
- 각 REQ 구현 후 부분 회귀 (pytest 전체 + 해당 영역 smoke)
|
||||
- 최종 batch 통합 회귀 + 로컬 smoke
|
||||
|
||||
## Changed Files
|
||||
- REQ-034:
|
||||
- `app/schemas/auth.py` — `LoginRequest.model_config = ConfigDict(extra="forbid")` 추가
|
||||
- `tests/test_auth.py` — `test_login_rejects_no_expiry_extra_field`, `test_login_rejects_arbitrary_extra_field` 신규
|
||||
- REQ-035:
|
||||
- `app/api/health.py` — `HealthResponse.db_enabled` 필드 추가 + `healthcheck()` 핸들러가 `settings.db_enabled` 노출
|
||||
- `tests/test_app.py` — `test_healthcheck`에 db_enabled 확인 추가, `test_health_exposes_db_enabled_true`, `test_health_exposes_db_enabled_false` 신규
|
||||
- `docs/runtime-flows.md` — health endpoint 섹션(line 97)에 `db_enabled` 의미 명문화 노트 추가
|
||||
- `docs/interface-contract.md` — `/health` payload schema에 `db_enabled` 필드 추가 + 의미 노트
|
||||
- REQ-033:
|
||||
- `migrations/versions/20260601_0024_ledger_trades_portfolio_id_rename.py` — 신규 alembic revision (ALTER COLUMN RENAME with has_table guard)
|
||||
- `app/domain/models.py` — `LedgerTrade.asset_id` → `portfolio_id`
|
||||
- `app/repositories/portfolio_repository.py` — `insert_ledger_trade` interface 시그니처 갱신
|
||||
- `app/repositories/postgres.py` — `insert_ledger_trade` INSERT/SELECT/RETURNING SQL 컬럼명 + `_row_to_ledger_trade` 매핑
|
||||
- `app/schemas/v1.py` — `LedgerTradeResponse.asset_id` → `portfolio_id`
|
||||
- `app/api/v1_portfolios.py` — ledger handler에서 `insert_ledger_trade(portfolio_id=...)` + 응답 매핑
|
||||
- `tests/test_v1_portfolios_ledger.py` — `LedgerTrade(portfolio_id=...)` + 응답 `body["portfolio_id"]` 검증
|
||||
- `tests/test_v1.py` — `_make_ledger_trade` + `body["portfolio_id"]` 검증
|
||||
- `tests/test_daemon_assignment.py` — mock `trade.portfolio_id` 갱신
|
||||
- `docs/data-model.md` — LedgerTrade entity, Key Fields, Uniqueness Constraints, State Rules의 `asset_id` 표기 모두 `portfolio_id`로 갱신 + revision 20260601_0024 명시
|
||||
- `docs/interface-contract.md` — `POST /v1/portfolios/{id}/ledger/trades` 응답 schema에 `portfolio_id` 명시 + REQ-033 cross-reference
|
||||
|
||||
## Execution Log
|
||||
1. REQ-034: `LoginRequest`에 `extra="forbid"` 적용 → `tests/test_auth.py` 16 passed
|
||||
2. REQ-035: `HealthResponse` 확장 + 핸들러 → `tests/test_app.py` 9 passed → baseline 갱신
|
||||
3. REQ-033: alembic revision 20260601_0024 작성 → `alembic upgrade head` 적용(local DB에 `ledger_trades` 테이블 부재로 `has_table` guard에 의해 안전 skip) → repository/도메인/스키마/라우트 정합 → `tests/test_v1_portfolios_ledger.py` 10 passed
|
||||
4. 통합 회귀 1차: 5 failed 발견 (test_v1.py + test_daemon_assignment.py 잔존 `asset_id` mock) → 갱신 → 통합 회귀 2차: **372 passed, 0 failed**
|
||||
5. 로컬 smoke:
|
||||
- GET /health → HTTP 200 + `db_enabled: true` (REQ-035 AC-1)
|
||||
- POST /auth/login + `no_expiry: true` → HTTP 422 + `extra_forbidden` (REQ-034 AC-2)
|
||||
- codebase grep `ledger_trades.*asset_id` → 0건 (REQ-033 AC-3)
|
||||
- `docs/runtime-flows.md` baseline 노트 grep → 매치 (REQ-035 AC-4)
|
||||
- `docs/interface-contract.md` /health payload grep → 매치 (REQ-035 AC-5)
|
||||
|
||||
## Validation
|
||||
- pytest 전체: **372 passed, 0 failed** (BAT-015 base 368 → +4: test_auth 2건 + test_app 2건 신규)
|
||||
- 로컬 smoke 5건 모두 통과
|
||||
- AC 매핑은 verification.md에서 정리
|
||||
- 실 DB AC (REQ-033 AC-1 `\d ledger_trades`, AC-4 staging dry-run lock 측정): 본 워크스페이스 `ledger_trades` 테이블 미생성 상태로 N/A. REL-002 staging 시점에 별도 수집.
|
||||
|
||||
## Remaining Risks
|
||||
- R-실 DB ledger_trades 테이블 미생성: REQ-033 마이그레이션은 `has_table` guard로 안전하지만, 본 워크스페이스에서 ledger_trades 적재 흐름이 활용되지 않는 상태. BAT-011 release-candidate verification은 mock 기반이라 이 gap이 release 단계에서 발견되지 않았음. **본 batch 범위 밖**, 별도 후속 항목으로 등록 후보(REL-002 또는 후속 Discovery).
|
||||
- (workspace_purpose 메모리: 본 워크스페이스 개발용 → prod 영향 검증 N/A)
|
||||
@@ -0,0 +1,34 @@
|
||||
# BAT-015: REL-001 Feedback 정합화 (ledger 컬럼명 / LoginRequest 엄격화 / health db_enabled)
|
||||
|
||||
- Status: release-candidate
|
||||
- Profile: standard
|
||||
- Source Discovery: dcy-005
|
||||
- Included REQ: REQ-033, REQ-034, REQ-035
|
||||
- Excluded REQ: 없음
|
||||
|
||||
## Summary
|
||||
- dcy-005(rel-001-feedback-cleanup) 산출물 3건을 단일 batch로 묶어 일괄 정합화한다. 3건 모두 변경 면적이 좁고 같은 release(REL-002 분리 예정) target으로 일관된 정합화 묶음 trigger를 가진다.
|
||||
|
||||
## Scope
|
||||
- In Scope:
|
||||
- REQ-033: `ledger_trades.asset_id` → `portfolio_id` 컬럼명 정합화 (Alembic 신규 revision + postgres.py SQL 갱신 + 도메인 모델/라우트 정합)
|
||||
- REQ-034: `LoginRequest`에 `model_config = ConfigDict(extra="forbid")` 적용 + pytest 422 회귀
|
||||
- REQ-035: `HealthResponse`에 `db_enabled: bool` 필드 추가 + `healthcheck()` 핸들러 갱신 + `docs/runtime-flows.md` baseline 명문화 + `docs/interface-contract.md` /health 응답 schema 갱신
|
||||
- Out of Scope:
|
||||
- `ledger_trades` 의미 재정의(asset 단위 vs portfolio 단위 — REQ-033 portfolio_id 의미 확정 가정)
|
||||
- session token TTL 정책 변경(REQ-021 본문 자체 갱신은 본 batch 범위 밖)
|
||||
- `/readiness` endpoint 분리(production-grade readiness probe, 별도 후속 Discovery로 처리)
|
||||
- REL-001 verification 재실행 (별도 REL-002로 분리 — dcy-005 OQ-3 결정)
|
||||
|
||||
## Documents
|
||||
- Planning: [planning.md](./planning.md)
|
||||
- Design: [design.md](./design.md)
|
||||
- Implementation: [implementation.md](./implementation.md)
|
||||
- Verification: [verification.md](./verification.md)
|
||||
|
||||
## Notes
|
||||
- 공통 trigger: REL-001 Feedback Handoff 3건 정합화 묶음
|
||||
- 공통 release: REL-002 분리 배포 예정(dcy-005 OQ-3 결정, 2026-06-01)
|
||||
- REQ-033 마이그레이션 staging dry-run을 verification 단계에서 수행해 운영 lock 시간 < 1초(NFR-1) 측정
|
||||
- REQ-035는 baseline 문서(`docs/runtime-flows.md`) 갱신 요구를 포함하므로 design 단계에서 architecture impact를 `runtime-flows`로 표기
|
||||
- 본 워크스페이스는 개발용이므로 prod 운영 영향 검증은 release 단계에서 별도 수행 (memory: workspace_purpose)
|
||||
@@ -0,0 +1,50 @@
|
||||
# BAT-015 Planning
|
||||
|
||||
- Status: confirmed
|
||||
- Batch: BAT-015
|
||||
- Profile: standard
|
||||
|
||||
## Included REQ
|
||||
- REQ-033 (Migration): ledger_trades.asset_id → portfolio_id 컬럼명 정합화
|
||||
- REQ-034 (Interface): LoginRequest 추가 필드 거부 enforcement
|
||||
- REQ-035 (Interface): GET /health 응답에 db_enabled 필드 추가
|
||||
|
||||
## Delivery Plan
|
||||
- 순서: REQ-034 → REQ-035 → REQ-033
|
||||
- REQ-034 우선: 1줄 schema 변경 + pytest 422 회귀 (저위험·빠른 evidence)
|
||||
- REQ-035 후속: HealthResponse 확장 + baseline 문서 갱신 (저위험·문서 변경 포함)
|
||||
- REQ-033 마지막: Migration 동반 (가장 큰 변경, alembic revision + DB schema, staging dry-run 필요)
|
||||
- 선행 조건:
|
||||
- dcy-005 confirmed (2026-06-01) ✓
|
||||
- REQ-033/034/035 Approved (2026-06-01) ✓
|
||||
- 본 워크스페이스 개발용 메모리 확인 — prod 영향 검증은 release 단계 ✓
|
||||
- batch 통합 회귀: 3건 변경 후 전체 pytest 회귀 + 로컬 smoke (`/health`, `/auth/login`, `/v1/portfolios/{id}/ledger/trades`)
|
||||
|
||||
## Dependencies
|
||||
- Internal:
|
||||
- dcy-005: Discovery 본문에 OQ-1/OQ-2/OQ-3 결정 명문화 완료
|
||||
- REQ-021: 본 batch가 REQ-021 AC-5 follow-up을 해소(REQ-034). REQ-021 본문 변경은 본 batch 범위 밖이며 cross-reference만 추가.
|
||||
- REQ-029: 본 batch가 REQ-029 Notes follow-up을 해소(REQ-033). REQ-029 본문 변경은 본 batch 범위 밖.
|
||||
- REL-001: 별도 release로 분리 배포 진행 중. 본 batch는 REL-002로 분리.
|
||||
- External:
|
||||
- 없음 (외부 client/integration 의존 없음)
|
||||
|
||||
## Design Gate
|
||||
- Design Required Before Implementation: yes
|
||||
- Reason: REQ-033이 DB schema 마이그레이션을 동반하고 REQ-035가 baseline 문서(`docs/runtime-flows.md`) 갱신을 요구한다. Architecture impact가 있으므로 design 단계에서 architecture 결정과 baseline 갱신 계획을 명문화한 뒤 implementation 진입.
|
||||
|
||||
## Milestones
|
||||
1. Design 단계: REQ-033 마이그레이션 전략 결정(ALTER COLUMN RENAME vs 별도 컬럼 추가+backfill), REQ-035 baseline 문서 갱신 위치 결정
|
||||
2. Implementation 단계: REQ-034 → REQ-035 → REQ-033 순서로 구현
|
||||
3. Verification 단계: pytest 회귀(전체 369+ passed 기대) + REQ-033 staging dry-run + 로컬 smoke (`/health` 응답, `/auth/login` 422, `/v1/portfolios/{id}/ledger/trades` 회귀)
|
||||
4. confirm-batch-verification 후 confirm-req-implemented 3건 일괄 전환
|
||||
|
||||
## Risks
|
||||
- R-1 (Low): REQ-033 ALTER COLUMN RENAME이 ledger_trades 테이블에 lock을 유발해 운영 영향 발생.
|
||||
- 대응: staging dry-run으로 lock 시간 측정(< 1초 권장), 짧은 단일 트랜잭션 패턴 적용. 본 워크스페이스는 개발용이라 prod 영향 검증은 release 단계.
|
||||
- R-2 (Low): REQ-034 `extra="forbid"`가 기존 ignored field를 보내던 외부 client를 silently 깨뜨림.
|
||||
- 대응: 본 워크스페이스 개발용이라 외부 monitoring 영향 없음. 회귀 테스트에서 기존 username/password만 보내는 케이스 통과 확인.
|
||||
- R-3 (Low): REQ-035 `HealthResponse` 확장이 strict JSON schema validation 사용 monitoring 도구를 깨뜨림.
|
||||
- 대응: 본 워크스페이스 개발용이라 영향 없음. prod 배포 시 사전 점검(REL-002 verification).
|
||||
- R-4 (Low): 3건 변경이 통합 회귀에서 예상치 못한 상호 영향 발생.
|
||||
- 대응: 각 REQ 구현 후 pytest 회귀 incremental 실행, batch 마지막에 전체 회귀.
|
||||
@@ -0,0 +1,60 @@
|
||||
# BAT-015 Verification
|
||||
|
||||
- Status: confirmed
|
||||
- Batch: BAT-015
|
||||
|
||||
## Acceptance Mapping
|
||||
|
||||
### REQ-033: ledger_trades.asset_id → portfolio_id 컬럼명 정합화
|
||||
- AC-1 (`\d ledger_trades`에 `portfolio_id` 존재, `asset_id` 부재): **Deferred to REL-002 staging** — 로컬 DB에 `ledger_trades` 테이블이 미생성 상태(BAT-011 release-candidate는 mock 기반 verification으로 진행됨). 마이그레이션 `20260601_0024`는 `has_table` 가드로 안전 skip되며, 코드 작성은 완료([migrations/versions/20260601_0024_ledger_trades_portfolio_id_rename.py:36-40](../../../migrations/versions/20260601_0024_ledger_trades_portfolio_id_rename.py#L36-L40)). 실 DB 검증은 REL-002 staging에서 수행.
|
||||
- AC-2 (INSERT/SELECT 회귀): `tests/test_v1_portfolios_ledger.py` 10 PASSED (mock 기반). `insert_ledger_trade(portfolio_id=...)` 시그니처 + 응답 schema `LedgerTradeResponse.portfolio_id` 일관 검증.
|
||||
- AC-3 (codebase grep `ledger_trades\..*asset_id` 0건): PASSED. `grep -rn "ledger_trades\..*asset_id\|asset_id.*ledger_trades\|insert_ledger_trade.*asset_id\|trade\.asset_id" app/` → 0건 (2026-06-05 implementation smoke).
|
||||
- AC-4 (마이그레이션 lock < 1초, NFR-1): **Deferred to REL-002 staging** — workspace_purpose 메모리 정합(개발용 워크스페이스). staging dry-run으로 측정.
|
||||
- AC-5 (전체 pytest 회귀 통과): PASSED. **372 passed, 0 failed** (5월 21일 base 368 → +4 누적: test_auth +2, test_app +2).
|
||||
|
||||
### REQ-034: LoginRequest 추가 필드 거부 enforcement
|
||||
- AC-1 (정상 username/password → HTTP 200): `tests/test_auth.py` 기존 회귀 16 PASSED (정상 login flow는 기존 케이스로 회귀 보호).
|
||||
- AC-2 (`no_expiry: true` → HTTP 422 + `extra_forbidden`): `tests/test_auth.py::test_login_rejects_no_expiry_extra_field` PASSED. 로컬 smoke 응답 `{"detail":[{"type":"extra_forbidden","loc":["body","no_expiry"],"msg":"Extra inputs are not permitted","input":true}]}` 확인 (2026-06-05).
|
||||
- AC-3 (임의 추가 필드 → HTTP 422): `tests/test_auth.py::test_login_rejects_arbitrary_extra_field` PASSED.
|
||||
- AC-4 (grep `extra.*forbid` app/schemas/auth.py 매치): PASSED. [app/schemas/auth.py:14](../../../app/schemas/auth.py#L14) `model_config = ConfigDict(extra="forbid")` 매치.
|
||||
- AC-5 (전체 pytest 회귀 통과): PASSED. 372 passed, 0 failed.
|
||||
|
||||
### REQ-035: GET /health 응답에 db_enabled 필드 추가
|
||||
- AC-1 (PORTFOLIOHUB_DB_ENABLED=true → `db_enabled: true`): `tests/test_app.py::test_health_exposes_db_enabled_true` PASSED. 로컬 smoke 응답 `{"status":"ok","service":"portfolio-hub","version":"0.1.0","checked_at":"2026-06-05T13:44:04.724864+09:00","db_enabled":true}` 확인 (2026-06-05).
|
||||
- AC-2 (PORTFOLIOHUB_DB_ENABLED=false → `db_enabled: false`): `tests/test_app.py::test_health_exposes_db_enabled_false` PASSED.
|
||||
- AC-3 (5필드 노출 + 기존 4필드 회귀): `tests/test_app.py::test_healthcheck` 갱신 PASSED (`db_enabled` 추가 검증 포함). 기존 `status/service/version/checked_at` 동작 회귀 유지.
|
||||
- AC-4 (baseline grep `db_enabled.*설정값|setting value|ping|readiness` docs/runtime-flows.md): PASSED. [docs/runtime-flows.md:97](../../../docs/runtime-flows.md#L97)에 의미 명문화 노트 1단락 추가.
|
||||
- AC-5 (interface-contract grep `/health.*db_enabled` docs/interface-contract.md): PASSED. [docs/interface-contract.md:48](../../../docs/interface-contract.md#L48) `GET /health payload` schema에 `db_enabled` 필드 추가.
|
||||
|
||||
## Evidence
|
||||
|
||||
- Test:
|
||||
- 전체 pytest: **372 passed, 0 failed, 3 warnings** (2026-06-05, deprecation warnings는 starlette HTTP_422_UNPROCESSABLE_ENTITY alias, 기능 무관)
|
||||
- 신규 회귀 4건: `test_login_rejects_no_expiry_extra_field`, `test_login_rejects_arbitrary_extra_field`, `test_health_exposes_db_enabled_true`, `test_health_exposes_db_enabled_false`
|
||||
- 영향 받은 기존 회귀 갱신: `test_v1_portfolios_ledger.py` (portfolio_id 응답 필드), `test_v1.py::test_v1_ledger_trade_*` (`_make_ledger_trade` + body 키), `test_daemon_assignment.py::test_ledger_trade_201_when_daemon_matches` (mock attribute), `test_app.py::test_healthcheck` (`db_enabled` 필드 확인 추가)
|
||||
- Manual:
|
||||
- 2026-06-05 13:44 KST 로컬 smoke 5건 (implementation.md Execution Log step 5 참조):
|
||||
- GET /health → 200 + `db_enabled: true`
|
||||
- POST /auth/login + `no_expiry: true` → 422 + extra_forbidden
|
||||
- codebase grep ledger_trades.asset_id → 0건
|
||||
- docs/runtime-flows.md db_enabled 의미 노트 grep → 매치
|
||||
- docs/interface-contract.md /health payload `db_enabled` grep → 매치
|
||||
- Logs:
|
||||
- alembic upgrade head (2026-06-05): `Running upgrade 20260521_0023 -> 20260601_0024, Rename ledger_trades.asset_id to portfolio_id for column-name semantic alignment (REQ-033)` — `has_table` guard로 안전 skip 확인
|
||||
|
||||
## Architecture Impact Synchronization (Baseline)
|
||||
|
||||
- design.md Architecture Impact: `runtime-flows`
|
||||
- Reference Doc Update Plan 이행:
|
||||
- `docs/project-structure.md`: 갱신 불요 (영향 없음, 기존 모듈 한정 변경) — 변경 없음 확인 ✓
|
||||
- `docs/runtime-flows.md`: `db_enabled` 의미 명문화 노트 [line 97](../../../docs/runtime-flows.md#L97) 추가 ✓
|
||||
- 추가 baseline 갱신: `docs/data-model.md` LedgerTrade entity 컬럼명/제약 갱신, `docs/interface-contract.md` /health payload + LedgerTradeResponse schema 갱신
|
||||
|
||||
## Result
|
||||
- Ready for Release: true
|
||||
- Blocking Issues:
|
||||
- 없음 (Deferred 2건은 blocker 아님, REL-002 staging에 이연 evidence)
|
||||
- Deferred Evidence (release execution 시 수집):
|
||||
- REQ-033 AC-1 (`\d ledger_trades` 실 DB 컬럼 검증) — REL-002 staging
|
||||
- REQ-033 AC-4 (마이그레이션 lock < 1초 staging dry-run) — REL-002 staging
|
||||
- 본 워크스페이스 ledger_trades 테이블 미생성 상태 자체는 본 batch 범위 밖 (BAT-011 mock 기반 verification의 후속 항목 후보)
|
||||
@@ -22,3 +22,4 @@
|
||||
| BAT-012 | standard | release-candidate | REQ-002, REQ-010 | dcy-001, dcy-002, dcy-003 | [bat-012](./bat-012_20260521_assets-tagging-and-admin-get-redefinition/) |
|
||||
| BAT-013 | batch-lite | release-candidate | REQ-005 | dcy-002 | [bat-013](./bat-013_20260521_asset-tag-apply-remove-fix/) |
|
||||
| BAT-014 | batch-lite | release-candidate | REQ-025, REQ-026, REQ-027, REQ-028, REQ-032 | dcy-003 | [bat-014](./bat-014_20260524_portfolioitem-symbol-response-contract/) |
|
||||
| BAT-015 | standard | release-candidate | REQ-033, REQ-034, REQ-035 | dcy-005 | [bat-015](./bat-015_20260601_rel-001-feedback-cleanup/) |
|
||||
|
||||
+7
-7
@@ -261,14 +261,14 @@
|
||||
|
||||
## Entity: `LedgerTrade`
|
||||
|
||||
- Purpose: record idempotent trade execution entries submitted by trex-basic via POST /v1/assets/{asset_id}/ledger/trades
|
||||
- Source of Truth: ledger_trades table (added by migration 20260502_0014)
|
||||
- Lifecycle: inserted once per unique (asset_id, order_id, execution_id) tuple; never updated or deleted
|
||||
- Purpose: record idempotent trade execution entries submitted by trex-basic via POST /v1/portfolios/{portfolio_id}/ledger/trades
|
||||
- Source of Truth: ledger_trades table (added by migration 20260502_0014; column `asset_id` renamed to `portfolio_id` by migration 20260601_0024 — REQ-033 / dcy-005 FR-1)
|
||||
- Lifecycle: inserted once per unique (portfolio_id, order_id, execution_id) tuple; never updated or deleted
|
||||
|
||||
### Key Fields
|
||||
|
||||
- ledger_entry_id :: bigint (Identity) :: auto-generated sequential primary key
|
||||
- asset_id :: text :: portfolio_id (UUID-format text after BAT-006) referencing the trading portfolio
|
||||
- portfolio_id :: text :: portfolios.portfolio_id (UUID-format text) referencing the trading portfolio (renamed from `asset_id` by REQ-033)
|
||||
- order_id :: text :: broker-assigned order identifier
|
||||
- execution_id :: text :: broker-assigned execution/fill identifier
|
||||
- side :: text :: "buy" or "sell"
|
||||
@@ -281,14 +281,14 @@
|
||||
|
||||
### Uniqueness Constraints
|
||||
|
||||
- uq_ledger_trades_idempotency :: UNIQUE (asset_id, order_id, execution_id) :: idempotency guarantee; duplicate inserts are detected and return HTTP 200 + status="duplicate"
|
||||
- idx_ledger_trades_asset_executed :: INDEX (asset_id, executed_at) :: time-range query optimization
|
||||
- uq_ledger_trades_idempotency :: UNIQUE (portfolio_id, order_id, execution_id) :: idempotency guarantee; duplicate inserts are detected and return HTTP 200 + status="duplicate"
|
||||
- idx_ledger_trades_asset_executed :: INDEX (portfolio_id, executed_at) :: time-range query optimization (constraint/index objects retain their original names; only the underlying column was renamed)
|
||||
|
||||
### State Rules
|
||||
|
||||
- Rows are immutable after insertion; no UPDATE path exists.
|
||||
- Payload conflict detection: if unique key matches but payload differs → HTTP 409 Conflict.
|
||||
- asset_id is TEXT (not a FK) but must correspond to a valid portfolios.portfolio_id before insertion.
|
||||
- portfolio_id is TEXT (not a FK) but must correspond to a valid portfolios.portfolio_id before insertion.
|
||||
|
||||
## Shared Consistency Rules
|
||||
|
||||
|
||||
@@ -0,0 +1,224 @@
|
||||
# 0. 문서 상태
|
||||
이 문서의 현재 상태와 식별 정보를 기록한다. 문서 추적과 승인 흐름에 필요한 최소 메타데이터를 적는다.
|
||||
|
||||
- 상태: confirmed
|
||||
- 문서 ID: dcy-005_20260601_rel-001-feedback-cleanup
|
||||
- 이슈명: REL-001 Feedback 정합화 — ledger_trades.asset_id 명명 / session no_expiry enforcement / health db_enabled
|
||||
- 작성 시각(KST): 2026-06-01 17:00
|
||||
- 마지막 갱신 시각(KST): 2026-06-01 19:00
|
||||
- 대체됨: 없음
|
||||
- 후속 Discovery 참조: 없음
|
||||
- 생성된 REQ 참조: REQ-033 ([req-033](../srs/Migration/req-033_ledger-trades-asset-id-to-portfolio-id-rename.md)), REQ-034 ([req-034](../srs/Interface/req-034_login-request-extra-forbid-enforcement.md)), REQ-035 ([req-035](../srs/Interface/req-035_health-response-db-enabled-field.md))
|
||||
|
||||
# 1. 계획 상태 요약
|
||||
현재 Discovery가 어떤 배경에서 작성되고 있는지, 연계 문서가 있는지, 지금 시점의 해석이 무엇인지 짧게 요약한다.
|
||||
|
||||
- 연계 문서 현황:
|
||||
- 관련 점수 파일: 미생성
|
||||
- 관련 TODO/메모: [rel-001 Feedback Handoff](../releases/rel-001_20260521_v1-api-full-surface.md#L107-L126)에 등록된 3건의 follow-up 후보
|
||||
- 해석:
|
||||
- REL-001 배포 준비 단계에서 누적된 3건의 잔여 정합화 항목(`ledger_trades.asset_id` 컬럼명 혼용, session no_expiry 코드 enforcement 누락, `/health` 응답 모델 `db_enabled` 부재)을 한 묶음 Discovery로 추적해 다음 batch에서 일괄 정리한다.
|
||||
- 3건 모두 작은 범위 변경이며 backwards-compat 영향이 제한적이므로 단일 Discovery + 단일 batch(Lite 또는 표준)로 처리한다. REL-001은 현 상태로 배포 진행하고 dcy-005 3건은 **별도 REL-002로 분리**(OQ-3 결정, 2026-06-01).
|
||||
|
||||
# 2. 요구사항 판정 결과
|
||||
이번 요청이 기존 요구사항과 비교해 신규인지, 유사한지, 중복인지 판단하고 그 근거를 남긴다.
|
||||
|
||||
- 판정: 신규
|
||||
- 근거
|
||||
- 3건 모두 기존 REQ(REQ-021, REQ-029) Notes/follow-up에 등록된 잔여 항목이며 별도 REQ로 분해된 적이 없다.
|
||||
- 정합화 묶음이라는 trigger(REL-001 Feedback 일괄 정리)는 기존 Discovery에 없다.
|
||||
|
||||
# 3. 문제점의 요약
|
||||
문제의 현재 상태, 기대 상태, 관련 이해관계자, 제약사항을 정리한다. Discovery의 핵심 맥락을 가장 먼저 이해할 수 있어야 한다.
|
||||
|
||||
- 현재 상태
|
||||
- **3건-1 `ledger_trades.asset_id` 컬럼명 혼용**: REQ-029 구현에서 ledger 경로를 `/v1/portfolios/{id}/ledger/trades`로 이전했으나, `ledger_trades` 테이블의 `asset_id` 컬럼은 실제로는 portfolio_id를 저장한다([app/repositories/postgres.py:1284-1294](../../app/repositories/postgres.py#L1284-L1294) INSERT 컬럼 순서 + RETURNING). 컬럼명과 의미가 일치하지 않아 schema reader가 혼동.
|
||||
- **3건-2 session no_expiry 코드 enforcement 누락**: REQ-021 정책 본문([docs/srs/Documentation/req-021_token-expiry-and-rotation-policy.md:15-17](../srs/Documentation/req-021_token-expiry-and-rotation-policy.md#L15-L17))은 "`no_expiry`는 service 한정, session/client에서 시도하면 HTTP 422 또는 ValueError로 거부"로 명시. 그러나 현재 `AuthService.login`([app/services/auth_service.py:47-65](../../app/services/auth_service.py#L47-L65))과 `LoginRequest` 스키마([app/schemas/auth.py:13-23](../../app/schemas/auth.py#L13-L23))에는 `no_expiry` 옵션 자체가 존재하지 않아 실질적으로 시도 불가하지만, 정책 명세 ↔ 코드 매핑(REQ-021 AC-5)에서 "명시적 거부 로직 미구현" follow-up이 등록됨.
|
||||
- **3건-3 `/health` 응답에 `db_enabled` 필드 미노출**: `HealthResponse` 모델([app/api/health.py:17-21](../../app/api/health.py#L17-L21))이 `status`, `service`, `version`, `checked_at` 4필드만 노출. REL-001 verification 체크리스트는 "GET /health → HTTP 200 + `db_enabled: true`"를 요구하지만 응답 모델에 해당 필드 없음 — 외부 health probe가 DB 연결 상태를 직접 관찰할 수 없는 gap.
|
||||
- 기대 상태
|
||||
- 3건-1: 컬럼명을 의미와 일치시키거나(`portfolio_id`로 rename), 또는 ledger_trades가 정말 asset 단위로 의미가 있는지 재정의. 마이그레이션 동반.
|
||||
- 3건-2: session 발급 경로에서 `no_expiry` 시도 자체를 거부하는 명시적 enforcement 코드 추가(현재는 옵션 부재로 시도 불가지만 정책 명문화 완결성). 또는 정책 본문에 "현재 코드 구조상 시도 불가, 명시적 거부 로직 불요"로 갱신.
|
||||
- 3건-3: `HealthResponse`에 `db_enabled: bool` 필드 추가 또는 별도 `/readiness` endpoint 분리. 어느 경로로 갈지 결정.
|
||||
- 이해관계자
|
||||
- Backend 팀 — 코드 변경 및 마이그레이션 책임
|
||||
- Operations — `/health` 응답 변경 시 health probe 동작 검증
|
||||
- REL-001 배포 이후 trex-basic 등 ledger 호출 클라이언트 — `ledger_trades.asset_id` 컬럼명 변경 시 외부 query 의존성 확인 필요(있는 경우)
|
||||
- 제약사항
|
||||
- REL-001은 현 상태로 배포 진행, dcy-005 결과물은 별도 REL-002로 분리(OQ-3 결정, 2026-06-01). REL-001 verification 재실행 불요.
|
||||
- 마이그레이션을 동반하면 dcy-004 / REQ-017 패턴(CREATE INDEX CONCURRENTLY 등)과 정합 유지. REL-001 마이그레이션 chain(`20260521_0023`) 다음 revision으로 자연스럽게 추가.
|
||||
- `/health` 응답 schema 변경은 backwards-compat 영향 — 기존 client(probe, monitoring)가 추가 필드를 허용하는지 확인.
|
||||
|
||||
## 주요 사용자 시나리오
|
||||
사용자 또는 운영자가 실제로 어떤 상황에서 이 변경을 필요로 하는지 대표 시나리오를 적는다.
|
||||
|
||||
- DB schema reader가 `ledger_trades.asset_id`를 보고 assets 테이블 참조라고 오해해 잘못된 join query 작성.
|
||||
- 정책 감사자가 REQ-021 vs 실제 코드를 매핑할 때 session에서 `no_expiry` 거부가 코드에 명시적으로 없어 명확성 부족으로 audit finding 작성.
|
||||
- 운영자가 prod에서 health probe 응답을 monitoring 도구로 받을 때 DB 연결 끊김을 빠르게 감지하지 못함(현재는 status: ok만 노출).
|
||||
|
||||
# 4. 이번에 정의할 변경
|
||||
이번 Discovery에서 다루는 핵심 변경 항목을 목록으로 적는다. 기능, 구조, 규칙 변경을 함께 포함해도 된다.
|
||||
|
||||
- `ledger_trades.asset_id` 컬럼명을 의미에 맞게 `portfolio_id`로 변경하는 마이그레이션 + 코드 경로 정합화.
|
||||
- `LoginRequest`에 `model_config = ConfigDict(extra="forbid")` 적용으로 session `no_expiry` 시도 자체를 HTTP 422로 거부 (OQ-1 결정, 2026-06-01).
|
||||
- `HealthResponse` 모델에 `db_enabled: bool` 필드 추가 + `healthcheck()` 핸들러가 `settings.db_enabled` 노출 (OQ-2 결정, 2026-06-01).
|
||||
|
||||
## 영향 범위
|
||||
변경이 미칠 코드/문서/운영 범위를 명시해 REQ 정제와 batch planning에서 추정 누락을 줄인다.
|
||||
|
||||
- 코드 영향:
|
||||
- 3건-1: `app/repositories/postgres.py` ledger_trades INSERT/SELECT 컬럼 변경, `app/domain/models.py` LedgerTrade dataclass 필드명 (있다면), `app/api/v1_portfolios.py` ledger route handler, `migrations/versions/` 신규 revision (rename column with backfill).
|
||||
- 3건-2: `app/schemas/auth.py:LoginRequest` — `model_config = ConfigDict(extra="forbid")` 추가 (OQ-1 결정 (a) 경로). `app/services/auth_service.py:login`은 변경 없음.
|
||||
- 3건-3: `app/api/health.py` HealthResponse에 `db_enabled: bool` 필드 추가 + `healthcheck()` 핸들러가 `settings.db_enabled` 노출 (OQ-2 결정 (a) 경로). 별도 `/readiness` router 신설은 본 Discovery 범위 밖.
|
||||
- 문서 영향:
|
||||
- 3건-1: `docs/data-model.md` ledger_trades 컬럼 명세, `docs/interface-contract.md` ledger 응답 schema(asset_id가 응답에 노출되는 경우)
|
||||
- 3건-2: `docs/srs/Documentation/req-021_token-expiry-and-rotation-policy.md` (선택 시 본문 갱신), `docs/runtime-flows.md` Token Expiry 섹션
|
||||
- 3건-3: `docs/interface-contract.md` /health 응답 schema(`db_enabled` 추가), `docs/runtime-flows.md` health probe flow에 "db_enabled는 설정값 노출, actual ping 아님" 명문화 (OQ-2 결정 (a) 경로)
|
||||
- 3건 공통: 본 Discovery + 후속 REQ + (선택) baseline 문서 정합
|
||||
- 운영 영향:
|
||||
- 3건-1: 마이그레이션 실행 시 ledger_trades 테이블 잠금 영향. CREATE INDEX CONCURRENTLY 패턴 적용 또는 짧은 ALTER COLUMN RENAME 트랜잭션.
|
||||
- 3건-2: `extra="forbid"` 적용으로 LoginRequest 전체에 추가 필드 거부 효과. 기존 client(현재는 username/password만 전송)는 영향 없음. 향후 외부 client가 ignored field를 보내고 있었다면 422로 깨질 수 있으나, 본 워크스페이스는 개발용이라 영향 무.
|
||||
- 3건-3: health probe 응답 필드 확장 — 본 워크스페이스는 개발용이라 외부 monitoring 영향 없음. 미래 prod 도입 시 도구가 추가 필드를 허용하는지 사전 확인.
|
||||
|
||||
# 5. 요구사항 목록
|
||||
실제로 구현과 검증으로 이어질 요구사항을 기능/비기능으로 나누어 정리하고, 범위 경계를 명확히 적는다.
|
||||
|
||||
## 기능 요구사항
|
||||
- FR-1: `ledger_trades.asset_id` 컬럼을 `portfolio_id`로 rename하는 Alembic 마이그레이션을 작성한다. 기존 데이터는 동일 의미(portfolio_id 저장)이므로 backfill 불요. INSERT/SELECT/RETURNING SQL 및 LedgerTrade dataclass 필드명도 함께 정합화한다.
|
||||
- FR-2: REQ-021의 "session에서 `no_expiry` 거부" 정책을 `LoginRequest` 스키마 단계에서 코드 enforcement로 명시적 거부한다(OQ-1 결정, 2026-06-01 — (a) 경로). 구현 방법: `LoginRequest`에 `model_config = ConfigDict(extra="forbid")` 적용 또는 별도 validator로 `no_expiry` 패턴 거부. 외부 client가 LoginRequest에 정의되지 않은 필드(`no_expiry` 포함)를 전송하면 HTTP 422로 거부된다. pytest 회귀 케이스 1건(TestClient `/auth/login` body에 `no_expiry: true` 포함 → 422 확인) 신설.
|
||||
- FR-3: `/health` 응답 본문에 `db_enabled: bool` 필드를 노출한다(OQ-2 결정 (a) 경로, 2026-06-01). `HealthResponse` 모델에 `db_enabled: bool` 필드 추가 + `healthcheck()` 핸들러가 `settings.db_enabled` 값을 그대로 채운다. **본 값은 환경변수 `PORTFOLIOHUB_DB_ENABLED` 설정값을 노출하는 것이며 실제 DB connectivity ping이 아님**을 docs/runtime-flows.md(또는 응답 모델 docstring)에 명문화한다. 미래 production monitoring 정착 시 `/readiness` endpoint 분리는 별도 Discovery로 분리 가능.
|
||||
|
||||
## 비기능 요구사항
|
||||
성능, 운영성, 일관성, 자동화 안정성 등 기능 외 요구사항을 적는다.
|
||||
|
||||
- NFR-1: 3건 모두 backwards-compat 영향 최소화. 마이그레이션(FR-1)은 짧은 ALTER COLUMN RENAME으로 처리하고 운영 lock 시간을 < 1초 권장. /health 응답 확장(FR-3)은 필드 추가형으로 기존 client 영향 없음.
|
||||
- NFR-2: 본 Discovery 결과물(후속 batch verification)은 자동 테스트로 검증 가능해야 한다. ledger_trades column rename은 마이그레이션 적용 후 INSERT/SELECT 회귀, `/health` 확장은 응답 본문 schema 검증, no_expiry enforcement는 `/auth/login` body에 추가 필드 포함 시 HTTP 422 회귀.
|
||||
|
||||
## 범위 경계
|
||||
이번 반복에서 반드시 포함할 범위와 명시적으로 제외할 범위를 구분한다.
|
||||
|
||||
- In Scope
|
||||
- `ledger_trades.asset_id` 컬럼명 정합화 (rename + 코드/스키마 갱신)
|
||||
- session `no_expiry` 코드 enforcement (`LoginRequest` extra="forbid") — OQ-1 결정 (a)
|
||||
- `HealthResponse`에 `db_enabled: bool` 필드 추가 (settings 값 노출, OQ-2 결정 (a))
|
||||
- 위 3건의 마이그레이션 + 회귀 테스트
|
||||
- Out of Scope
|
||||
- ledger_trades 의미 재정의(asset 단위 vs portfolio 단위 — 본 REQ는 portfolio_id 의미 확정 가정)
|
||||
- session token TTL 정책 변경 (REQ-021 본문 자체 정책 갱신은 본 Discovery 범위 밖)
|
||||
- prod 운영 health probe 도구 변경 (운영 영역)
|
||||
- prod admin token rotation 등 보안 hotfix (본 워크스페이스는 개발용 — 참조 메모리)
|
||||
|
||||
# 6. 리스크/가정 목록
|
||||
불확실성과 전제 조건을 분리해서 적는다. 리스크는 영향과 완화 방안을, 가정은 현재 판단의 전제를 기록한다.
|
||||
|
||||
## 리스크
|
||||
- R-1 (Low): `ledger_trades.asset_id` 컬럼명 변경 시 외부 client(trex-basic 등)가 컬럼명을 직접 query하면 깨질 수 있음.
|
||||
- 완화 방안: trex-basic은 HTTP API(POST /v1/portfolios/{id}/ledger/trades)만 호출하므로 DB 컬럼명 의존 없음 추정. 마이그레이션 전 외부 query 의존성 확인.
|
||||
- R-2 (Low): `/health` 응답 본문 확장 시 monitoring 도구가 strict JSON schema validation을 사용하면 깨질 수 있음.
|
||||
- 완화 방안: 본 워크스페이스는 개발용이라 외부 monitoring 영향 없음. prod 배포 시 사전 점검.
|
||||
- R-3 (Low): 마이그레이션 적용 순서가 잘못되어 ledger_trades INSERT가 일시 실패할 수 있음.
|
||||
- 완화 방안: 배포 전 staging에서 dry-run, 또는 ALTER COLUMN RENAME과 코드 배포를 같은 release window에 묶음.
|
||||
|
||||
## 가정
|
||||
아직 확정되지 않았지만 현재 문서 작성의 기반이 되는 전제를 적는다.
|
||||
|
||||
- A-1: `ledger_trades.asset_id`가 저장하는 값은 portfolio_id로 일관됨(과거 다른 의미가 섞이지 않음). REQ-029 구현 시점부터 portfolio_id를 저장한 것으로 확인. **사용자 confirm-discovery(2026-06-01)로 동의 확정**.
|
||||
- A-2: 본 워크스페이스는 개발용이며 prod 배포 영향 검증은 release 단계에서 별도 수행. 본 Discovery 범위는 코드/문서/마이그레이션 작성에 한정.
|
||||
- A-3: 3건은 같은 batch로 묶어 처리 가능하다(서로 독립적 변경이라 상호 충돌 없음). **사용자 confirm-discovery(2026-06-01)로 동의 확정**.
|
||||
|
||||
## 오픈 질문
|
||||
답을 아직 모르는 항목을 적는다. 추정으로 덮지 말고 REQ 초안 작성 이전에 확인할 질문으로 남긴다.
|
||||
|
||||
- OQ-1: session `no_expiry` 처리를 (a) 코드 enforcement 추가, (b) REQ-021 본문 갱신("코드 구조상 시도 불가") 중 어느 경로로 갈지.
|
||||
- 상태: Answered (2026-06-01)
|
||||
- 결정: (a) 코드 enforcement. `LoginRequest`에 `model_config = ConfigDict(extra="forbid")` 적용으로 정의되지 않은 필드(`no_expiry` 포함)를 HTTP 422로 거부. 정책 명문(REQ-021) 그대로 유지, 미래 안전망 효과 확보.
|
||||
- 본문 반영: FR-2, # 4 코드/운영 영향, # 7 S-2 측정 방식, # 10 DECIDE 섹션에서 항목 제거
|
||||
- OQ-2: `/health` 응답에 `db_enabled` 추가 방식을 (a) HealthResponse 필드 추가, (b) 별도 `/readiness` endpoint 분리 중 어느 경로로 갈지.
|
||||
- 상태: Answered (2026-06-01)
|
||||
- 결정: (a) HealthResponse 필드 추가. `settings.db_enabled`(환경변수 PORTFOLIOHUB_DB_ENABLED 설정값)를 응답에 노출. **본 값은 설정값이며 actual DB connectivity ping이 아님**을 docs/runtime-flows.md 및 응답 모델 docstring에 명문화한다. 본 워크스페이스가 개발용이므로 Kubernetes-grade readiness 분리는 over-spec. 미래 production monitoring 정착 시 `/readiness` 분리는 별도 Discovery로 분리 가능.
|
||||
- 본문 반영: FR-3, # 4 코드/문서/운영 영향, # 7 S-3 측정 방식, # 10 DECIDE 섹션에서 항목 제거
|
||||
- OQ-3: `ledger_trades.asset_id` rename 마이그레이션을 REL-001 배포 전/후 어디에 넣을지(별도 release 또는 REL-001 후속).
|
||||
- 상태: Answered (2026-06-01)
|
||||
- 결정: 별도 **REL-002**로 분리. REL-001은 현 상태(BAT-001~014 + REQ-028 augmentation, verification 완료, smoke 통과)로 배포 진행. dcy-005 3건은 신규 batch(BAT-015 가정) + REL-002로 분리해 sequence를 명확히 한다.
|
||||
- 사유: REL-001 verification 안정성 최우선, 마이그레이션 sequence 분리, dcy-005가 "REL-001 잔여 정리" 묶음 trigger로 독립 추적 가치. REL-001 verification gap(`/health db_enabled` 미노출 [~] 부분 통과)은 우회 검증으로 blocker 아님.
|
||||
- 본문 반영: # 1 해석, # 3 제약사항, # 10 DECIDE 섹션에서 항목 제거
|
||||
|
||||
### 오픈 질문 처리 기준
|
||||
- `Open`: 아직 답이 없고 다음 단계 진행에 영향이 있는 상태다. 문서에 유지한다.
|
||||
- `Answered`: 답이 확정된 상태다. 관련 본문/요구사항에 반영한 뒤 오픈 질문 목록에서는 제거하거나 resolved 기록으로 이동한다.
|
||||
- `Deferred`: 지금 단계에서 결정하지 않고 다음 단계 또는 후속 이슈로 넘기는 상태다. 넘길 대상과 이유를 반드시 적는다.
|
||||
- `Dropped`: 더 이상 유효하지 않은 질문이다. 삭제 사유를 짧게 남기고 목록에서 제거한다.
|
||||
|
||||
# 7. 초기 성공 기준
|
||||
이 변경이 성공했다고 판단할 기준을 측정 가능하게 정의한다. 가능하면 데이터 출처까지 연결한다.
|
||||
|
||||
- S-1: `ledger_trades` 테이블에 `portfolio_id` 컬럼이 존재하고 `asset_id` 컬럼이 더 이상 사용되지 않는다(코드 grep + DB \d 검증). 기존 INSERT/SELECT 회귀 테스트 모두 통과.
|
||||
- S-2: REQ-021 정책과 실제 코드가 일치한다 — `/auth/login` body에 `no_expiry: true`(또는 LoginRequest에 정의되지 않은 추가 필드)를 포함해 시도 시 HTTP 422 응답 (OQ-1 결정 (a) 경로, 2026-06-01).
|
||||
- S-3: `GET /health` 응답이 `db_enabled` 필드를 직접 노출하고 그 값이 `settings.db_enabled`와 일치한다 (OQ-2 결정 (a) 경로, 2026-06-01). 응답 본문 schema 검증 + grep.
|
||||
|
||||
## 측정 방식 및 데이터 출처
|
||||
각 성공 기준을 어떻게 측정할지와 어떤 데이터나 문서를 근거로 쓸지 적는다.
|
||||
|
||||
- S-1 측정 방식: `grep -rn "ledger_trades\..*asset_id\|.asset_id\b" app/repositories/postgres.py app/domain/models.py app/api/v1_portfolios.py` 결과 0건 + DB `\d ledger_trades`에서 `portfolio_id` 컬럼 확인 / 데이터 출처: 코드 grep + alembic upgrade 후 DB schema
|
||||
- S-2 측정 방식: pytest TestClient `/auth/login` body에 `no_expiry: true` 포함 → HTTP 422 응답 회귀 / 데이터 출처: 신규 pytest 케이스 (예: `tests/test_auth.py::test_login_rejects_no_expiry_extra_field`)
|
||||
- S-3 측정 방식: pytest TestClient `GET /health` 응답 JSON에 `"db_enabled": true` 매치 (PORTFOLIOHUB_DB_ENABLED=true 환경) + 로컬 smoke `curl http://127.0.0.1:18000/health` / 데이터 출처: 신규 pytest 케이스 (예: `tests/test_app.py::test_health_exposes_db_enabled`) + 로컬 smoke
|
||||
|
||||
# 8. REQ로 넘기기 전 확인 체크
|
||||
REQ 초안 작성 단계로 넘기기 전에 무엇이 확정되어야 하는지 확인한다. 미완료 항목은 후속 결정이 필요함을 뜻한다.
|
||||
|
||||
- [x] 범위(In Scope/Out of Scope) 확정
|
||||
- 기준: OQ-1/OQ-2/OQ-3 모두 2026-06-01 Answered. 범위 + 배포 시점(REL-002 분리) 확정.
|
||||
- [x] 성공 지표의 측정 방식/데이터 출처 확정
|
||||
- 기준: S-1(코드 grep + DB \d), S-2(pytest 422 회귀), S-3(pytest TestClient + 로컬 smoke) 모두 확정.
|
||||
- [x] High 영향 리스크 우선순위 및 해소 책임자 지정
|
||||
- 기준: 본 Discovery에는 High 리스크 없음 (R-1/R-2/R-3 모두 Low) — 해소 책임자 지정 불요.
|
||||
- [x] 모든 오픈 질문(OQ) `Open` 상태 없음 확인
|
||||
- 기준: OQ-1/OQ-2/OQ-3 모두 Answered (2026-06-01). Open 항목 0개.
|
||||
- [x] Freeze 확정 담당자(CONFIRMED_BY) 지정
|
||||
- 기준: CONFIRMED_BY = "Justin Ko" (confirm-discovery 2026-06-01).
|
||||
|
||||
# 9. 파일 처리 결과
|
||||
이 Discovery 문서 생성/갱신 결과와 참조 문서를 기록한다. 이후 추적과 리뷰 시 근거로 사용한다.
|
||||
|
||||
- 처리 결과: 생성
|
||||
- 생성 경로: docs/discovery/dcy-005_20260601_rel-001-feedback-cleanup.md
|
||||
- 참조 문서:
|
||||
- [docs/releases/rel-001_20260521_v1-api-full-surface.md](../releases/rel-001_20260521_v1-api-full-surface.md) Feedback Handoff Discovery Input 항목
|
||||
- [docs/srs/Documentation/req-021_token-expiry-and-rotation-policy.md](../srs/Documentation/req-021_token-expiry-and-rotation-policy.md) (REQ-021 정책 본문)
|
||||
- [docs/srs/Interface/req-029_v1-portfolios-ledger-trades-migration.md](../srs/Interface/req-029_v1-portfolios-ledger-trades-migration.md) Notes (ledger_trades 컬럼명 follow-up 출처)
|
||||
- [app/api/health.py:17-32](../../app/api/health.py#L17-L32) HealthResponse 모델/handler
|
||||
- [app/repositories/postgres.py:1284-1294](../../app/repositories/postgres.py#L1284-L1294) ledger_trades INSERT
|
||||
- [app/services/auth_service.py:47-65](../../app/services/auth_service.py#L47-L65) AuthService.login
|
||||
|
||||
# 10. 사용자 결정 필요 항목 요약
|
||||
사용자나 승인자가 결정해야 하는 항목만 모아 정리한다. 결정, 확인, 데이터 요청을 구분해서 적는다.
|
||||
|
||||
## DECIDE
|
||||
- (none — OQ-3 결정 완료, DECIDE 섹션 비움)
|
||||
|
||||
## CONFIRM
|
||||
- (none — 두 항목 모두 사용자 confirm-discovery(2026-06-01)로 가정 A-1/A-3에 대한 동의 확정. 본문 가정 섹션에 반영됨.)
|
||||
|
||||
## DATA
|
||||
- (none — 본 Discovery는 본 repo 내부 정보만으로 자립)
|
||||
|
||||
# 11. Discovery Freeze
|
||||
REQ 초안 작성 인계 시점의 확정 플래그와 섹션 참조만 기록한다. 내용 재작성 없이 원본 섹션을 링크로 대신한다.
|
||||
|
||||
## 섹션 참조
|
||||
- 범위: [# 5. 요구사항 목록 > 범위 경계](#5-요구사항-목록)
|
||||
- 리스크/오픈 질문: [# 6. 리스크/가정 목록](#6-리스크가정-목록)
|
||||
- 성공 기준: [# 7. 초기 성공 기준](#7-초기-성공-기준)
|
||||
- 인계 전 확인: [# 8. REQ로 넘기기 전 확인 체크](#8-req로-넘기기-전-확인-체크)
|
||||
- 사용자 결정 필요: [# 10. 사용자 결정 필요 항목 요약](#10-사용자-결정-필요-항목-요약)
|
||||
|
||||
## Freeze 플래그
|
||||
- Handoff Decision: 승인
|
||||
- Handoff Rationale: OQ-1/OQ-2/OQ-3 모두 Answered, CONFIRM 2건은 사용자 confirm-discovery(2026-06-01)로 가정 A-1/A-3 동의 확정. # 8 체크리스트 5건 모두 [x]. REQ Drafting 진입 준비 완료.
|
||||
- Ready for REQ Drafting: true
|
||||
- Confirmed By: Justin
|
||||
- Confirmed At (KST): 2026-06-01 18:30
|
||||
|
||||
# 12. 작성 가이드
|
||||
- 이 파일은 dcy-005 작성 완료본이다. confirm-discovery 진입 전까지 OQ 결정 + 본문 반영 필요.
|
||||
- 3건이 서로 독립이므로 OQ 결정도 항목별로 진행 가능. 결정이 끝나는 항목부터 본문(FR-2/FR-3)을 (a)/(b) 선택안으로 압축한다.
|
||||
- 본 Discovery는 코드/문서/마이그레이션 모두 영향이 있는 표준 SDLC 트랙. minor-change fast path는 3건 묶음 특성상 부적합 — 표준 batch로 진행 권장.
|
||||
@@ -10,3 +10,4 @@
|
||||
| dcy-002_20260519_tag-ownership-and-applicability-policy | 20260519 | 태그 소유권 및 적용 대상 정책 — admin/user 구분 태그 모델과 asset·portfolio 적용 정책 도입 | [Discovery](./dcy-002_20260519_tag-ownership-and-applicability-policy.md) | confirmed |
|
||||
| dcy-003_20260520_user-portfolio-mutation-v1-api-migration | 20260520 | User Portfolio Mutation v1 API 이전 및 path 명명 분리 (/v1/portfolios* 신설) | [Discovery](./dcy-003_20260520_user-portfolio-mutation-v1-api-migration.md) | confirmed |
|
||||
| dcy-004_20260520_token-issuance-and-lifecycle-overhaul | 20260520 | 토큰 발급 및 관리 전면 정비 — client token 폐지, service-token rotate 정상화, 발급 권한 매트릭스, 만료·회전·자동 revoke 정책 | [Discovery](./dcy-004_20260520_token-issuance-and-lifecycle-overhaul.md) | confirmed |
|
||||
| dcy-005_20260601_rel-001-feedback-cleanup | 20260601 | REL-001 Feedback 정합화 — ledger_trades.asset_id 명명 / session no_expiry enforcement / health db_enabled | [Discovery](./dcy-005_20260601_rel-001-feedback-cleanup.md) | confirmed |
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
|
||||
- JSON response models :: responses are shaped by app/schemas/auth.py, app/schemas/user.py, and app/schemas/portfolio.py
|
||||
- HTTP 201, 200, and 204 status codes :: create, read or update, and delete semantics respectively
|
||||
- GET /health payload :: returns `{"status": "ok", "service": "portfolio-hub", "version": "<semver>", "checked_at": "<ISO8601 KST>"}` for readiness checks
|
||||
- GET /health payload :: returns `{"status": "ok", "service": "portfolio-hub", "version": "<semver>", "checked_at": "<ISO8601 KST>", "db_enabled": <bool>}` for readiness checks. `db_enabled` exposes the `PORTFOLIOHUB_DB_ENABLED` setting value (not an actual DB connectivity ping); see `docs/runtime-flows.md` for the readiness semantics (REQ-035, dcy-005).
|
||||
|
||||
### Error Contract
|
||||
|
||||
@@ -379,11 +379,12 @@
|
||||
- Response: HTTP 204 (no content)
|
||||
- Error: HTTP 403 if service/client or ownership mismatch; HTTP 404 if portfolio or comment not found; HTTP 409 if disabled portfolio
|
||||
|
||||
### Endpoint: `POST /v1/portfolios/{portfolio_id}/ledger/trades` (REQ-029, BAT-011)
|
||||
### Endpoint: `POST /v1/portfolios/{portfolio_id}/ledger/trades` (REQ-029, BAT-011; REQ-033 column rename BAT-015)
|
||||
|
||||
- Auth: DB service token with `trade-write` permission; `daemon_id IS NULL` → HTTP 403 fail-closed
|
||||
- Request: `LedgerTradeRequest` — `order_id`, `execution_id`, `side` (buy|sell), `quantity`, `price`, `fee_amount`, `tax_amount`, `executed_at`
|
||||
- Response: HTTP 201 + `LedgerTradeResponse` with `status="new"`; HTTP 200 + `status="duplicate"` for idempotent replay; HTTP 409 for conflicting payload
|
||||
- `LedgerTradeResponse` fields: `ledger_entry_id`, `portfolio_id` (renamed from `asset_id` by REQ-033 / dcy-005 — schema and DB column now match in meaning), `order_id`, `execution_id`, `side`, `quantity`, `price`, `fee_amount`, `tax_amount`, `executed_at`, `recorded_at`, `status`
|
||||
- Error: HTTP 403 (no daemon_id on token, portfolio unassigned, daemon mismatch); HTTP 404 (non-existent portfolio); HTTP 409 (disabled portfolio or payload conflict)
|
||||
- Idempotency key: `(portfolio_id, order_id, execution_id)` — reuses `uq_ledger_trades_idempotency` constraint
|
||||
- **구 path `POST /v1/assets/{asset_id}/ledger/trades`**: BAT-011에서 제거됨. 호출 시 HTTP 404.
|
||||
|
||||
@@ -94,6 +94,7 @@
|
||||
- Permission levels read, trade-write, and admin must map consistently across API guards, issued tokens, and CLI mutations.
|
||||
- Request validation and auth resolution should happen before persistence mutation or response rendering.
|
||||
- The health endpoint remains unauthenticated so infrastructure can probe readiness without a token.
|
||||
- `GET /health` response includes `db_enabled: bool` (REQ-035, dcy-005). 본 필드는 환경변수 `PORTFOLIOHUB_DB_ENABLED` 설정값을 그대로 노출하는 것이며 actual DB connectivity ping이 아니다. production-grade readiness probe (SELECT 1 ping 등)는 본 endpoint 범위 밖이며 별도 `/readiness` endpoint 분리는 후속 Discovery에서 다룬다.
|
||||
|
||||
## Flow: `v1 API — Tag Management (POST/GET/PATCH /v1/tags)`
|
||||
|
||||
|
||||
@@ -0,0 +1,126 @@
|
||||
# REQ-034: LoginRequest 추가 필드 거부 enforcement (session no_expiry 정책 정합)
|
||||
|
||||
- Status: Implemented
|
||||
- Type: Interface
|
||||
- Priority: Medium
|
||||
- Owner: Justin Ko
|
||||
|
||||
## Intent
|
||||
- REQ-021 정책 본문이 "session에서 `no_expiry` 시도 → HTTP 422 또는 ValueError 거부"로 명시하지만, 현재 `LoginRequest` 스키마([app/schemas/auth.py:13-23](../../app/schemas/auth.py#L13-L23))가 Pydantic 기본(`extra="ignore"`)이라 외부에서 `no_expiry: true`를 추가해 보내도 silently 무시된다.
|
||||
- `LoginRequest`에 `extra="forbid"` 적용으로 정책 명문과 코드 동작을 일치시켜 audit 명확성을 확보한다.
|
||||
|
||||
## Requirement
|
||||
- `app/schemas/auth.py:LoginRequest`에 `model_config = ConfigDict(extra="forbid")`를 적용한다.
|
||||
- 외부 client가 LoginRequest에 정의되지 않은 필드(`no_expiry` 포함, 기타 임의 필드)를 전송하면 FastAPI/Pydantic이 HTTP 422로 거부한다.
|
||||
- `AuthService.login`([app/services/auth_service.py:47-65](../../app/services/auth_service.py#L47-L65))의 동작은 변경하지 않는다(정책 enforcement는 스키마 단계에서 처리).
|
||||
- 기존 정상 client(username/password 2필드만 전송)는 영향 없음을 회귀 테스트로 확인한다.
|
||||
|
||||
## Acceptance Criteria
|
||||
1. Given valid username/password, When `POST /auth/login` body `{"username": "...", "password": "..."}` 호출, Then HTTP 200 + 정상 TokenResponse 반환(기존 동작 회귀).
|
||||
2. Given valid username/password + 추가 필드, When `POST /auth/login` body `{"username": "...", "password": "...", "no_expiry": true}` 호출, Then HTTP 422 응답 + Pydantic validation error detail에 `extra_forbidden` 표기.
|
||||
3. Given valid username/password + 임의 추가 필드, When `POST /auth/login` body `{"username": "...", "password": "...", "arbitrary_field": "x"}` 호출, Then HTTP 422 응답 (extra="forbid" 일반 동작 회귀).
|
||||
4. Given codebase grep `grep -n "extra.*forbid" app/schemas/auth.py`, When 본 REQ 구현 후, Then `LoginRequest` 정의 부근에 매치된다.
|
||||
5. Given 본 REQ 구현 후 전체 pytest 회귀, When 실행, Then 기존 통과 케이스 모두 유지.
|
||||
|
||||
## Impacted Area
|
||||
- Docs:
|
||||
- (선택) `docs/srs/Documentation/req-021_token-expiry-and-rotation-policy.md` Notes에 "AC-4 enforcement는 REQ-034로 분리 구현" 추가 (cross-reference)
|
||||
- Modules:
|
||||
- `app/schemas/auth.py` — `LoginRequest.model_config = ConfigDict(extra="forbid")` 추가
|
||||
- Tests:
|
||||
- 신규 `tests/test_auth.py::test_login_rejects_no_expiry_extra_field` — AC-2 검증
|
||||
- 신규 `tests/test_auth.py::test_login_rejects_arbitrary_extra_field` — AC-3 검증 (또는 AC-2와 단일 케이스로 합침)
|
||||
|
||||
## Notes
|
||||
- 본 REQ는 dcy-005 FR-2 산출물. REQ-021 follow-up.
|
||||
- `extra="forbid"`는 `LoginRequest` 전체에 추가 필드 거부 효과. 기존 정상 client는 username/password만 전송하므로 영향 없음. 본 워크스페이스는 개발용이라 외부 monitoring 영향 무.
|
||||
- 본 REQ는 별도 REL-002로 분리 배포(dcy-005 OQ-3 결정, 2026-06-01).
|
||||
- Source: dcy-005 FR-2, REQ-021 AC-5 follow-up.
|
||||
|
||||
## Change Log
|
||||
|
||||
### CHG-20260601-03
|
||||
- Date: 2026-06-01
|
||||
- Author: Justin Ko
|
||||
|
||||
#### Change Summary
|
||||
- Status: Approved → Implemented
|
||||
|
||||
#### Intent
|
||||
- BAT-015 confirm-batch-verification 완료(2026-06-05). AC-1~5 모두 PASSED. `LoginRequest` extra="forbid" 적용 + pytest 422 회귀 2건 신규 + 로컬 smoke 422 + extra_forbidden 확인.
|
||||
|
||||
#### Acceptance Criteria Delta
|
||||
- No change
|
||||
|
||||
#### Impacted Area Delta
|
||||
- No change
|
||||
|
||||
#### Delivery Trace Delta
|
||||
- Batches:
|
||||
- BAT-015 (release-candidate)
|
||||
- Releases:
|
||||
- REL-002 (예정)
|
||||
|
||||
#### Revalidation Impact
|
||||
- Reverification Needed: no
|
||||
- Existing Implementation Invalidated: no
|
||||
- Status Recommendation: implemented
|
||||
- Follow-up Action: none
|
||||
|
||||
### CHG-20260601-02
|
||||
- Date: 2026-06-01
|
||||
- Author: Justin Ko
|
||||
|
||||
#### Change Summary
|
||||
- Status: Proposed → Approved
|
||||
|
||||
#### Intent
|
||||
- 승인 게이트 통과: 필수 필드(Status/Type/Priority/Owner/Intent/Requirement/AC/Impacted Area/Change Log) 모두 충족, AC-1~5 구체성 확인 완료, blocker 없음.
|
||||
|
||||
#### Acceptance Criteria Delta
|
||||
- No change
|
||||
|
||||
#### Impacted Area Delta
|
||||
- No change
|
||||
|
||||
#### Delivery Trace Delta
|
||||
- Batches: none (미정)
|
||||
- Releases: none (미정 — REL-002 분리 예정)
|
||||
|
||||
#### Revalidation Impact
|
||||
- Reverification Needed: no
|
||||
- Existing Implementation Invalidated: no
|
||||
- Status Recommendation: promote-to-approved
|
||||
- Follow-up Action: batch 계획 편입 (BAT-015 후보)
|
||||
|
||||
### CHG-20260601-01
|
||||
- Date: 2026-06-01
|
||||
- Author: Justin Ko
|
||||
|
||||
#### Change Summary
|
||||
- 최초 생성 — dcy-005 confirm-discovery 후 draft-req 실행. dcy-005 FR-2 산출물.
|
||||
|
||||
#### Intent
|
||||
- REQ-021 정책 본문과 LoginRequest 코드 동작 일치 (session no_expiry 거부 enforcement).
|
||||
|
||||
#### Acceptance Criteria Delta
|
||||
- Added:
|
||||
- AC-1~5 최초 작성
|
||||
|
||||
#### Impacted Area Delta
|
||||
- Docs: (선택) docs/srs/Documentation/req-021_*.md cross-reference — 최초 지정
|
||||
- Modules: app/schemas/auth.py — 최초 지정
|
||||
- Tests: tests/test_auth.py — 최초 지정
|
||||
|
||||
#### Delivery Trace Delta
|
||||
- Batches: none (미정)
|
||||
- Releases: none (미정 — REL-002 분리 예정)
|
||||
|
||||
#### Revalidation Impact
|
||||
- Reverification Needed: no
|
||||
- Existing Implementation Invalidated: no
|
||||
- Status Recommendation: keep-current
|
||||
- Follow-up Action: confirm-req 후 batch 계획 편입
|
||||
|
||||
#### Validation Plan
|
||||
- pytest TestClient로 AC-1~3 검증; grep으로 AC-4 검증
|
||||
@@ -0,0 +1,128 @@
|
||||
# REQ-035: GET /health 응답에 db_enabled 필드 추가
|
||||
|
||||
- Status: Implemented
|
||||
- Type: Interface
|
||||
- Priority: Medium
|
||||
- Owner: Justin Ko
|
||||
|
||||
## Intent
|
||||
- 현재 `HealthResponse` 모델([app/api/health.py:17-21](../../app/api/health.py#L17-L21))이 `status`, `service`, `version`, `checked_at` 4필드만 노출하여 외부 health probe가 DB 사용 설정 상태를 직접 관찰할 수 없다.
|
||||
- REL-001 verification 체크리스트의 "GET /health → HTTP 200 + `db_enabled: true`" 요구사항과 응답 모델 gap을 닫는다.
|
||||
- 본 필드는 환경변수 `PORTFOLIOHUB_DB_ENABLED` 설정값을 노출하는 것이며 actual DB connectivity ping이 아님을 baseline 문서(`docs/runtime-flows.md`)에 명문화한다.
|
||||
|
||||
## Requirement
|
||||
- `HealthResponse` 모델에 `db_enabled: bool` 필드를 추가한다.
|
||||
- `healthcheck()` 핸들러가 `settings.db_enabled`(환경변수 `PORTFOLIOHUB_DB_ENABLED` 설정값)를 `db_enabled` 필드에 채워서 응답한다.
|
||||
- `docs/runtime-flows.md`에 "GET /health 응답의 `db_enabled`는 환경변수 설정값 노출이며 actual DB connectivity ping이 아니다. production-grade readiness probe는 별도 `/readiness` endpoint 분리로 후속 처리한다"는 의미 명문화 노트를 추가한다.
|
||||
- `docs/interface-contract.md`에 /health 응답 schema의 `db_enabled` 필드 명세를 추가한다.
|
||||
|
||||
## Acceptance Criteria
|
||||
1. Given PORTFOLIOHUB_DB_ENABLED=true 환경, When `GET /health` 호출, Then HTTP 200 응답 본문에 `"db_enabled": true` 필드가 포함된다.
|
||||
2. Given PORTFOLIOHUB_DB_ENABLED=false 환경, When `GET /health` 호출, Then HTTP 200 응답 본문에 `"db_enabled": false` 필드가 포함된다.
|
||||
3. Given 응답 본문, When `status`, `service`, `version`, `checked_at`, `db_enabled` 5필드 확인, Then 모두 노출되고 기존 4필드 동작은 회귀 통과한다.
|
||||
4. Given grep `grep -n "db_enabled" docs/runtime-flows.md`, When 실행, Then "설정값 노출, actual ping 아님" 의미 노트가 매치된다.
|
||||
5. Given grep `grep -n "/health.*db_enabled\|db_enabled.*health" docs/interface-contract.md`, When 실행, Then /health 응답 schema에 `db_enabled` 필드 명세가 매치된다.
|
||||
|
||||
## Impacted Area
|
||||
- Docs:
|
||||
- `docs/runtime-flows.md` — /health probe flow 섹션에 "db_enabled는 설정값 노출, actual ping 아님" 의미 노트 추가 (baseline 문서 갱신)
|
||||
- `docs/interface-contract.md` — /health 응답 schema에 `db_enabled: bool` 필드 추가
|
||||
- Modules:
|
||||
- `app/api/health.py` — `HealthResponse`에 `db_enabled: bool` 필드 추가 + `healthcheck()` 핸들러가 `settings.db_enabled` 채움
|
||||
- Tests:
|
||||
- 신규 `tests/test_app.py::test_health_exposes_db_enabled` — AC-1 검증 (PORTFOLIOHUB_DB_ENABLED=true 환경)
|
||||
- (선택) 추가 케이스 — AC-2 검증 (PORTFOLIOHUB_DB_ENABLED=false 환경)
|
||||
|
||||
## Notes
|
||||
- 본 REQ는 dcy-005 FR-3 산출물. REL-001 verification gap을 닫는 정합화.
|
||||
- `db_enabled` 값은 단순 설정값 노출이며 actual DB connectivity 보장이 아니다. production-grade readiness probe(SELECT 1 ping 등)는 본 REQ 범위 밖이며 별도 후속 Discovery + `/readiness` endpoint 분리로 처리.
|
||||
- 본 REQ는 별도 REL-002로 분리 배포(dcy-005 OQ-3 결정, 2026-06-01). REL-001은 현 상태(체크리스트 `db_enabled` 미노출 [~] 부분 통과)로 배포 진행.
|
||||
- Source: dcy-005 FR-3, rel-001 Feedback Handoff 후보.
|
||||
|
||||
## Change Log
|
||||
|
||||
### CHG-20260601-03
|
||||
- Date: 2026-06-01
|
||||
- Author: Justin Ko
|
||||
|
||||
#### Change Summary
|
||||
- Status: Approved → Implemented
|
||||
|
||||
#### Intent
|
||||
- BAT-015 confirm-batch-verification 완료(2026-06-05). AC-1~5 모두 PASSED. HealthResponse 확장 + handler + pytest 3건(test_healthcheck 갱신 + 신규 2건) + baseline 문서(`docs/runtime-flows.md`) 명문화 + interface-contract.md 정합 모두 evidence 확보.
|
||||
|
||||
#### Acceptance Criteria Delta
|
||||
- No change
|
||||
|
||||
#### Impacted Area Delta
|
||||
- No change
|
||||
|
||||
#### Delivery Trace Delta
|
||||
- Batches:
|
||||
- BAT-015 (release-candidate)
|
||||
- Releases:
|
||||
- REL-002 (예정)
|
||||
|
||||
#### Revalidation Impact
|
||||
- Reverification Needed: no
|
||||
- Existing Implementation Invalidated: no
|
||||
- Status Recommendation: implemented
|
||||
- Follow-up Action: 미래 production-grade readiness probe(`/readiness` endpoint 분리)는 본 REQ 범위 밖, 별도 후속 Discovery 후보.
|
||||
|
||||
### CHG-20260601-02
|
||||
- Date: 2026-06-01
|
||||
- Author: Justin Ko
|
||||
|
||||
#### Change Summary
|
||||
- Status: Proposed → Approved
|
||||
|
||||
#### Intent
|
||||
- 승인 게이트 통과: 필수 필드(Status/Type/Priority/Owner/Intent/Requirement/AC/Impacted Area/Change Log) 모두 충족, AC-1~5 구체성 확인 완료, blocker 없음. baseline 문서(docs/runtime-flows.md) 갱신 요구가 AC-4에 반영됨.
|
||||
|
||||
#### Acceptance Criteria Delta
|
||||
- No change
|
||||
|
||||
#### Impacted Area Delta
|
||||
- No change
|
||||
|
||||
#### Delivery Trace Delta
|
||||
- Batches: none (미정)
|
||||
- Releases: none (미정 — REL-002 분리 예정)
|
||||
|
||||
#### Revalidation Impact
|
||||
- Reverification Needed: no
|
||||
- Existing Implementation Invalidated: no
|
||||
- Status Recommendation: promote-to-approved
|
||||
- Follow-up Action: batch 계획 편입 (BAT-015 후보)
|
||||
|
||||
### CHG-20260601-01
|
||||
- Date: 2026-06-01
|
||||
- Author: Justin Ko
|
||||
|
||||
#### Change Summary
|
||||
- 최초 생성 — dcy-005 confirm-discovery 후 draft-req 실행. dcy-005 FR-3 산출물.
|
||||
|
||||
#### Intent
|
||||
- REL-001 verification checklist의 `/health db_enabled` gap을 닫고 응답 모델 확장 + baseline 문서(runtime-flows.md) 명문화.
|
||||
|
||||
#### Acceptance Criteria Delta
|
||||
- Added:
|
||||
- AC-1~5 최초 작성
|
||||
|
||||
#### Impacted Area Delta
|
||||
- Docs: docs/runtime-flows.md (baseline), docs/interface-contract.md — 최초 지정
|
||||
- Modules: app/api/health.py — 최초 지정
|
||||
- Tests: tests/test_app.py — 최초 지정
|
||||
|
||||
#### Delivery Trace Delta
|
||||
- Batches: none (미정)
|
||||
- Releases: none (미정 — REL-002 분리 예정)
|
||||
|
||||
#### Revalidation Impact
|
||||
- Reverification Needed: no
|
||||
- Existing Implementation Invalidated: no
|
||||
- Status Recommendation: keep-current
|
||||
- Follow-up Action: confirm-req 후 batch 계획 편입
|
||||
|
||||
#### Validation Plan
|
||||
- pytest TestClient로 AC-1~3 검증; grep으로 AC-4/AC-5 baseline 문서 명문화 검증
|
||||
@@ -0,0 +1,132 @@
|
||||
# REQ-033: ledger_trades.asset_id → portfolio_id 컬럼명 정합화
|
||||
|
||||
- Status: Implemented
|
||||
- Type: Migration
|
||||
- Priority: Medium
|
||||
- Owner: Justin Ko
|
||||
|
||||
## Intent
|
||||
- `ledger_trades` 테이블의 `asset_id` 컬럼이 실제로는 portfolio_id를 저장하지만 컬럼명이 의미와 불일치하여 schema reader가 assets 테이블 참조로 오해할 수 있다.
|
||||
- REQ-029 구현(POST /v1/portfolios/{id}/ledger/trades 이전) 시점의 잔여 명명 정합화를 마무리한다.
|
||||
|
||||
## Requirement
|
||||
- Alembic 신규 revision을 작성해 `ledger_trades.asset_id` 컬럼을 `portfolio_id`로 rename한다(`ALTER TABLE ledger_trades RENAME COLUMN asset_id TO portfolio_id`).
|
||||
- 기존 데이터는 동일 의미(portfolio_id 저장)이므로 backfill 불요.
|
||||
- `app/repositories/postgres.py`의 ledger_trades INSERT/SELECT/RETURNING SQL에서 컬럼명을 `portfolio_id`로 갱신한다.
|
||||
- `app/domain/models.py`의 `LedgerTrade` dataclass에 `asset_id` 필드가 있다면 `portfolio_id`로 rename한다(있는 경우).
|
||||
- `app/api/v1_portfolios.py` ledger route handler에서 응답에 `asset_id` 노출이 있다면 `portfolio_id`로 정합화한다(있는 경우).
|
||||
- 마이그레이션 lock 시간을 운영 영향 < 1초로 유지하기 위해 짧은 단일 `ALTER COLUMN RENAME` 트랜잭션으로 처리한다.
|
||||
|
||||
## Acceptance Criteria
|
||||
1. Given 신규 Alembic revision 적용 후, When `\d ledger_trades` 실행, Then `portfolio_id` 컬럼이 존재하고 `asset_id` 컬럼은 존재하지 않는다.
|
||||
2. Given AC-1 환경, When 기존 `POST /v1/portfolios/{id}/ledger/trades` 호출, Then INSERT/SELECT 회귀 테스트가 모두 통과한다.
|
||||
3. Given codebase grep `grep -rn "ledger_trades\..*asset_id\|asset_id.*ledger_trades" app/repositories/postgres.py app/domain/models.py app/api/v1_portfolios.py`, When 본 REQ 구현 후, Then 결과는 0건이다.
|
||||
4. Given 마이그레이션 staging dry-run, When `ALTER COLUMN RENAME` 실행 시간 측정, Then 측정 시간 < 1초 (NFR-1 정합).
|
||||
5. Given 본 REQ 구현 후 전체 pytest 회귀, When 실행, Then 기존 통과 케이스 모두 유지 + ledger_trades 의존 케이스 통과.
|
||||
|
||||
## Impacted Area
|
||||
- Docs:
|
||||
- `docs/data-model.md` — ledger_trades 컬럼 명세에 `portfolio_id` 반영, `asset_id` 표기 제거
|
||||
- (선택) `docs/interface-contract.md` — ledger 응답에 `asset_id` 노출이 있다면 `portfolio_id`로 갱신
|
||||
- Modules:
|
||||
- `migrations/versions/` — 신규 revision (예: `20260601_NNNN_ledger_trades_portfolio_id_rename.py`)
|
||||
- `app/repositories/postgres.py` — ledger_trades INSERT/SELECT/RETURNING 컬럼명 갱신([app/repositories/postgres.py:1284-1314](../../app/repositories/postgres.py#L1284-L1314))
|
||||
- `app/domain/models.py` — `LedgerTrade` dataclass 필드명 (있다면)
|
||||
- `app/api/v1_portfolios.py` — ledger route handler 응답 매핑 (있다면)
|
||||
- Tests:
|
||||
- 갱신 `tests/test_v1_portfolios_ledger.py` — 컬럼명 변경 회귀 검증
|
||||
- (선택) 신규 마이그레이션 dry-run 회귀
|
||||
|
||||
## Notes
|
||||
- 본 REQ는 dcy-005 FR-1 산출물. 정합화 묶음 trigger.
|
||||
- 외부 client(trex-basic 등)는 HTTP API(`POST /v1/portfolios/{id}/ledger/trades`)만 호출하므로 DB 컬럼명 의존 없음 추정. 마이그레이션 전 외부 query 의존성 확인.
|
||||
- 본 REQ는 별도 REL-002로 분리 배포(dcy-005 OQ-3 결정, 2026-06-01).
|
||||
- Source: dcy-005 FR-1, REQ-029 Notes follow-up.
|
||||
|
||||
## Change Log
|
||||
|
||||
### CHG-20260601-03
|
||||
- Date: 2026-06-01
|
||||
- Author: Justin Ko
|
||||
|
||||
#### Change Summary
|
||||
- Status: Approved → Implemented
|
||||
|
||||
#### Intent
|
||||
- BAT-015 confirm-batch-verification 완료(2026-06-05). AC-2/3/5 PASSED, AC-1/4 staging Deferred(blocker 아님). 마이그레이션 + 코드/스키마/도메인/라우트/테스트/문서 정합화 모두 evidence 확보.
|
||||
|
||||
#### Acceptance Criteria Delta
|
||||
- No change
|
||||
|
||||
#### Impacted Area Delta
|
||||
- No change
|
||||
|
||||
#### Delivery Trace Delta
|
||||
- Batches:
|
||||
- BAT-015 (release-candidate)
|
||||
- Releases:
|
||||
- REL-002 (예정)
|
||||
|
||||
#### Revalidation Impact
|
||||
- Reverification Needed: no
|
||||
- Existing Implementation Invalidated: no
|
||||
- Status Recommendation: implemented
|
||||
- Follow-up Action: REL-002 staging 시점에 AC-1 (`\d ledger_trades` 컬럼 검증) + AC-4 (마이그레이션 lock < 1초) evidence 수집. 본 워크스페이스 ledger_trades 테이블 미생성 상태 자체는 별도 후속 항목(BAT-011 mock verification 후속) 등록 후보.
|
||||
|
||||
### CHG-20260601-02
|
||||
- Date: 2026-06-01
|
||||
- Author: Justin Ko
|
||||
|
||||
#### Change Summary
|
||||
- Status: Proposed → Approved
|
||||
|
||||
#### Intent
|
||||
- 승인 게이트 통과: 필수 필드(Status/Type/Priority/Owner/Intent/Requirement/AC/Impacted Area/Change Log) 모두 충족, AC-1~5 구체성 확인 완료, blocker 없음.
|
||||
|
||||
#### Acceptance Criteria Delta
|
||||
- No change
|
||||
|
||||
#### Impacted Area Delta
|
||||
- No change
|
||||
|
||||
#### Delivery Trace Delta
|
||||
- Batches: none (미정)
|
||||
- Releases: none (미정 — REL-002 분리 예정)
|
||||
|
||||
#### Revalidation Impact
|
||||
- Reverification Needed: no
|
||||
- Existing Implementation Invalidated: no
|
||||
- Status Recommendation: promote-to-approved
|
||||
- Follow-up Action: batch 계획 편입 (BAT-015 후보)
|
||||
|
||||
### CHG-20260601-01
|
||||
- Date: 2026-06-01
|
||||
- Author: Justin Ko
|
||||
|
||||
#### Change Summary
|
||||
- 최초 생성 — dcy-005 confirm-discovery 후 draft-req 실행. dcy-005 FR-1 산출물.
|
||||
|
||||
#### Intent
|
||||
- ledger_trades.asset_id 컬럼명을 의미에 맞게 portfolio_id로 정합화.
|
||||
|
||||
#### Acceptance Criteria Delta
|
||||
- Added:
|
||||
- AC-1~5 최초 작성
|
||||
|
||||
#### Impacted Area Delta
|
||||
- Docs: docs/data-model.md, (선택) docs/interface-contract.md — 최초 지정
|
||||
- Modules: migrations/versions/, app/repositories/postgres.py, app/domain/models.py, app/api/v1_portfolios.py — 최초 지정
|
||||
- Tests: tests/test_v1_portfolios_ledger.py — 최초 지정
|
||||
|
||||
#### Delivery Trace Delta
|
||||
- Batches: none (미정)
|
||||
- Releases: none (미정 — REL-002 분리 예정)
|
||||
|
||||
#### Revalidation Impact
|
||||
- Reverification Needed: no
|
||||
- Existing Implementation Invalidated: no
|
||||
- Status Recommendation: keep-current
|
||||
- Follow-up Action: confirm-req 후 batch 계획 편입; staging 환경에서 ALTER COLUMN RENAME dry-run으로 lock 시간 측정
|
||||
|
||||
#### Validation Plan
|
||||
- pytest로 AC-1~5 검증 + staging dry-run AC-4 측정
|
||||
+7
-4
@@ -74,7 +74,7 @@
|
||||
|
||||
## Next Requirement ID
|
||||
|
||||
- Current: `REQ-033`
|
||||
- Current: `REQ-036`
|
||||
- Rule: `Requirement Register`에 있는 가장 큰 REQ 번호보다 정확히 1 커야 한다.
|
||||
|
||||
## Requirement Register
|
||||
@@ -113,11 +113,14 @@
|
||||
| REQ-030 | portfolio tag 적용/해제/조회 v1 라우트 | Interface | Implemented | Medium | Justin Ko | [req-030](./Interface/req-030_v1-portfolios-tags-crud.md) |
|
||||
| REQ-031 | portfolio comment CRUD v1 라우트 | Interface | Implemented | Medium | Justin Ko | [req-031](./Interface/req-031_v1-portfolios-comments-crud.md) |
|
||||
| REQ-032 | portfolio v1 baseline 문서 갱신 | Documentation | Implemented | Medium | Justin Ko | [req-032](./Documentation/req-032_portfolio-v1-baseline-docs-update.md) |
|
||||
| REQ-033 | ledger_trades.asset_id → portfolio_id 컬럼명 정합화 | Migration | Implemented | Medium | Justin Ko | [req-033](./Migration/req-033_ledger-trades-asset-id-to-portfolio-id-rename.md) |
|
||||
| REQ-034 | LoginRequest 추가 필드 거부 enforcement | Interface | Implemented | Medium | Justin Ko | [req-034](./Interface/req-034_login-request-extra-forbid-enforcement.md) |
|
||||
| REQ-035 | GET /health 응답에 db_enabled 필드 추가 | Interface | Implemented | Medium | Justin Ko | [req-035](./Interface/req-035_health-response-db-enabled-field.md) |
|
||||
|
||||
## Recent Change Log Summary
|
||||
|
||||
- 2026-06-05: REQ-033/034/035 Approved → Implemented (CHG-20260601-03 각각) — BAT-015 confirm-batch-verification 완료(2026-06-05). 전체 372 passed + 로컬 smoke 5건 통과. REQ-033 AC-1/AC-4 실 DB evidence는 REL-002 staging 이연(blocker 아님). BAT-015 status: release-candidate.
|
||||
- 2026-06-01: REQ-033/034/035 Proposed → Approved (CHG-20260601-02 각각) — confirm-req 무인자 일괄 모드 실행, 승인 게이트(필수 필드/AC 구체성/Change Log) 모두 충족, blocker 없음. dcy-005 산출물 3건 모두 Approved.
|
||||
- 2026-06-01: REQ-033/034/035 신규 (Proposed) — dcy-005(rel-001-feedback-cleanup) draft-req 산출물. REQ-033 ledger_trades.asset_id → portfolio_id 컬럼명 정합화 (Migration), REQ-034 LoginRequest extra="forbid" enforcement (Interface, REQ-021 follow-up), REQ-035 HealthResponse db_enabled 필드 추가 + docs/runtime-flows.md baseline 갱신 (Interface). 3건 모두 REL-002 분리 배포 예정(dcy-005 OQ-3).
|
||||
- 2026-06-01: REQ-028 Approved → Implemented (CHG-20260601-02) — CHG-20260601-01 augmentation에 대한 verification evidence 확보. pytest 368 PASSED + 로컬 smoke로 GET /v1/portfolios 응답에 `display_name` 노출 확인. BAT-009 verification.md AC-13 매핑 누적. REL-001 실 배포 환경 smoke는 배포 시 최종 확인.
|
||||
- 2026-06-01: REQ-028 Implemented → Approved (CHG-20260601-01) — GET /v1/portfolios 응답 schema에 `display_name` 필드 추가 augmentation. portfolio가 참조하는 asset의 display_name을 join하여 UI/CLI 단일 호출 표시 가능. AC-13 신설, Portfolio domain model + PortfolioItem schema + repo join 보강. BAT-009/BAT-014 verification 재실행 필요.
|
||||
- 2026-05-24: REQ-025 / REQ-026 / REQ-027 / REQ-028 / REQ-032 Approved → Implemented (CHG-20260524-02 각각) — BAT-014 verification 완료. `PortfolioItem.symbol` 응답 계약이 코드(`app/schemas/v1.py`, `app/api/v1_portfolios.py`), 테스트(`tests/test_v1_portfolios_crud.py`, `tests/test_v1_portfolios_list.py`), baseline 문서(`docs/interface-contract.md`)에 반영됨. BAT-014는 release-candidate로 승격되었고 BAT-009/BAT-010의 관련 response-contract evidence는 superseded.
|
||||
- 2026-05-24: REQ-025 / REQ-026 / REQ-027 / REQ-028 / REQ-032 Implemented → Approved (CHG-20260524-01 각각) — trex-orbit 팀 이슈(`docs/issues/20260524_portfolio-hub-ticket-symbol-in-portfolios.md`) 후속. `PortfolioItem.symbol` 응답 계약 추가 및 `GET /v1/portfolios` 의 asset join 기반 응답(`portfolios JOIN assets` → `assets.symbol`)을 명문화. BAT-009 / BAT-010 및 REL-001의 기존 response-contract evidence는 부분 무효화되어 follow-up BAT-014에서 재검증 필요.
|
||||
- 2026-05-21: REQ-005 Approved → Implemented (CHG-20260521-03) — BAT-013 confirm-batch-verification 완료. `append_asset_tag_id`/`remove_asset_tag_id` 메서드 신규 + 라우트 호출 교체 + 회귀 테스트 7건 추가. assets 테이블 mutation 정합화 evidence 확보. 전체 369 passed (BAT-012 baseline 362 → +7). BAT-004의 AC-1/AC-5 evidence는 BAT-013에서 superseded. CLI 동일 mismatch는 follow-up REQ 후보.
|
||||
|
||||
Reference in New Issue
Block a user