Commit Graph
83 Commits
Author SHA1 Message Date
genichin 02bfa60299 chore(release): prepare portfolio-hub v0.1.3
Publish verified PortfolioHub image / verify (push) Canceled after 0s
Publish verified PortfolioHub image / publish (push) Canceled after 0s
2026-08-16 16:57:26 +09:00
genichin 75e55fde9c feat(api): expose strategy nick on portfolio items 2026-08-16 13:13:39 +09:00
genichin ee1af6ef49 fix(api): reject explicit null strategy nick
Publish verified PortfolioHub image / verify (push) Canceled after 0s
Publish verified PortfolioHub image / publish (push) Canceled after 0s
2026-08-16 00:03:25 +09:00
genichin c292df0a4b chore(release): harden nick contract for v0.1.2 2026-08-15 23:46:57 +09:00
genichin e6a90800ec feat(strategy): expose configurable nick contract 2026-08-15 23:38:18 +09:00
genichin 9c8baac70b feat(container): harden v0.1.1 publication 2026-08-15 15:14:36 +09:00
genichin fd080496b3 feat(release): add verified image publication pipeline 2026-08-15 14:52:01 +09:00
genichin e90519ad39 fix(daemons): block owner transfer while assigned 2026-08-15 13:20:02 +09:00
genichin e2102b6f2b feat(portfolios): persist and validate daemon assignment on create/update
POST /v1/portfolios now forwards a non-null managed_by_daemon_id into
create persistence, and both create and PATCH apply the same daemon
validation contract (UUID format -> 422, missing/foreign -> 404,
inactive -> 409, admin target-owner basis). Daemon validation locks the
daemon row FOR SHARE inside the portfolio write transaction so
validation and write form one atomic decision boundary.

Adds domain errors, repository protocol/signature updates, API/PostgreSQL
tests (POST->GET persistence, no partial state on failure, assignment vs
deactivation race), and contract/data-model documentation.
2026-08-15 12:56:53 +09:00
genichin 6e02d92282 Add ProofLine criteria documentation and Strategy Catalog v1 implementation
- Add acceptance criteria documentation (AC-0013 through AC-0023)
- Add Line-0003 discovery documentation
- Implement Strategy Catalog v1 with new database migration
- Update portfolio API endpoints and schemas
- Refactor strategy and portfolio services
- Add strategy management CLI commands
- Update domain models with strategy catalog support
- Add comprehensive tests for v1 strategies
- Update design documentation (data model, interfaces, runtime flows)
2026-08-15 11:18:31 +09:00
genichin 93840800fa feat(portfolios): expose ledger investment aggregates
Add reversal-aware grouped manual-ledger aggregates to portfolio list responses and align the single-portfolio return summary with the same semantics.\n\nRefs #23
2026-08-13 13:23:31 +09:00
Hermes Agent d48279f7ab fix(migrations): harden BAT-025 PostgreSQL verification 2026-07-15 17:58:18 +09:00
genichin b3447f699e feat(api): add owner-scoped manual ledger endpoint 2026-07-14 19:49:03 +09:00
genichinandClaude Fable 5 8a07742049 feat(req-055): expose strategy & market meta fields in PortfolioItem response (additive)
Adds 4 additive fields to GET /v1/portfolios (and single-portfolio) response:
- active_strategy_id (str|null): portfolios.active_strategy_id raw value
- strategy_name (str|null): strategy_sets.name via LEFT JOIN
- strategy_params (dict): strategy_sets.config JSON parsed; {} on missing/invalid
- markets (list[str]): assets.markets; [] on missing/empty

_SELECT_PORTFOLIO extended with LEFT JOIN strategy_sets; _row_to_portfolio
parses new columns (row[19-21]) with JSON error isolation (WARNING log, no 500).
_portfolio_to_item maps all 4 new fields. Existing 16 fields unchanged.
N+1 prevented: single SQL JOIN for both list and single-item paths.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VHtM5Ws1HKCKQ4JGi6GRb5
2026-07-04 11:05:51 +09:00
genichinandClaude Opus 4.8 2dcbafb7a8 fix(dcy-011/bat-022): GET /v1/admin/daemons 목록 엔드포인트 추가 — UI 계약 누락(405) 해소 (라이브 통합테스트 결함)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VHtM5Ws1HKCKQ4JGi6GRb5
2026-06-28 18:20:13 +09:00
genichinandClaude Sonnet 4.6 83e6ddead4 fix(bat-022): owner 재할당 credential 무효화 원자화 — leak 방지 (QC Major)
update_daemon_owner()에서 owner UPDATE + credential DELETE를 단일
conn.transaction() 블록으로 묶어 원자성 보장. 실패 시 두 연산 모두
롤백되므로 "새 owner + 이전 credential 잔류" leak 상태(dcy-011 OQ-3)
가 불가능해짐. 실행 순서도 DELETE-first(안전 순서)로 고정.

