Files
vault-dash/docs/CORE-001D_BOUNDARY_CLEANUP_PLAN.md
2026-03-25 10:33:10 +01:00

4.5 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-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

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