feat: implement portfolio UUID migration and v1 API finalization (BAT-006, BAT-001 re-verified)
Core Changes: - Migrate portfolio_id from string to UUID in schema and repository layer - Update portfolio CLI commands to use name+user combination for identification - Update HTTP API to accept portfolio_id as UUID path parameter - Implement portfolio ownership validation in repository queries Requirements Completed (REQ-016 to REQ-019): - REQ-016: portfolios table UUID schema migration with data migration script - REQ-017: CLI portfolio commands with name+user identification - REQ-018: HTTP API portfolio path parameters and v1 asset_id UUID returns - REQ-019: Portfolio UUID baseline documentation updates BAT-001 Re-verification: - REQ-004~010 marked as Implemented after verification evidence validation - All v1 API endpoints verified with comprehensive test coverage Documentation Updates: - Update data-model.md with portfolio UUID structure - Update interface-contract.md with UUID path parameters - Update runtime-flows.md with ownership-based resolution flows - Update project-structure.md with database layer changes - Add migration script for portfolio UUID transition Testing: - Add test_portfolio_uuid.py for migration validation - Update test_cli.py and test_portfolio_ownership.py for ownership checks
This commit is contained in:
+136
-3
@@ -4,8 +4,8 @@
|
||||
- Baseline Seed: .stagepilot/bootstrap/baseline.yaml
|
||||
- Status: confirmed
|
||||
- Owner: repository-maintainers
|
||||
- Last Updated (KST): 2026-04-30 22:13
|
||||
- Source Discovery / Batch: bootstrap-baseline
|
||||
- Last Updated (KST): 2026-05-11 00:00
|
||||
- Source Discovery / Batch: bootstrap-baseline, bat-001, bat-006, bat-008
|
||||
- Primary Runtime: mixed
|
||||
|
||||
## Project Summary
|
||||
@@ -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"} for readiness checks
|
||||
- GET /health payload :: returns `{"status": "ok", "service": "portfolio-hub", "version": "<semver>", "checked_at": "<ISO8601 KST>"}` for readiness checks
|
||||
|
||||
### Error Contract
|
||||
|
||||
@@ -72,6 +72,7 @@
|
||||
- pfhub subcommands :: include user, portfolio, strategy, service-token, health-check, init-db, and migrate-db command families
|
||||
- Flags and options :: include --format table|json, --force confirmations, and operation-specific mutation arguments
|
||||
- Environment configuration :: app/core/config.py resolves PORTFOLIOHUB_* and TIMESCALEDB_* values before command execution
|
||||
- Portfolio identification (bat-006) :: all portfolio mutation commands accept `<name>` (positional) + `--user <username>` instead of raw portfolio_id; `comment` and `tag` sub-commands still accept portfolio_id (UUID) directly
|
||||
|
||||
### Outputs
|
||||
|
||||
@@ -91,6 +92,136 @@
|
||||
- Write commands require explicit confirmation unless --force is present.
|
||||
- JSON output should remain machine-readable across additive command evolution.
|
||||
|
||||
## Interface: `v1 Consumer API (trex-basic)`
|
||||
|
||||
- Type: http-api
|
||||
- Consumers / Producers: trex-basic service (Consumer-Driven Contract, dcy-002)
|
||||
- Purpose: provide asset listing, user info, and trade ledger recording for the trex-basic trading daemon
|
||||
- Stability: internal (versioned under /v1)
|
||||
|
||||
### Authentication
|
||||
|
||||
- All /v1 endpoints require `Authorization: Bearer <service-token>` header.
|
||||
- Token must be a DB-issued service token (`token_source="db"`, `token_type="service"`); env tokens are rejected with HTTP 403.
|
||||
- POST /v1/assets/{asset_id}/ledger/trades additionally requires `trade-write` permission.
|
||||
|
||||
### Endpoint: `GET /v1/assets`
|
||||
|
||||
- Auth: service-token bearer (read)
|
||||
- Query Params: `?status=<value>` (optional, filters by portfolio status)
|
||||
- Response: HTTP 200
|
||||
|
||||
```
|
||||
{
|
||||
"total": <int>,
|
||||
"assets": [
|
||||
{
|
||||
"asset_id": "<UUID>", // portfolio.portfolio_id (UUID after BAT-006)
|
||||
"symbol": "<str>",
|
||||
"market": "<str|null>",
|
||||
"status": "<str>",
|
||||
"cash": <float>,
|
||||
"quantity": <float>,
|
||||
"broker_type": "<str>",
|
||||
"strategy_id": "<str|null>",
|
||||
"strategy_type": "<str|null>",
|
||||
"strategy_params": {<dict>},
|
||||
"updated_at": "<ISO8601|null>",
|
||||
"name": "<str|null>",
|
||||
"price": <float|null>,
|
||||
"profit_buffer": <float|null>,
|
||||
"tags": [<str>]|null,
|
||||
"owner_user_id": "<str|null>",
|
||||
"created_at": "<ISO8601|null>",
|
||||
"last_trade_at": "<ISO8601|null>"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
- Error: HTTP 401 if no token; HTTP 403 if not a service token
|
||||
|
||||
### Endpoint: `GET /v1/user_info`
|
||||
|
||||
- Auth: service-token bearer (read); token must have `runtime_id` assigned
|
||||
- Response: HTTP 200
|
||||
|
||||
```
|
||||
{
|
||||
"runtime_id": "<str>",
|
||||
"trading_enabled": <bool>, // mapped from users.is_active
|
||||
"disabled_reason": "<str|null>", // "account_inactive" when trading_enabled=false
|
||||
"brokers": [
|
||||
{
|
||||
"broker": "<str>",
|
||||
"account_ref": "<str>",
|
||||
"credential_payload": {<dict>}|null, // Fernet-decrypted {app_key, app_secret}
|
||||
"credential_version": <int>,
|
||||
"last_rotated_at": "<ISO8601|null>"
|
||||
}
|
||||
],
|
||||
"user_id": "<str>",
|
||||
"username": "<str>",
|
||||
"email": "<str|null>",
|
||||
"permission": "<str>"
|
||||
}
|
||||
```
|
||||
|
||||
- Error: HTTP 401 if no token; HTTP 403 if not a service token; HTTP 404 if token has no runtime_id
|
||||
|
||||
### Endpoint: `POST /v1/assets/{asset_id}/ledger/trades`
|
||||
|
||||
- Auth: service-token bearer (trade-write)
|
||||
- Path Param: `asset_id` — must be a valid UUID portfolio_id
|
||||
- Request Body:
|
||||
|
||||
```
|
||||
{
|
||||
"order_id": "<str>", // required, min_length=1
|
||||
"execution_id": "<str>", // required, min_length=1
|
||||
"side": "buy"|"sell",
|
||||
"quantity": <decimal>, // > 0
|
||||
"price": <decimal>, // > 0
|
||||
"fee_amount": <decimal>, // >= 0, default 0
|
||||
"tax_amount": <decimal>, // >= 0, default 0
|
||||
"executed_at": "<ISO8601>"
|
||||
}
|
||||
```
|
||||
|
||||
- Response HTTP 201 (new record):
|
||||
|
||||
```
|
||||
{
|
||||
"ledger_entry_id": <int>,
|
||||
"asset_id": "<str>",
|
||||
"order_id": "<str>",
|
||||
"execution_id": "<str>",
|
||||
"side": "<str>",
|
||||
"quantity": <float>,
|
||||
"price": <float>,
|
||||
"fee_amount": <float>,
|
||||
"tax_amount": <float>,
|
||||
"executed_at": "<ISO8601>",
|
||||
"recorded_at": "<ISO8601|null>",
|
||||
"status": "new"
|
||||
}
|
||||
```
|
||||
|
||||
- Response HTTP 200 (idempotent duplicate with identical payload): same body with `"status": "duplicate"`
|
||||
- Error: HTTP 401 if no token; HTTP 403 if not service token or insufficient permission; HTTP 404 if asset_id not found; HTTP 409 if duplicate key with different payload
|
||||
|
||||
### Idempotency Contract
|
||||
|
||||
- Deduplication key: `(asset_id, order_id, execution_id)` — enforced by `uq_ledger_trades_idempotency` DB unique constraint
|
||||
- Identical payload re-send → HTTP 200 + `status="duplicate"`, no new row created
|
||||
- Same key, different payload → HTTP 409 Conflict
|
||||
|
||||
### Deployment Constraint (bat-006)
|
||||
|
||||
- trex-basic and portfolio-hub must be deployed simultaneously when BAT-006 is released.
|
||||
- After BAT-006, all `asset_id` values in GET /v1/assets responses change from TEXT to UUID format.
|
||||
- trex-basic consumers must handle UUID-format asset_id before the coordinated deployment is completed.
|
||||
|
||||
## Shared Constraints
|
||||
|
||||
- Token permission levels gate both HTTP and CLI mutations consistently.
|
||||
@@ -111,4 +242,6 @@
|
||||
|
||||
## Change Log
|
||||
|
||||
- 2026-05-11 KST: bat-008 — REQ-011 AC-1: expanded v1 Consumer API section with authentication contract, detailed request/response schemas for GET /v1/assets, GET /v1/user_info, POST /v1/assets/{asset_id}/ledger/trades; updated GET /health response schema (REQ-004); added idempotency contract.
|
||||
- 2026-05-11 KST: bat-006 — Added v1 consumer API interface section (trex-basic); noted asset_id UUID change and simultaneous deployment constraint; updated CLI portfolio inputs to reflect name+user identification.
|
||||
- 2026-04-30 KST: Bootstrap baseline created from repository observation for initial StagePilot adoption.
|
||||
Reference in New Issue
Block a user