5.0 KiB
5.0 KiB
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-001ADecimal/unit foundationsCORE-001Boverview + hedge migrationCORE-001Cbacktests + 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.jsonpayloads
Current state:
PortfolioConfigstores float fieldsPortfolioRepository.save/loadround-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.pyapp/services/settings_status.pyapp/services/alerts.py
Current state:
LombardPortfolioremains 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.pyapp/services/backtesting/comparison.pyapp/services/event_comparison_ui.pyapp/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.pyapp/services/price_feed.pyapp/services/data_service.pyapp/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:
PortfolioConfigJSON 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
PortfolioConfigpersistence 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
Pre-launch rollout policy
For the current pre-launch stage, the storage schema may make a clean breaking transition.
That means:
- newly persisted numeric domain values should use explicit structured unit-aware storage
- old flat storage payloads do not need compatibility or migration yet
- invalid or old-format payloads should fail loudly instead of being silently normalized
A real migration path should be introduced later, once persistence is considered live for users.