docs: record governance changes for REQ-008/017/023 defect fixes and add REQ-029

- REQ-017 CHG-20260518-01: document get_portfolio_by_name_and_user postgres
  defect fix; status Implemented preserved
- REQ-008 CHG-20260518-01: document insert_ledger_trade postgres defect fix
  (HTTP 500 on ledger write-back); status Approved, re-verification required
- REQ-023 CHG-20260518-01: add AC-5 (portfolio update --daemon-id CLI) and
  AC-6 (portfolio list daemon-id display); expand Impacted Area
- REQ-029: new Interface REQ for portfolio list borderless output format;
  status Approved, minor-change fast path
- SRS index: register REQ-029, advance Next ID to REQ-030, update Recent
  Change Log Summary

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-18 04:05:58 +00:00
co-authored by Claude Sonnet 4.6
parent b155eeff7d
commit 47c4f9bfde
5 changed files with 190 additions and 6 deletions
@@ -40,6 +40,43 @@
## Change Log
### CHG-20260518-01
- Date: 2026-05-18
- Author: repository-maintainers
#### Change Summary
- `insert_ledger_trade()` 미구현 결함 수정 — PostgresPortfolioRepository에 메서드가 존재하지 않아 모든 POST /v1/assets/{asset_id}/ledger/trades 요청이 HTTP 500으로 실패하던 결함을 수정. Status Approved 유지.
#### Intent
- `app/api/v1_ledger.py:78`에서 `repo.insert_ledger_trade()`를 호출하지만 PostgresPortfolioRepository에 구현이 없어 AttributeError → HTTP 500이 발생했다. FakeRepo 기반 테스트는 통과했으나 실 DB 환경에서는 endpoint 전체가 불동작 상태였다. trex-basic의 write-back이 전면 차단된 High 심각도 결함이었으며, 구현을 추가해 정상화했다.
#### Acceptance Criteria Delta
- Added: 없음
- Updated: 없음
- Removed: 없음
#### Impacted Area Delta
- Docs: 없음
- Modules:
- `app/repositories/postgres.py`: `insert_ledger_trade(asset_id, order_id, execution_id, side, quantity, price, executed_at, fee_amount, tax_amount)` 추가. `INSERT INTO ledger_trades ... ON CONFLICT ON CONSTRAINT uq_ledger_trades_idempotency DO NOTHING RETURNING *` 패턴으로 신규/중복 여부를 `(LedgerTrade, bool)` 튜플로 반환.
- `app/repositories/portfolio_repository.py`: Protocol에 `insert_ledger_trade` 선언 추가, `LedgerTrade` import 추가
- Tests: 기존 FakeRepo 기반 테스트는 영향 없음. 실 DB 통합 테스트(AC-1~7)는 별도 재검증 필요.
#### Delivery Trace Delta
- Batches: BAT-010 (release-candidate) — 결함 수정이 BAT-010 delivery scope에 포함
- Releases: REL-006 (draft) — 배포 전 결함 수정 완료. 실 DB 통합 재검증 후 배포 권장.
#### Revalidation Impact
- Reverification Needed: yes
- Existing Implementation Invalidated: no (AC 정의는 유효, postgres 구현만 누락되어 있었음)
- Status Recommendation: keep-current (Approved; CHG-20260513-01에 의해 Approved 상태)
- Follow-up Action: 실 DB 환경에서 AC-1(신규 체결 201), AC-2(중복 200+duplicate), AC-3(payload 불일치 409), AC-6~7(daemon mismatch/미배정 403) 통합 시나리오 검증 후 Implemented 전환 가능
#### Validation Plan
- `POST /v1/assets/{asset_id}/ledger/trades`에 유효 daemon-bound service-token으로 요청 시 HTTP 201 반환 확인
- 동일 (asset_id, order_id, execution_id) 재요청 시 HTTP 200 + status=duplicate 확인
- daemon mismatch, 미배정 asset 요청 시 HTTP 403 확인
### CHG-20260513-01
- Date: 2026-05-13
- Author: repository-maintainers
@@ -43,6 +43,41 @@
## Change Log
### CHG-20260518-01
- Date: 2026-05-18
- Author: repository-maintainers
#### Change Summary
- `get_portfolio_by_name_and_user()` 미구현 결함 수정 — PostgresPortfolioRepository에 메서드가 존재하지 않아 실 DB 환경에서 모든 name+user 식별 경로가 AttributeError로 실패하던 결함을 수정. Status Implemented 유지.
#### Intent
- REQ-017 AC-2~4(name+user 조합으로 portfolio 조회)를 구현한 CLI 코드와 FakeRepo는 완성되어 있었으나, PostgresPortfolioRepository에 `get_portfolio_by_name_and_user()`가 누락되어 실 DB 환경에서 AttributeError가 발생했다. 결함을 수정해 Implemented 근거를 복원한다.
#### Acceptance Criteria Delta
- Added: 없음
- Updated: 없음
- Removed: 없음
#### Impacted Area Delta
- Docs: 없음
- Modules:
- `app/repositories/postgres.py`: `get_portfolio_by_name_and_user(owner_user_id, name)` 추가 (`_SELECT_PORTFOLIO WHERE owner_user_id = %s AND name = %s`)
- `app/repositories/portfolio_repository.py`: Protocol에 `get_portfolio_by_name_and_user` 선언 추가
- Tests: `tests/test_portfolio_uuid.py` 14 passed (기존 FakeRepo 기반 테스트는 영향 없음)
#### Delivery Trace Delta
- Batches: BAT-006 (release-candidate) — 결함 수정이 BAT-006 delivery scope에 포함
- Releases: REL-006 (draft) — 배포 전 결함 수정 완료
#### Revalidation Impact
- Reverification Needed: no
- Existing Implementation Invalidated: no
- Status Recommendation: keep-current (Implemented)
- Follow-up Action: 없음 (실 DB 통합 테스트로 AC-2~4 재확인 권장)
#### Validation Plan
- `pfhub portfolio status <name> --user <user>`, `pfhub portfolio buy <name> --user <user>` 등 name+user 식별 명령이 실 DB 환경에서 정상 응답하는지 확인
### CHG-20260511-01
- Date: 2026-05-11
- Author: repository-maintainers
@@ -20,10 +20,13 @@
2. Given 기존 daemon에 대해 `daemon_display_name` 수정을 요청할 때, Then daemon entry가 갱신된다.
3. Given 동일 owner user 내에 이미 존재하는 `daemon_slug`로 daemon create를 요청할 때, Then 명시적 오류로 거부되고 중복 entry가 생성되지 않는다.
4. Given daemon list 조회를 요청할 때, Then 인증된 user 소유의 daemon만 반환된다.
5. Given `pfhub portfolio update <name> --user <username> --daemon-id <daemon_id>`를 실행할 때, Then 해당 portfolio의 `managed_by_daemon_id`가 지정한 daemon_id로 갱신된다.
6. Given `pfhub portfolio list --user <username>`을 실행할 때, Then `managed_by_daemon_id`가 설정된 portfolio에는 해당 daemon_id가 출력에 포함되고, 미설정 portfolio에는 해당 항목이 출력되지 않는다.
## Impacted Area
- Docs: `docs/interface-contract.md`, `docs/runtime-flows.md`
- Modules: `app/domain/models.py`, `app/repositories/postgres.py`, `app/api/auth_routes.py` 또는 별도 daemon management route, `app/cli/commands/` (daemon subcommand 신규)
- Modules (AC-5~6 추가): `app/cli/commands/portfolio.py` (`update` 명령 `--daemon-id` 옵션), `app/schemas/portfolio.py` (`UpdatePortfolioRequest.managed_by_daemon_id`, `PortfolioSummary.managed_by_daemon_id`), `app/services/portfolio_service.py` (`_to_portfolio_summary` 필드 전달), `app/cli/core/utils.py` (`print_portfolio_list` daemon 표시)
- Tests: 신규 `tests/test_daemon_registry.py`
## Notes
@@ -33,6 +36,45 @@
## Change Log
### CHG-20260518-01
- Date: 2026-05-18
- Author: repository-maintainers
#### Change Summary
- AC-5(`portfolio update --daemon-id`), AC-6(`portfolio list` daemon-id 표시) 추가 및 Impacted Area 보강. 구현 완료. Status Approved 유지.
#### Intent
- daemon provisioning runbook에서 "asset → daemon 배정" 단계가 CLI로 수행 가능해야 한다. 기존 AC-1~4는 daemon registry CRUD만 다루며 portfolio-daemon 배정 경로가 누락되어 있었다. 운영팀 요청(2026-05-18)에 따라 `portfolio update --daemon-id` CLI 옵션과 `portfolio list` 결과의 daemon-id 표시를 REQ 범위에 추가한다.
#### Acceptance Criteria Delta
- Added:
- AC-5: `pfhub portfolio update <name> --user <username> --daemon-id <daemon_id>`로 `managed_by_daemon_id` 갱신
- AC-6: `pfhub portfolio list --user <username>` 결과에 `managed_by_daemon_id` 조건부 표시
- Updated: 없음
- Removed: 없음
#### Impacted Area Delta
- Modules:
- `app/cli/commands/portfolio.py`: `update` 명령에 `--daemon-id` 옵션 추가
- `app/schemas/portfolio.py`: `UpdatePortfolioRequest.managed_by_daemon_id`, `PortfolioSummary.managed_by_daemon_id` 필드 추가
- `app/services/portfolio_service.py`: `_to_portfolio_summary`에 `managed_by_daemon_id` 전달 추가
- `app/repositories/postgres.py`: `update_portfolio` allowed dict에 `managed_by_daemon_id` 추가
- `app/cli/core/utils.py`: `print_portfolio_list`에 daemon 줄 조건부 출력 추가
#### Delivery Trace Delta
- Batches: BAT-010 (release-candidate) — AC-5~6 구현이 BAT-010 delivery scope에 포함
- Releases: REL-006 (draft)
#### Revalidation Impact
- Reverification Needed: yes (AC-5~6 신규이므로 검증 증거 추가 필요)
- Existing Implementation Invalidated: no (AC-1~4 기존 구현 유효)
- Status Recommendation: keep-current (Approved)
- Follow-up Action: BAT-010 verification.md에 AC-5~6 검증 증거 추가 후 Implemented 전환 가능
#### Validation Plan
- `pfhub portfolio update <name> --user <user> --daemon-id <id> --force` 실행 후 DB `portfolios.managed_by_daemon_id` 값 확인
- `pfhub portfolio list --user <user>` 출력에서 daemon-id 유무 항목별 표시 확인
### CHG-20260513-02
- Date: 2026-05-13
- Author: repository-maintainers
@@ -0,0 +1,69 @@
# REQ-029: CLI `portfolio list` 출력 — 테두리 없는 리스트 형식
- Status: Approved
- Type: Interface
- Priority: Low
- Owner: repository-maintainers
## Intent
- `pfhub portfolio list` 기본 출력을 Rich Table(테두리 포함) 대신 항목별 텍스트 블록으로 변경해 터미널 폭에 구애받지 않는 가독성을 확보한다.
- JSON 출력(`--format json`)은 기존과 동일하게 유지한다.
## Requirement
- `pfhub portfolio list --user <username>` 명령의 기본 출력(text 형식)은 테두리·구분선 없는 리스트 형태로 각 portfolio를 표시해야 한다.
- 각 항목은 3줄 블록으로 구성한다: (1) name/symbol/status/broker [tags], (2) cash/qty/price/buffer/total 수치, (3) portfolio_id.
- `managed_by_daemon_id`가 설정된 항목은 (3) 다음 줄에 daemon_id를 추가로 표시하고, 미설정 항목은 해당 줄을 생략한다.
- 항목 간에 빈 줄 한 줄로 구분한다.
- `--format json` 옵션 사용 시 기존 JSON 응답을 그대로 반환한다.
## Acceptance Criteria
1. Given `pfhub portfolio list --user <username>` 실행 시, Then Rich Table 형식(헤더·테두리)이 아닌 텍스트 블록 리스트가 출력된다.
2. 각 portfolio 항목은 name, symbol, status, broker 정보와 cash/qty/price/buffer/total 수치, portfolio_id를 포함한다.
3. Given `managed_by_daemon_id`가 설정된 portfolio가 있을 때, Then 해당 항목에 daemon_id 줄이 추가로 표시된다.
4. Given `pfhub portfolio list --user <username> --format json` 실행 시, Then JSON 배열이 반환되며 텍스트 블록 출력이 표시되지 않는다.
## Impacted Area
- Docs: 없음
- Modules:
- `app/cli/core/utils.py`: `print_portfolio_table` 제거, `print_portfolio_list` 신규 구현, `render_output`에서 `print_portfolio_list` 호출
- Tests: 기존 `tests/test_portfolio_uuid.py` 출력 내용 미검증이므로 별도 영향 없음
## Notes
- 이 변경은 CLI 출력 형식만 변경하며 API, 데이터 모델, DB 스키마에는 영향을 주지 않는다.
- REQ-023 AC-6(managed_by_daemon_id 표시)와 구현을 공유한다.
## Change Log
### CHG-20260518-01
- Date: 2026-05-18
- Author: repository-maintainers
#### Change Summary
- 최초 생성 및 Approved 전환 — 운영팀 요청(2026-05-18)에 따라 `portfolio list` 출력 형식을 테두리 없는 리스트 형태로 변경 완료. 구현 및 사용자 확인 완료.
#### Intent
- Rich Table 형식은 터미널 폭이 좁거나 항목 수가 많을 때 가독성이 떨어진다. 텍스트 블록 리스트는 항목별 스캔이 쉽고 터미널 폭 의존성이 없다.
#### Acceptance Criteria Delta
- Added: AC-1~4 (최초 생성)
- Updated: 없음
- Removed: 없음
#### Impacted Area Delta
- Modules:
- `app/cli/core/utils.py`: `print_portfolio_table` → `print_portfolio_list` 교체, REQ-023 AC-6 daemon 줄 출력 포함
#### Delivery Trace Delta
- Batches: 미정 (minor-change fast path로 batch-lite 생성 예정)
- Releases: 미정
#### Revalidation Impact
- Reverification Needed: no (신규 REQ, 기존 구현 무효화 없음)
- Existing Implementation Invalidated: no
- Status Recommendation: Approved
- Follow-up Action: batch-lite 생성 → 검증 → confirm-req-implemented
#### Validation Plan
- `pfhub portfolio list --user <username>` 실행 결과에 Rich Table 헤더·테두리가 없는지 확인
- daemon-id 있는 항목과 없는 항목 혼재 시 조건부 표시 확인
- `--format json` 옵션 시 JSON 반환 확인
+7 -6
View File
@@ -74,7 +74,7 @@
## Next Requirement ID
- Current: `REQ-029`
- Current: `REQ-030`
- Rule: `Requirement Register`에 있는 가장 큰 REQ 번호보다 정확히 1 커야 한다.
## Requirement Register
@@ -105,11 +105,12 @@
| REQ-022 | 운영 로그 접근 및 release evidence 기록 절차 | Documentation | Approved | Medium | repository-maintainers | [REQ-022](./Documentation/req-022_operator-log-access-and-release-evidence-recording.md) |
| REQ-023 | Daemon Registry 관리 경로 | Interface | Approved | High | repository-maintainers | [REQ-023](./Interface/req-023_daemon-registry-and-service-token-daemon-binding.md) |
| REQ-028 | README.md Daemon Provisioning Runbook 반영 | Documentation | Approved | Medium | repository-maintainers | [REQ-028](./Documentation/req-028_daemon-model-baseline-docs-update.md) |
| REQ-029 | CLI `portfolio list` 출력 — 테두리 없는 리스트 형식 | Interface | Approved | Low | repository-maintainers | [REQ-029](./Interface/req-029_portfolio-list-borderless-output.md) |
## Recent Change Log Summary
- 2026-05-13: REQ-023, REQ-028 Approved 전환 — confirm-req 게이트 통과 (필수 필드, AC, blocker 없음)
- 2026-05-13: REQ-023, REQ-028 Proposed 생성 — dcy-006 FR-1(daemon registry CRUD)·FR-6(README.md runbook); REQ-024~027은 REQ-005~009/011/012 CHG-20260513-01과 완전 중복으로 미생성
- 2026-05-13: REQ-005~009, REQ-011~012 CHG-20260513-01 — dcy-006 daemon_id / exact-one asset assignment 반영으로 revised contract가 기존 Implemented evidence를 무효화해 Status를 Approved로 되돌림
- 2026-05-13: REQ-021 CHG-20260513-01 — token provenance 절차에 `daemon_id` evidence 필드와 rotate invariant 반영, Status 유지
- 2026-05-12: REQ-020~022 Approved 전환 — confirm-req 게이트 통과 (필수 필드, AC, blocker 없음)
- 2026-05-18: REQ-029 Approved 생성 — `portfolio list` 테두리 없는 리스트 형식, minor-change fast path, 구현 완료
- 2026-05-18: REQ-023 CHG-20260518-01 — AC-5(`portfolio update --daemon-id`), AC-6(`portfolio list` daemon-id 표시) 추가, Impacted Area 보강, Status Approved 유지
- 2026-05-18: REQ-008 CHG-20260518-01 — `insert_ledger_trade` postgres 미구현 결함 수정, HTTP 500 정상화, Status Approved 유지
- 2026-05-18: REQ-017 CHG-20260518-01 — `get_portfolio_by_name_and_user` postgres 미구현 결함 수정, Status Implemented 유지
- 2026-05-13: REQ-023, REQ-028 Approved 전환 — confirm-req 게이트 통과 (필수 필드, AC, blocker 없음)