- Value↔JSON: From impls on domain Value behind `json` feature, delete 4 duplicate converters
- ConfigRepository split into ConfigRepository (12), UserRepository (3), WidgetStateCache (2)
- polling orchestration moved from bootstrap to application::polling_service
- WidgetRenderer in client-domain owns scroll/cache, both clients use it
- network loop consolidated into client-application::run_connection_loop
- protocol crate drops domain dep, Wire↔Domain conversions move to adapters
api-types: standalone crate with DTOs (widget, data source, layout, preset)
extracted from http-api. Shared between http-api and future SPA.
thiserror: replaced all manual Display impls with derive macros across
8 crates (config-sqlite, config-memory, tcp-server, tcp-client,
http-json, rss, media, application).
SQLite config store: full ConfigRepository impl with JSON serialization
for mappings, layouts, data source configs. 12 integration tests.
HTTP API: Axum REST endpoints for widgets, data sources, layout, presets.
6 integration tests using tower::oneshot.
Port traits updated to return Send futures for Axum compatibility.