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>
168 lines
8.8 KiB
Markdown
168 lines
8.8 KiB
Markdown
# Portfolio Hub Project Structure
|
|
|
|
- Baseline Mode: observed
|
|
- Baseline Seed: .stagepilot/bootstrap/baseline.yaml
|
|
- Status: confirmed
|
|
- Owner: repository-maintainers
|
|
- Last Updated (KST): 2026-05-20 00:00
|
|
- Source Discovery / Batch: bootstrap-baseline, bat-001, bat-002, bat-006, bat-007, bat-003, bat-004, bat-005
|
|
|
|
## Project Summary
|
|
- PortfolioHub exposes portfolio management and trading ledger workflows through a FastAPI service and a companion CLI.
|
|
|
|
## Primary Domain
|
|
- Portfolio management and trading ledger services.
|
|
|
|
## Tech Stack
|
|
- Python 3.12
|
|
- FastAPI
|
|
- Typer
|
|
- Pydantic Settings
|
|
- Psycopg
|
|
- Alembic
|
|
- PostgreSQL/TimescaleDB
|
|
- Docker Compose
|
|
|
|
## Primary Runtime
|
|
- Type: mixed
|
|
- Planned Entry Points:
|
|
- uvicorn app.main:app :: serve the authenticated HTTP API, OpenAPI docs, and startup bootstrap hooks
|
|
- pfhub :: run operational CLI commands for health, users, portfolios, strategies, tags, service tokens, and broker credentials
|
|
- alembic upgrade head :: apply ordered database schema migrations for persistent environments
|
|
|
|
## Purpose
|
|
- This document is the current approved repository and package structure baseline.
|
|
- It is a cross-cutting reference document for Discovery, REQ, Batch Design, Batch Verification, and implementation updates.
|
|
|
|
## Scope
|
|
- Covers the top-level repository layout and package or module boundaries.
|
|
- Does not duplicate detailed business rules, acceptance criteria, or implementation-only function signatures.
|
|
|
|
## Top-Level Structure
|
|
|
|
```text
|
|
.
|
|
|-- .github/
|
|
| |-- instructions/
|
|
| |-- prompts/
|
|
| |-- runbooks/
|
|
| |-- scripts/
|
|
| |-- skills/
|
|
| `-- templates/
|
|
|-- .stagepilot/
|
|
| `-- bootstrap/
|
|
|-- .vendor/
|
|
| `-- stage-pilot/
|
|
|-- app/
|
|
| |-- api/
|
|
| |-- cli/
|
|
| |-- core/
|
|
| |-- db/
|
|
| |-- domain/
|
|
| |-- repositories/
|
|
| |-- schemas/
|
|
| `-- services/
|
|
|-- docs/
|
|
| |-- batches/
|
|
| |-- discovery/
|
|
| |-- releases/
|
|
| `-- srs/
|
|
|-- migrations/
|
|
| `-- versions/
|
|
|-- tests/
|
|
|-- temp/
|
|
|-- README.md
|
|
|-- alembic.ini
|
|
|-- docker-compose.local-test.yml
|
|
`-- pyproject.toml
|
|
```
|
|
|
|
## Package / Module Responsibilities
|
|
|
|
- .github/
|
|
- Hosts StagePilot skills, document templates, and helper scripts such as stagepilot-doctor and stagepilot-bootstrap-seed.
|
|
- app/api/
|
|
- Defines FastAPI route surfaces for health, authentication, users, strategy sets, portfolios, and tags.
|
|
- v1 route surfaces: `v1_assets.py` (GET /v1/assets token-branched, POST /v1/assets admin catalog, PATCH /v1/assets/{id}, PATCH /v1/assets/{id}/deactivate, POST/DELETE /v1/assets/{id}/tags), `v1_tags.py` (POST/GET /v1/tags, PATCH /v1/tags/{id}/deactivate), `v1_portfolios.py` (POST/DELETE /v1/portfolios/{id}/tags), `v1_user_info.py` (GET /v1/user_info), `v1_ledger.py` (POST /v1/assets/{asset_id}/ledger/trades).
|
|
- GET /v1/assets supports service/user/admin token branching; admin catalog CRUD requires admin token; tag endpoints added by BAT-004.
|
|
- app/cli/
|
|
- Defines the Typer entrypoint, command groups, output helpers, and operator-facing command flows.
|
|
- Command groups: `asset` (catalog CRUD via admin), `portfolio`, `strategy`, `tag` (tag master CRUD and apply/remove; BAT-005), `user`, `service-token`, `credential` (broker credential management), and health commands (`health-check`, `init-db`, `migrate-db`).
|
|
- `app/cli/commands/tag.py` (BAT-005): `pfhub tag create/list/deactivate/apply asset|portfolio/remove asset|portfolio`; direct repo method calls following existing AppContext pattern. `pfhub portfolio tag add/list/remove` removed (breaking change, BAT-005).
|
|
- app/core/
|
|
- Owns configuration loading, auth guards, security helpers, and shared error handling.
|
|
- `encryption.py`: Fernet-based credential payload encrypt/decrypt helpers. Reads `CREDENTIAL_ENCRYPTION_KEY` env var via `Settings.require_credential_encryption_key()`.
|
|
- app/domain/
|
|
- Holds framework-light domain models shared by services, routes, and repositories.
|
|
- app/repositories/
|
|
- Provides the repository protocol, repository factory, and concrete Postgres persistence implementation.
|
|
- Key methods added in bat-006: `get_portfolio_by_name_and_user(owner_user_id, name) -> Portfolio | None` for (owner_user_id, name) → portfolio_id (UUID) resolution used by CLI commands.
|
|
- app/schemas/
|
|
- Defines request and response schemas for HTTP contracts.
|
|
- `v1.py`: Contract schemas for v1 API (AssetItem, AssetsResponse, UserInfoResponse, BrokerItem, LedgerTradeRequest/Response, AssetCatalogCreateRequest, AssetCatalogUpdateRequest, AssetCatalogResponse, TagItem, TagCreateRequest, TagListResponse, TagApplyRequest, TagApplyResponse; tag schemas added by BAT-004).
|
|
- app/services/
|
|
- Implements business logic for auth, portfolios, strategy sets, and users.
|
|
- migrations/
|
|
- Stores Alembic environment files and ordered schema revisions for persistent database changes.
|
|
- tests/
|
|
- Covers API behavior, auth flows, CLI behavior, domain services, portfolio comments, tags, ownership, and users.
|
|
- docs/
|
|
- Stores active SDLC indices and cross-cutting baseline documents.
|
|
|
|
## Dependency Rules
|
|
|
|
- Interface layers in app/api/ and app/cli/ should reach persistence through app/services/ and app/repositories/factory.py rather than importing database code directly.
|
|
- app/domain/models.py remains the shared contract between services, repositories, and interface adapters.
|
|
- app/repositories/postgres.py owns SQL details; migrations/versions/ owns ordered schema evolution history.
|
|
- app/core/config.py is the shared configuration source for both the FastAPI app and the Typer CLI.
|
|
- tests/ should prefer public API, CLI, or service entrypoints over asserting against private persistence details unless a regression requires it.
|
|
|
|
## Shared Boundaries
|
|
|
|
- PortfolioRepository is the abstraction boundary between business logic and concrete persistence.
|
|
- Settings and auth primitives are shared across API requests, CLI commands, and startup bootstrap behavior.
|
|
- docs/ holds SDLC source-of-truth artifacts, while temp/ remains scratch or legacy material outside the active document flow.
|
|
- .vendor/stage-pilot/ is a vendored upstream toolkit and should not be treated as the active host source of truth.
|
|
|
|
## Schema Ownership Boundary
|
|
|
|
`app/repositories/postgres.py`의 `ensure_schema()`와 `migrations/versions/` Alembic revision 양쪽이 일부 DB 객체를 다룬다.
|
|
|
|
### 소유 경계
|
|
|
|
| 소유 경로 | 관리 객체 | 비고 |
|
|
|-----------|-----------|------|
|
|
| ensure_schema() 전용 | `idx_auth_tokens_service_name_unique` (partial index) | Alembic revision에 없음 |
|
|
| Alembic 전용 | `portfolios.market`, `auth_tokens.runtime_id`, `idx_auth_tokens_runtime_id_unique`, `broker_credentials`, `ledger_trades` | ensure_schema()에 없음 — v1 API 전제 조건 |
|
|
| 양쪽 공유 (IF NOT EXISTS 보호) | users, auth_tokens, strategy_sets, portfolios, portfolio_ledger, portfolio_comments, portfolio_tags + 대부분의 인덱스 | ensure_schema() 우선 실행 후 Alembic 실행 시 충돌 없음 |
|
|
|
|
### 허용 실행 순서
|
|
|
|
1. `init-db` 선행 → App 시작 → 정상 운영 (권장)
|
|
2. App 시작 (ensure_schema() 선행) → `migrate-db` 후속 실행 (legacy stamp 경로)
|
|
3. 이미 Alembic 관리 DB → `migrate-db` 증분 upgrade
|
|
|
|
### 운영 제약
|
|
|
|
- ensure_schema()만 실행한 환경은 v1 API 스키마(`broker_credentials`, `ledger_trades`, `runtime_id`)가 없어 v1 엔드포인트가 동작하지 않는다. `migrate-db`로 보완 필요.
|
|
- `alembic_version` 테이블을 수동으로 삭제한 뒤 `migrate-db`를 재실행하면 legacy stamp 오판이 발생할 수 있다.
|
|
- legacy DB(bot_principals 등 구 revision 0006~0013 구조)와 신규 revision 간 호환성 검증은 이번 batch 범위 밖이다.
|
|
|
|
## Current Gaps / Planned Changes
|
|
|
|
- The non-DB repository path intentionally raises a feature-not-ready error when persistence is disabled.
|
|
- Redis and Loki settings exist, but their runtime ownership and module boundaries are not yet represented in active docs.
|
|
- Broker credential write path (upsert) is available only via CLI; no public HTTP API exists for credential registration yet.
|
|
|
|
## Update Triggers
|
|
|
|
- Update this document when package boundaries change.
|
|
- Update this document when new top-level runtime or domain modules are added.
|
|
- Update this document when a batch changes the approved structure baseline.
|
|
|
|
## Change Log
|
|
|
|
- 2026-05-20 KST: bat-005 — REQ-008 AC-3: added app/cli/commands/tag.py (pfhub tag command group); updated app/api/ to reflect v1_tags.py, v1_portfolios.py, v1_assets.py tag endpoints; updated app/schemas/v1.py tag schemas; updated pfhub entry point (tags); noted pfhub portfolio tag removal.
|
|
- 2026-05-11 KST: bat-007 — Added "Schema Ownership Boundary" section documenting ensure_schema() vs Alembic ownership zones, allowed/forbidden execution sequences, and legacy scope exclusion.
|
|
- 2026-05-11 KST: bat-006 — Added get_portfolio_by_name_and_user() to repositories section; noted that portfolio_id is now a server-generated UUID.
|
|
- 2026-04-30 KST: Bootstrap baseline created from repository observation for initial StagePilot adoption. |