docs: scope decimal boundary cleanup

This commit is contained in:
Bu5hm4nn
2026-03-25 10:33:10 +01:00
parent b1e5cbd47e
commit 8270e2dcbb
2 changed files with 132 additions and 0 deletions

View File

@@ -0,0 +1,129 @@
# CORE-001D Boundary and Persistence Cleanup Plan
## Goal
Make Decimal/unit-safe domain types reliable at external boundaries without forcing a risky full-model rewrite.
This slice follows:
- `CORE-001A` Decimal/unit foundations
- `CORE-001B` overview + hedge migration
- `CORE-001C` backtests + event comparison migration
## Why this exists
The visible math paths now use unit-safe domain helpers, but several persistence and adapter seams still store or pass raw floats. That is acceptable at the edge for now, but the boundaries should be explicit, tested, and stable.
## Main hotspots found in the current codebase
### 1. Portfolio persistence still serializes raw floats
Files:
- `app/models/portfolio.py`
- workspace-backed `portfolio_config.json` payloads
Current state:
- `PortfolioConfig` stores float fields
- `PortfolioRepository.save/load` round-trips plain JSON numbers
Risk:
- persistence format is implicit
- Decimal-safe internal math can be silently rounded or reinterpreted at reload boundaries
### 2. Legacy portfolio/domain types still expose float-heavy APIs
Files:
- `app/models/portfolio.py`
- `app/services/settings_status.py`
- `app/services/alerts.py`
Current state:
- `LombardPortfolio` remains float-based for compatibility
- several services convert Decimal-backed results back to float for formatting/threshold checks
Risk:
- domain-safe calculations exist, but callers can still drift into ambiguous float semantics
### 3. Backtesting UI/service seams still take float inputs
Files:
- `app/services/backtesting/ui_service.py`
- `app/services/backtesting/comparison.py`
- `app/services/event_comparison_ui.py`
- `app/domain/backtesting_math.py`
Current state:
- typed materialization exists, but service entrypoints still accept `float`
- conversions back to float happen for model compatibility
Risk:
- callers can bypass intent and reintroduce unit ambiguity at service boundaries
### 4. Provider/cache adapters use generic JSON and float payloads
Files:
- `app/services/cache.py`
- `app/services/price_feed.py`
- `app/services/data_service.py`
- `app/services/backtesting/historical_provider.py`
Current state:
- cache serialization supports datetime only via custom default
- provider payloads are mostly raw floats, dicts, and lists
Risk:
- external payloads are fine to keep float-heavy, but conversion into domain-safe structures should happen at named boundaries and be test-covered
## Recommended implementation order
### Step 1: make persistence format explicit
Target:
- `PortfolioConfig` JSON shape
- workspace portfolio JSON shape
Deliverables:
- explicit serialization helpers for persisted money/price/weight-like fields
- tests proving stable round-trip behavior
- docs for JSON number vs string decisions
Preferred near-term approach:
- keep external JSON ergonomic
- document exact persisted field meanings and units
- ensure reload path normalizes through a single constructor/adapter
### Step 2: add named boundary adapters
Target:
- portfolio persistence load/save
- price feed quote ingestion
- historical close ingestion
- options-chain normalization
Deliverables:
- helper functions with explicit names such as `*_from_provider_payload(...)` or `*_to_persistence_dict(...)`
- tests proving conversion behavior and fail-closed validation
### Step 3: reduce raw-float service entrypoints where practical
Target:
- backtesting UI/comparison service inputs
- settings/alerts status helpers
Deliverables:
- services accept typed or normalized values earlier
- float conversion, where still required, happens at the last compatibility seam
## Non-goals
- replacing every float in every Pydantic/dataclass immediately
- redesigning third-party payload models wholesale
- changing public UI formatting behavior just for type purity
## First candidate sub-slices
### CORE-001D1 — Portfolio persistence serialization seam
- make `PortfolioConfig` persistence round-trip explicit
- add serialization tests for workspace-scoped config files
### CORE-001D2 — Provider and cache adapter boundaries
- document/test cache + provider conversion seams
- ensure raw external floats are normalized before domain math
### CORE-001D3 — Service entrypoint tightening
- narrow float-heavy internal service APIs where easy and low-risk
## Success criteria
- persistence schema for bookkeeping-sensitive fields is explicit and tested
- Decimal/unit-safe values cross boundaries through named adapters
- remaining float-heavy hotspots are either removed or intentionally documented as edge-only
- no regression in existing browser-visible flows

View File

@@ -13,3 +13,6 @@ acceptance_criteria:
- Decimal-bearing JSON/API serialization is documented and tested.
- Float-heavy integrations have named conversion boundaries.
- Remaining raw-float domain hotspots are identified or removed.
technical_notes:
- Prioritize portfolio/workspace persistence, provider normalization, and cache serialization seams.
- See `docs/CORE-001D_BOUNDARY_CLEANUP_PLAN.md` for the current hotspot inventory and proposed sub-slices.