단위 테스트 3종 추가:
- DELETE-before-UPDATE 실행 순서 검증
- UPDATE 실패 시 rollback(commit 미호출) 검증
- 단일 transaction() 블록 안에서 두 연산 실행 검증

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VHtM5Ws1HKCKQ4JGi6GRb5
2026-06-28 14:31:00 +09:00
genichinandClaude Sonnet 4.6 d24058d863 feat(bat-022): daemon CRUD admin API + self-service credential management (REQ-052/053/054)
- POST/PATCH/deactivate/activate /v1/admin/daemons — admin-only daemon CRUD (REQ-053)
- GET/PUT/DELETE /v1/daemons/{daemon_id}/credential — user-owned scoped credential management (REQ-052)
- Owner re-assignment auto-invalidates BrokerCredential via update_daemon_owner() (REQ-053 AC-4)
- Deactivation auto-revokes service tokens via revoke_tokens_by_daemon_id() (REQ-022 reuse, REQ-053 AC-6)
- DELETE /v1/admin/daemons/{id} → 405 (hard delete not provided, REQ-053 AC-8)
- All responses exclude app_key/app_secret; Fernet encryption at rest (REQ-054)
- Service-token → 403 on credential endpoints (REQ-052 AC-10)
- Admin ownership scope enforced: admin cannot write other user's credential (REQ-052 AC-9)
- REQ-051 regression: 4 service-token guard tests pass (REQ-054 AC-7)
- 44 new tests, 565 total passed (521 baseline + 44 new), 0 failures

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VHtM5Ws1HKCKQ4JGi6GRb5
2026-06-28 14:18:37 +09:00
genichin 3e1b571906 feat(bat-021): REQ-047 Step2 동반 코드 수정 + 실 DB 통합검증 + 런북
- ensure_schema() broker_credentials: daemon_id NOT NULL, PK=(daemon_id,broker)로 갱신
- _row_to_broker_credential: Step2 컬럼 순서(daemon_id 선두)로 갱신
- upsert_broker_credential: ON CONFLICT (daemon_id,broker)로 수정, fallback try/except 제거
- list_broker_credentials: fallback 제거, SELECT 컬럼 순서 갱신
- test_bat020_broker_credential_domain.py: AC-3/AC-4 행 순서를 Step2 포맷으로 갱신
- tests/test_bat021_step2_ensure_schema_and_upsert.py: 12개 신규 테스트
- docs/runbooks/bat-021-req047-step2-migration-runbook.md: 운영 적용 런북
- docs/batches/bat-021: batch 문서(index/planning/design/implementation/verification)

실 PostgreSQL 통합검증(AC-1~7 전건 PASS):
  Step1 upgrade → backfill → Step2 upgrade → downgrade 시나리오 포함
  NULL 사전검증(upgrade 거부) 및 downgrade 안전장치(중복 시 RuntimeError) 확인

521 passed, 0 failed (기존 509 + 신규 12)
2026-06-28 10:19:27 +09:00
genichinandClaude Sonnet 4.6 51b7cbee35 feat(bat-020): daemon-scoped 브로커 인증정보 (REQ-046~051)
- REQ-046: BrokerCredential.daemon_id 필드 추가, repository 시그니처 갱신
- REQ-047: alembic revision Step1(daemon_id NULL 컬럼 추가), Step2(PK 전환, dev/test DB 한정)
- REQ-048: backfill-daemon-id CLI subcommand (--dry-run/--force/--broker 지원)
- REQ-049: GET /v1/user_info daemon-scoped 단일 broker 전달 (brokers→broker, daemon_id 없으면 403)
- REQ-050: credential set/list/remove --daemon-id 옵션 추가 (daemon_slug 해석)
- REQ-051: 보안 격리 - service-token 전용, web 토큰 차단, 로그 app_secret 비노출, graceful degrade

비가역 결재 게이트: REQ-047 Step2(PK 전환)는 사장님 결재 후 실DB 적용 예정(이 batch는 revision 코드만)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VHtM5Ws1HKCKQ4JGi6GRb5
2026-06-28 00:45:46 +09:00
genichinandClaude Opus 4.8 233cbc3df6 fix(portfolio): name 검증에 한글+공백 허용 (minor-change, CHG-20260627-03)
_PORTFOLIO_NAME_RE + CLI _NAME_RE를 ^[a-zA-Z0-9가-힣 ._:-]{1,100}$로 완화. name은 (owner,name) 조회 키일 뿐 wire 식별자 아님. 475 passed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VHtM5Ws1HKCKQ4JGi6GRb5
2026-06-27 12:03:33 +09:00
genichinandClaude Sonnet 4.6 c5288a8c42 feat(bat-019): REQ-044 GET /v1/assets require_read + REQ-045 GET /v1/daemons caller-scope
REQ-044: list_catalog_assets 의존성을 require_admin → require_read로 완화.
asset 쓰기(POST/PATCH/DELETE)는 require_admin 유지.

REQ-045: GET /v1/daemons 신규 엔드포인트 — caller 소유 데몬만 반환(require_read).
user_id=None(env 토큰)은 빈 목록 200. DaemonResponse/DaemonListResponse 스키마 추가.

기존 테스트(test_v1.py, test_asset_catalog.py, test_daemon_assignment.py, test_tags_master.py)
REQ-044 권한 완화에 맞게 업데이트. test_v1_daemons.py 신규 추가(13 케이스).

pytest 472 passed, 0 failed.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VHtM5Ws1HKCKQ4JGi6GRb5
2026-06-27 10:36:04 +09:00
genichinandClaude Sonnet 4.6 d224d716f6 fix(holdings): /holdings/cost-basis WAC row에 fee_amount 편입 (QC보정)
get_holdings_cost_basis 핸들러의 row dict에 fee_amount 누락으로
/holdings/cost-basis의 cost_basis_per_unit이 매수 수수료 미편입값을
반환하던 버그 수정. /pnl/realized 핸들러와 동일하게 e.fee_amount 포함.
테스트: buy fee>0 케이스(1000.5 단언) + fee=0 회귀 케이스 추가.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VHtM5Ws1HKCKQ4JGi6GRb5
2026-06-26 20:34:03 +09:00
genichinandClaude Sonnet 4.6 73414817e0 fix(pnl): 매수 수수료를 WAC cost basis에 편입 (REQ-041 CHG-20260626-01)
WAC 공식에 buy row의 fee_amount를 분자에 포함해 실현 PnL이 당일 net과 정확히 일치하도록 수정.
calculate_realized_pnl + calculate_current_wac 양 함수 동일 적용.
신규 단위 테스트 9개(TestBuyFeeInCostBasis 5 + TestCurrentWacBuyFeeInCostBasis 4) 추가.
change-req: REQ-041 AC-9/AC-10 추가, REQ-040/REQ-043 Notes 갱신, index 갱신.
전체 456 passed / 0 failed.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VHtM5Ws1HKCKQ4JGi6GRb5
2026-06-26 20:34:03 +09:00
genichinandClaude Opus 4.8 d93c0bcaf5 feat(holdings): REQ-043 현재 보유 평단(WAC) read — GET /v1/portfolios/{id}/holdings/cost-basis (BAT-018, PR #14)
dcy-008(confirmed)/REQ-043(Approved)/BAT-018. trex-core dcy-034 FR-7 cold-start seed 의존. AC 11/11, QC PASS. worktree+PR 워크플로 첫 파일럿.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VHtM5Ws1HKCKQ4JGi6GRb5
2026-06-26 00:07:43 +09:00
genichinandClaude Opus 4.8 bd34450ee9 feat(pnl): trade history + realized PnL API (WAC) — BAT-017, REQ-039~042 Implemented
- GET /v1/portfolios/{id}/trades and GET /v1/portfolios/{id}/pnl/realized endpoints
- Weighted Average Cost (WAC) realized PnL calculation in app/domain/pnl.py
- cost_basis and data_completeness_warning returned in pnl API response
- Test coverage: 50 passed (WAC 20 + trades 12 + pnl 18), full 427 passed regression ✓
- WAC validated against 199,300 unit e2e test cases
- DB migration: none required
- QC: qc-reviewer PASS-WITH-NOTES; team lead confirm-batch-verification → release-candidate
- Remaining non-blocking: symbol filter edge case testing, HTTP_422 deprecation

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VHtM5Ws1HKCKQ4JGi6GRb5
2026-06-18 16:45:01 +09:00
genichinandClaude Opus 4.8 6b812b6035 fix: GET /v1/user_info 500 on missing broker_credentials table (issue #9)
trex-core reported GET /v1/user_info returning 500 for valid service/daemon
tokens (whoami → daemon_id), degrading the daemon and holding new trades. The
reported hypothesis (service-token None deref) was a misdiagnosis. Root cause:

  app/api/v1_user_info.py: credentials = repo.list_broker_credentials(...)
  psycopg.errors.UndefinedTable: relation "broker_credentials" does not exist

The broker_credentials table was missing and the handler only caught
AttributeError, so the DB error propagated to a bare 500.

Two fixes:
1. Handler now degrades gracefully when broker-credential lookup fails — returns
   empty brokers and still serves daemon_id/user_id/permission, which do not
   depend on broker credentials. This is what trex-core's whoami needs.
2. ensure_schema() now also CREATE TABLE broker_credentials (same shape as
   migration 20260502_0014). ensure_schema-bootstrapped DBs were missing it,
   since previously only the alembic path created it.

Added tests/test_v1.py regression: broker-credential lookup raising a DB error
must yield HTTP 200 with daemon_id, not 500. Verified against the real DB
(was 500 → now 200 + daemon_id). Full suite 377 passed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-06 10:17:30 +09:00
genichinandClaude Opus 4.8 2a19b7f058 fix: ledger write idempotency for TimescaleDB hypertable (BAT-016 rework)
BAT-016's initial implementation passed only against mocked repositories and
broke on a real database. Two defects fixed:

1. insert_ledger_trade used `ON CONFLICT (portfolio_id, order_id, execution_id)
   WHERE order_id IS NOT NULL` but portfolio_ledger is a TimescaleDB hypertable,
   which cannot have that partial unique index (must include the partitioning
   column recorded_at). The statement errored against Postgres. Replaced with
   application-level idempotency: SELECT-before-insert inside the existing
   `SELECT ... FOR UPDATE` portfolio row lock, which serializes concurrent
   same-portfolio writes (race-safe). Duplicate returns current authoritative
   portfolio state (dcy-006 OQ-4 b).

2. `with self._connect() as conn:` closes the shared psycopg3 connection on exit,
   leaving the reused repository connection unusable. Switched to
   `with conn.transaction():` (commits without closing).

Added tests/test_ledger_consolidation_integration.py — real-Postgres integration
tests (new / duplicate / negative-balance) that close the mock-only verification
gap. Full suite: 376 passed.

SDLC: REQ-036/037 CHG-20260605-03 reconcile OQ-7 (ii) (DB partial unique →
application-level idempotency) since the hypertable makes the DB constraint
infeasible. REQ-036/037/038 → Implemented; BAT-016 → release-candidate.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-05 22:11:17 +09:00
genichin 44f2102d26 feat: consolidate ledger trade writes into portfolio_ledger
- add ledger consolidation migration and remove ledger_trades usage
- return portfolio state after v1 ledger trade writes
- deprecate legacy buy/sell endpoints with deprecation headers
- add BAT-016, DCY-006, and REQ-036~038 documentation
- update repository models and tests for unified ledger entries
2026-06-05 20:55:24 +09:00
genichin 2470c0a00c feat: apply rel-001 feedback cleanup 2026-06-05 13:57:46 +09:00
genichinandClaude Opus 4.7 84de34c301 feat: REQ-028 add display_name to GET /v1/portfolios response
- Portfolio domain model + PortfolioItem schema에 display_name 필드 추가
- _SELECT_PORTFOLIO에 a.display_name JOIN 보강, _portfolio_to_item 매퍼 전달
- REQ-028 CHG-20260601-01/02: Implemented → Approved → Implemented
  - AC-13 신설 (display_name == joined assets.display_name)
- BAT-009 verification.md: AC-13 매핑 + augmentation history 누적
- REL-001 verification checklist 재실행 evidence 갱신 (368 PASSED, 로컬 smoke 5건 통과)
- tests/test_v1.py: dead 중복 GET /v1/assets 섹션 제거 (두 번째 동명 정의가 첫 번째를 override하던 잔재)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-01 14:00:59 +00:00
genichin 136ae16885 feat: add portfolio item symbol to v1 responses 2026-05-24 13:09:37 +09:00
genichinandClaude Opus 4.7 3a78e5c138 fix: reveal plaintext service token on issue/rotate (HOTFIX-001)
- render_service_token_result no longer masks the token field; plaintext
  tokens are SHA-256 hashed in DB and irrecoverable once lost, so issue/
  rotate must reveal them once at issuance (matches interface-contract.md)
- Remove now-unused _mask_payload helper; keep mask_token_text for the
  auxiliary masked_token identifier line
- Add one-time reveal warning to issue/rotate table output
- 3 regression tests: JSON plaintext assertion, render unit test, table warning
- Hotfix Exception Path record: docs/hotfixes/hotfix-001
- Total tests: 369 -> 371 passed

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 17:36:05 +09:00
genichinandClaude Opus 4.7 58899b1a94 feat: REQ-005 assets table tag apply/remove SQL alignment (BAT-013)
- REQ-005 CHG-20260521-02 follow-up: clarify AC-1/AC-5 to target assets table
- Add append_asset_tag_id/remove_asset_tag_id on PostgresPortfolioRepository
- Replace internal calls in apply_asset_tag/remove_asset_tag routes (HTTP contract unchanged)
- 7 new regression tests: method existence, SQL target table, error branches, idempotency guard
- Update existing AC-1/AC-5 assertions to verify new methods + portfolios methods uncalled
- Documentation: runtime-flows.md flow body + change log; rel-001 includes BAT-013
- REQ-005 transition: Approved → Implemented (CHG-20260521-03) via BAT-013 verification
- Total tests: 362 → 369 passed

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 00:04:33 +09:00
genichinandClaude Opus 4.7 9038f41010 feat: REQ-002/REQ-010 implementation — assets.tag_ids column + GET /v1/assets admin catalog (BAT-012)
- REQ-002 scope redefinition: portfolios.tag_ids → assets.tag_ids UUID[] column with GIN index
- REQ-010 augmentation: GET /v1/assets redesigned as admin catalog endpoint (admin-only, no token_type branching)
- Asset tagging infrastructure: tag_ids column, archived flag synthesis for inactive tags, list_assets N+1 optimization
- DB migration 20260521_0023: assets.tag_ids UUID[] NOT NULL DEFAULT '{}', idx_assets_tag_ids_gin
- Test suite: 17 new test cases for assets.tag_ids + GET /v1/assets AC-9~12 (362 tests total)
- Documentation: runtime flows, release baseline, batch verification all updated
- BAT-012 added to REL-001 Included Batch and marked release-candidate

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 23:37:08 +09:00
genichin d01039e98f Merge branch 'main' of https://github.com/fourblocks2020/portfolio-hub
# Conflicts:
#	migrations/versions/20260519_0021_portfolio_tags_data_migration.py
#	migrations/versions/20260520_0022_service_token_unique_index_fix.py
2026-05-21 13:28:56 +00:00
genichin c4f8718872 fix: respect root_path in FastAPI docs 2026-05-21 21:45:39 +09:00
genichinandClaude Sonnet 4.6 6add0ade29 feat: REQ-001~018 Implemented 전환, REQ-010 GET /v1/assets admin catalog 재정의, REL-001 draft
- confirm-req-implemented: REQ-001~018 (17건, REQ-013 제외) Approved → Implemented
  - BAT-001(REQ-009/011/014), BAT-002(REQ-010/012/015), BAT-003(REQ-001/002/003/006)
  - BAT-004(REQ-004/005), BAT-005(REQ-007/008), BAT-006(REQ-016/017/018)
  - CHG-20260521-01 entry 각 REQ에 추가; SRS index Recent Change Log 갱신

- REQ-010 CHG-20260521-02: GET /v1/assets admin catalog 전용 재정의
  - 기존 token_type 분기 portfolio 조회 코드 제거, admin permission + list_assets() 호출로 대체
  - AssetCatalogListResponse 스키마 추가 (items/total)
  - REQ-010 Status: Implemented → Approved (재검증 필요)
  - 테스트 343건 전체 통과: service/user token 403, admin token 200 + catalog schema 검증

- migration 보강: portfolios_asset_migration market 컬럼 부재 fallback,
  portfolio_tags 빈 테이블 early-return, service_token index fix autocommit_block 정리

- docs/releases/rel-001: REL-001 draft 추가 (BAT-001~011 포함)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-21 10:17:39 +00:00
genichinandClaude Sonnet 4.6 4bf6b554a7 feat: v1 portfolio mutation, sub-resources, ledger path migration (BAT-008~011)
dcy-003/004 기반 v1 API surface 완성 및 BAT-008~011 구현·검증 완료:

- BAT-008 (REQ-019~021,024): 토큰 surface baseline 문서 갱신
- BAT-009 (REQ-025~028): POST/PATCH/DELETE/GET /v1/portfolios CRUD 신설
  - _require_write_token, _get_portfolio_or_404, soft delete, daemon-scoped list
- BAT-010 (REQ-030~032): portfolio tag/comment 서브 라우트 신설 + baseline 갱신
  - GET/POST/DELETE /v1/portfolios/{id}/tags
  - POST/GET/PATCH/DELETE /v1/portfolios/{id}/comments
- BAT-011 (REQ-029): ledger path migration
  - POST /v1/assets/{asset_id}/ledger/trades → /v1/portfolios/{portfolio_id}/ledger/trades
  - v1_ledger.py 제거, disabled 차단(409) 추가
  - test_v1.py / test_daemon_assignment.py 구 path 갱신
- 신규 tests: test_v1_portfolios_crud.py, test_v1_portfolios_list.py,
  test_v1_portfolios_tags_comments.py, test_v1_portfolios_ledger.py
- 전체 suite: 340 passed

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-21 07:25:39 +00:00
genichinandClaude Sonnet 4.6 c815ce39b6 feat: dcy-003/004 토큰 surface 개편 및 daemon lifecycle 정책 구현 (BAT-006/007)
BAT-006 (REQ-016/017/018):
- client token 발급 경로 제거 (issue_client_token, POST /auth/tokens, 관련 스키마/테스트)
- service-token rotate unique index revoked_at IS NULL 조건 추가 (Alembic CONCURRENTLY 마이그레이션)
- POST /auth/service-tokens body daemon_id forward 누락 보정 (1-line fix)

BAT-007 (REQ-022/023):
- daemon deactivate 시 바인딩 service token 자동 revoke (CLI --force 옵션 + revoke_tokens_by_daemon_id)
- NULL daemon_id legacy token 진단 CLI (migrate-null-daemon --dry-run)

거버넌스 문서: dcy-003/004 Discovery, REQ-016~023/025~032 신규, BAT-006/007 전체 산출물

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-21 01:03:39 +00:00
genichinandClaude Sonnet 4.6 d4b841bc37 feat: 태그 HTTP API(BAT-004) 및 pfhub tag CLI + baseline 문서 갱신(BAT-005)
BAT-004 (REQ-004, REQ-005):
- app/api/v1_tags.py: POST /v1/tags, GET /v1/tags, PATCH /v1/tags/{id}/deactivate
- app/api/v1_portfolios.py: POST/DELETE /v1/portfolios/{id}/tags
- app/api/v1_assets.py: POST/DELETE /v1/assets/{id}/tags 엔드포인트 추가
- app/schemas/v1.py: TagItem, TagCreateRequest, TagListResponse, TagApplyRequest/Response 추가
- app/repositories/portfolio_repository.py, postgres.py: append/remove_portfolio_tag_id 추가
- app/main.py: v1_portfolios_router, v1_tags_router 등록
- tests/test_tags_http_api.py: 22개 HTTP API 테스트

BAT-005 (REQ-007, REQ-008):
- app/cli/commands/tag.py: pfhub tag create/list/deactivate/apply/remove 7개 커맨드 신규
- app/cli/main.py: tag_app 등록
- app/cli/commands/portfolio.py: pfhub portfolio tag add/list/remove 완전 제거(breaking change)
- app/cli/core/utils.py: print_tag_table + list[Tag] render 분기 추가
- tests/test_tags_cli.py: REQ-007 AC-1~9 커버 24개 CLI 테스트 신규
- tests/test_cli.py: 제거된 portfolio tag 테스트 2개 삭제
- docs/data-model.md, interface-contract.md, project-structure.md, runtime-flows.md: 태그 모델 baseline 갱신(REQ-008)
- docs/batches/: BAT-004, BAT-005 전체 산출물(release-candidate)

247 passed

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-20 01:16:13 +00:00
genichin 158929b2d9 feat: asset catalog API, 태그 DB 기초, 마이그레이션 3건 및 도메인/테스트 갱신 (2026-05-19) 2026-05-19 22:15:55 +00:00
genichin e2226e28b6 feat: implement asset catalog database foundation (BAT-001, REQ-009/011/014)
Asset Catalog Model Implementation (BAT-001):
- Introduce assets table with UUID PK, symbol UNIQUE, markets TEXT[] array
- Define MarketCode enum (KRX, NXT, SOR, NASDAQ, NYSE, UPBIT, BITHUMB)
- Add Asset domain model with catalog properties
- Implement asset CRUD protocols in portfolio_repository
- Add PostgreSQL asset CRUD operations with GIN index on markets

Portfolio Model Refactoring (REQ-011):
- Add portfolios.asset_id UUID FK to assets(asset_id) ON DELETE RESTRICT
- Remove portfolios.symbol and portfolios.market columns
- Update Portfolio domain model to reference asset_id instead of symbol/market
- Refactor all portfolio queries to JOIN assets catalog
- Update all repository methods to use asset references

Data Migration (REQ-014):
- Create 20260519_0017 Alembic revision for assets table creation
- Create 20260519_0018 Alembic revision for atomic data migration:
  * Extract unique symbol combinations from portfolios
  * Aggregate markets using ARRAY_AGG(DISTINCT market)
  * Populate assets catalog with extracted data
  * Update portfolios.asset_id to reference new assets
  * Remove legacy symbol/market columns in single transaction
- Implement migration with rollback support

Requirements Approved (REQ-001~008):
- REQ-001~008 transitioned from Proposed to Approved
- Complete tag model requirement set with implementation readiness

New Requirements Approved (REQ-009~015):
- REQ-009: assets table schema (approved, included in BAT-001)
- REQ-010: admin asset catalog CRUD (approved, future BAT-002)
- REQ-011: portfolios asset_id FK (approved, included in BAT-001)
- REQ-012: portfolio create catalog validation (approved, future BAT-002)
- REQ-013: GET /v1/assets token branching (approved, future BAT-002)
- REQ-014: portfolios data migration (approved, included in BAT-001)
- REQ-015: asset catalog baseline docs (approved)

Code Changes:
- app/domain/models.py: Add Asset model, MarketCode enum, Portfolio asset_id FK
- app/repositories/portfolio_repository.py: Define asset CRUD protocol
- app/repositories/postgres.py: Implement asset operations with full CRUD
- app/api/v1_assets.py: Remove symbol/market direct references
- app/cli/commands/portfolio.py: Update portfolio commands for asset references
- app/schemas/portfolio.py: Update portfolio schema with asset_id
- All portfolio operations throughout codebase: Use asset references

Testing:
- Add test_asset_catalog.py for asset CRUD operations
- Verify REQ-009 AC-1~5 (asset catalog creation, lookup, uniqueness)
- Verify REQ-011 AC-1~5 (FK constraint, portfolio asset reference)
- Verify REQ-014 AC-1~6 (migration atomicity, data correctness, rollback)
- Update existing tests for asset model usage

Documentation:
- Create BAT-001 batch documents (planning, design, implementation, verification)
- Update dcy-001 with implementation details and confirmed architecture
- Add 8 new requirement documents (REQ-009~015)
- Update docs/discovery/index.md with batch entry

Impact:
- Database: assets catalog table, portfolios refactored to reference assets
- Migration: 2 new Alembic revisions (0017 assets, 0018 portfolio migration)
- API: Symbol/market now accessed through asset references
- CLI: Portfolio commands require asset reference pattern
- All existing portfolio data preserved and correctly migrated
2026-05-19 15:59:20 +00:00
genichin 1082f041a1 chore: restructure SRS for asset catalog and tag model redesign (dcy-001/002, REQ-001~008)
Architecture Redesign (May 19, 2026):
- Separate admin-managed asset catalog from user portfolios
- Eliminate portfolio as asset definition; clarify portfolio as user position
- Introduce new asset_catalog + portfolio structure

Discovery Documents:
- dcy-001: asset-catalog-and-user-portfolio-model — admin asset management and user portfolio separation
- dcy-002: tag-ownership-and-applicability-policy — tag model implementation strategy

New Requirements (REQ-001~008):
- REQ-001: tags master table introduction (Data, Proposed)
- REQ-002: assets.tag_ids UUID[] column addition (Data, Proposed)
- REQ-003: portfolios.tag_ids and portfolio_tags removal (Data, Proposed)
- REQ-004: tags master HTTP API (Interface, Proposed)
- REQ-005: tag apply/remove HTTP API (Interface, Proposed)
- REQ-006: portfolio_tags data migration (Migration, Proposed)
- REQ-007: pfhub tag CLI commands (Interface, Proposed)
- REQ-008: tag model baseline docs update (Documentation, Proposed)

Cleanup:
- Remove archived SRS documents (REQ-001~029 old structure)
- Remove archived batch documentation (BAT-001~010)
- Remove archived discovery documents (dcy-001~006 old structure)
- Remove archived release documentation (REL-001~006)

Data Model Updates:
- Add Deployment category to SRS structure
- Add Data category for new data model requirements
- Update app/cli/core/utils.py for new tag handling

Documentation:
- Create new SRS index with consolidated requirement structure
- Update docs/discovery/index.md with new discovery references
- Migrate from requirement-centric to domain-model-centric organization

Rationale:
- Existing single-portfolio model lacks admin catalog governance
- New asset catalog enables central management of tradeable symbols
- Tag system supports user-driven asset organization and discovery
- Cleaner separation of concerns between admin operations and user trading
2026-05-19 13:54:25 +00:00
genichinandClaude Sonnet 4.6 b155eeff7d fix: add missing postgres implementations for get_portfolio_by_name_and_user and insert_ledger_trade
- add get_portfolio_by_name_and_user to PortfolioRepository protocol and
  PostgresPortfolioRepository; resolves AttributeError on all CLI name+user
  resolution paths (REQ-017)
- add insert_ledger_trade to PortfolioRepository protocol and
  PostgresPortfolioRepository; resolves HTTP 500 on POST
  /v1/assets/{asset_id}/ledger/trades (REQ-008)
- add _row_to_ledger_trade static helper; uses INSERT ... ON CONFLICT DO
  NOTHING RETURNING to return (LedgerTrade, created: bool)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 04:05:48 +00:00
genichin b9e3bddb34 feat: add portfolio daemon assignment and improve list rendering
- Add --daemon-id option to portfolio update command for daemon assignment
- Update managed_by_daemon_id field in portfolio updates
- Refactor print_portfolio_table() to print_portfolio_list() for better readability
- Display daemon_id in portfolio list output when assigned
- Show portfolio tags and managed daemon information in CLI list view
- Add daemon_id support to portfolio repository and service layers
- Update schemas to include managed_by_daemon_id field in responses
2026-05-15 08:27:14 +00:00
genichin d194873616 feat: implement broker credential repository with upsert and list operations
- Add BrokerCredential to domain models and repositories
- Implement upsert_broker_credential() for credential storage and rotation
- Implement list_broker_credentials() for credential retrieval
- Implement delete_broker_credential() for credential removal
- Add broker_credentials table schema with (user_id, broker) composite key
- Implement credential version tracking and last_rotated_at timestamps
- Add FakeRepo support for broker credential CRUD operations
- Add comprehensive test coverage for broker credential lifecycle
2026-05-14 07:56:37 +00:00
genichin f4663f202e feat: add daemon CLI rendering functions and tests
- Add print_daemon_table() function to display daemon list in rich table format
- Add print_daemon_detail() function to display individual daemon information
- Update render_output() to support daemon rendering with json/table formats
- Add test for daemon create and list CLI commands with output validation
- Integrate daemon output rendering into CLI workflow
2026-05-14 06:38:21 +00:00
genichin 3bb3ff547d feat: extend daemon model with daemon status tracking and list support
- Add daemon status tracking with state and metrics (idle, running, error)
- Implement pfhub daemon list CLI command for viewing registered daemons
- Extend auth schema and service for daemon lifecycle management
- Add postgres repository queries for daemon state persistence
- Expand test coverage for daemon registry and status transitions
- Update rel-006 release document with daemon model verification results
- Support daemon graceful shutdown and error recovery patterns
2026-05-14 06:29:52 +00:00
genichin e6b6a8715b feat: implement daemon model with registry and single-asset assignment (BAT-010, REQ-023/028)
Core Implementation (BAT-010):
- Add daemon registry schema and CRUD operations (req-023)
- Implement single-daemon asset assignment constraint (one asset per daemon)
- Add daemon_id field to service_token for daemon identification
- Add daemon.py CLI module for daemon management commands
- Add 20260513_0016_daemon_model.py migration for daemon tables

API Changes:
- GET /v1/assets: include daemon_id in AssetResponse
- POST /v1/assets/{asset_id}/ledger/trades: require daemon token binding
- GET /v1/user_info: include daemon metadata
- Enhanced service-token rotate to preserve daemon bindings

CLI Commands:
- pfhub daemon register --name <name> --runtime-id <runtime-id>
- pfhub daemon list
- pfhub daemon get <daemon-id>
- pfhub daemon revoke <daemon-id>

Requirements (REQ-023, REQ-028):
- REQ-023: Daemon Registry and Service Token Daemon Binding (Interface, Approved)
  * AC-1: daemon CRUD operations
  * AC-2: service token daemon binding for POST /v1/assets/{asset_id}/ledger/trades
  * AC-3: single daemon per asset (exact-one assignment model)
- REQ-028: README.md Daemon Provisioning Runbook update (Documentation, Approved)

Documentation:
- Add dcy-006 Discovery for daemon-id and single-daemon asset assignment
- Update README.md with Daemon Provisioning Runbook section
- Add BAT-010 batch documentation with planning, implementation, verification
- Update rel-006 release document (daemon model implementation)
- Update data-model.md with daemon and daemon_asset_assignment tables
- Update interface-contract.md with daemon registry APIs
- Update runtime-flows.md with daemon lifecycle flows

Breaking Changes:
- REQ-005~009, REQ-011~012 status reverted to Approved due to revised contract
  * daemon_id field addition and exact-one asset assignment requirement
  * Existing Implemented evidence invalidated by contract revision
  * BAT-001/002/008 re-verification required

Testing:
- Add test_daemon_registry.py for daemon CRUD operations
- Add test_daemon_assignment.py for single-asset assignment constraints
- Update test_auth.py, test_cli.py, test_v1.py for daemon support
- Verify daemon token binding in trade endpoint authorization

Impact:
- Database: 2 new tables (daemons, daemon_asset_assignments)
- Migration: 20260513_0016 creates daemon model schema
- CLI: 4 new daemon subcommands
- API: daemon_id propagation through v1 endpoints
- Verification: BAT-001/002/008 re-verification gates next release
2026-05-13 23:07:24 +00:00
genichin f0a6caa178 feat: implement v1 consumer API endpoints with runtime_id support (REQ-004~010)
- Generate runtime_id for service-token issuance (CLI and API)
- Implement GET /v1/assets endpoint with asset view and strategy_params
- Implement GET /v1/user_info endpoint with trading_enabled and broker credentials
- Implement POST /v1/assets/{asset_id}/ledger/trades with idempotency support
- Update auth service to support runtime_id tracking and credential encryption
- Add comprehensive test coverage for v1 endpoints (dcy-002 implementation)
- Update postgres repository for ledger and broker credential queries
- Ensure all tests pass for feature validation
2026-05-13 07:13:03 +00:00