KMPCharts/.paul/ROADMAP.md
Trentin Davide af6814e2b8 feat(chart): v0.5.0 architecture + interaction
Ships v0.5.0 via kmp-manager 6-phase flow. 13 plan tasks (D1, T1-T10, D3, D4),
14 agent calls, 6 P-groups. 107 → 207 tests on iosSimulatorArm64.

Toolchain (D1):
- Kotlin 2.1.0 → 2.3.21
- Compose-Multiplatform 1.8.0 → 1.11.0
- Android Gradle Plugin 8.7.3 → 9.2.0
- Gradle 8.11.1 → 9.5.1
- coroutines 1.9.0 → 1.11.0, kotlin-test → 2.3.21
- Drop kotlinx-datetime; use stdlib kotlin.time.Clock
- Drop iosX64 target (Compose-MP 1.11.0 has no ios_x64 variant)

Architecture (T1-T6, T10):
- TieredBuffer.snapshotWindow: bisect-based windowed snapshot
- New lod/ package: LodStrategy interface + MinMax/Lttb/MinMaxLttb impls
  (MinMaxLttb SOTA per arXiv 2305.00332, 1.80× faster than pure LTTB)
- New render/SignalRenderer: public interface + LineSignalRenderer object
- New render/AxisFormatter: 4 default impls (Time, Decimal, DateTime, Unit)
- HARD BREAK: deleted LodMode, LodDecimator, ChartConfig.targetFps
- ChartConfig split: DataConfig + AxisConfig + RenderConfig + FrameRate sealed
- @Immutable/@Stable on all public types (0 unstable)
- RealtimeChartState.clear() API

Interaction layer (T7):
- New interaction/ package
- ChartInteractionState + rememberChartInteractionState()
- ViewportMode sealed: Following / Frozen / History(anchorMs)
- Pinch zoom + drag pan + tap crosshair gestures
- Swipe-to-edge resumes Following
- InverseProjection: pixel → ms + bisect nearest-sample

Perf finishing (T8, T9, D3):
- LineSignalRenderer Stroke cache, AxisRenderer TextStyle cache
- resolveYRange Pair<Float,Float> → FloatArray out-param
- RealtimeChartState.signalsArray cached (invalidated on add/remove only)
- LTTB upper-bound aligned to half-open [start, start+windowMs) semantic

Correctness (D4):
- NumberFormat.formatFixed Long overflow guard @ |v|≥1e19

ABI baseline regenerated:
- chart-realtime.api: 161 → 428 LOC
- chart-realtime.klib.api: 211 → 501 LOC

Modules touched: chart-realtime (lib), app (consumer), gradle (toolchain),
.paul (state).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 00:05:43 +02:00

9.3 KiB
Raw Blame History

Roadmap: chart-realtime

Overview

KMP real-time chart library. Six phases v0.1.0 MVP shipped; v0.2.0 polish shipped.

Milestones

Milestone Version Status Completed
v0.1.0 MVP 0.1.0 ✓ Shipped 2026-05-20
v0.2.0 Polish 0.5.0-SNAPSHOT ✓ Shipped 2026-05-21
v0.3.0 iOS Build Pipeline 0.5.0-SNAPSHOT ✓ Shipped 2026-05-21
v0.4.0 Portfolio Hardening 0.5.0-SNAPSHOT ✓ Shipped 2026-05-21
v0.5.0 Architecture + Interaction 0.5.0-SNAPSHOT ✓ Shipped 2026-05-21
v1.0.0 Public Release - Planned -

v0.1.0 — MVP (SHIPPED)

Status: ✓ Complete
Phases: 6 of 6 complete

Phases

Phase Name Status Completed
1 Module Scaffold ✓ Done 2026-05-20
2 Buffer + LoD ✓ Done 2026-05-20
3 State API ✓ Done 2026-05-20
4 Render Engine ✓ Done 2026-05-20
5 Demo App ✓ Done 2026-05-20
6 Polish + Docs ✓ Done 2026-05-20

Delivered: CircularBuffer, LodDecimator, RealtimeChart composable, basic AxisRenderer.


v0.2.0 — Polish (SHIPPED as v0.5.0-SNAPSHOT)

Status: ✓ Complete
Summary: .paul/phases/v0.2.0-plan-SUMMARY.md

Delivered

  • TieredBuffer (3-tier 1h ring buffer, MIN_MAX binning, zero-alloc)
  • LodMode enum (MIN_MAX, LTTB) — MEAN removed
  • LTTB render-time decimation (O(n) Largest-Triangle-Three-Buckets)
  • AxisLabelMode (INSIDE/BESIDE/HIDDEN) with chartLeft/chartBottom insets
  • T0-relative X-axis timestamps with mm:ss/hh:mm:ss formatting, negative support
  • Material3 dynamic theming (light/dark, wallpaper-based, API 31+)
  • Camera cutout / edge-to-edge support
  • Sensor domain layer (Clean Architecture)
  • Koin 4.0.0 DI wiring
  • Package rename: eu.henesis → dev.dtrentin
  • Git remote: ssh://git@3nt-git.duckdns.org:222/davide.trentin/KMPCharts.git
  • Sensor rate cap: 200Hz (5_000μs)

v0.3.0 — iOS Build Pipeline (SHIPPED)

Status: ✓ Complete Plan: .paul/phases/v0.3.0-ios-build-plan.md

Delivered

  • 3 iOS targets compile green (iosX64, iosArm64, iosSimulatorArm64) — iosX64 dropped in v0.5.0 D1 (Compose-MP 1.11.0 no ios_x64 variant)
  • applyDefaultHierarchyTemplate() wires iosMain/iosTest across targets
  • linkDebugFrameworkIosSimulatorArm64 green
  • iosSimulatorArm64Test green — 33 tests, 0 failures
  • XCFramework ChartRealtime produced (debug + release): ios-arm64 + ios-arm64_x86_64-simulator slices
  • Pre-existing compile bugs fixed:
    • @Volatilekotlin.concurrent.Volatile import
    • @JvmNamekotlin.jvm.JvmName import (OptionalExpectation)
    • String.format in AxisRenderer → manual formatFixed/formatScientific/pow10 helpers
    • NSDate.date()NSDate() + timeIntervalSinceReferenceDate + 1970 epoch offset
  • Android non-regression confirmed (assembleRelease, publishToMavenLocal --dry-run)
  • Local Mac config: removed Linux JDK pin from gradle.properties, added local.properties for Android SDK

v0.4.0 — Portfolio Hardening (SHIPPED)

Plan: .paul/phases/v0.4.0-portfolio-hardening-plan.md Source: 3-reviewer audit 2026-05-21 Executed via: kmp-manager group-by-group flow, 8 P-groups, ~15 agent calls Completed: 2026-05-21

Delivered

Critical fixes:

  • LICENSE MIT (T1)
  • explicitApi() Strict + BCV 0.16.3 + klib enabled (T2) — ABI baseline 161/211 LOC
  • Internal lockdown of CircularBuffer / TieredBuffer / LodDecimator / dataVersion / rememberFrameTick (T3)
  • kotlinx-datetime 0.6.2 replaces expect/actual currentTimeMs (T4) — Platform.* 3 files deleted
  • CircularBuffer.writeIndex Int→Long (T5) — overflow 124d @ 200Hz fixed
  • @Suppress + !! removed from commonMain (T6)
  • Dead config removed: maxBufferSeconds, xLabelDecimals, SignalConfig.label (T7)

Correctness:

  • POSITIVE_INFINITY/Float.MAX_VALUE sentinels → hasData boolean flag (T8)
  • Snapshot.withMutableSnapshot + SnapshotApplyConflictException retry in push/addSignal/removeSignal (T9)
  • dataVersion mutableLongStateOf — Compose-observable, idle = 0 recompositions (T10)
  • RenderLoop.kt deleted; targetFps @Deprecated (T6/T10)
  • Single snapshot per signal per frame via per-signal scratch arrays in SignalEntry (T11)
  • M4 binning Tier1/Tier2: firstTs/V, minTs/V, maxTs/V, lastTs/V with chronological dedup (T12) — no C7 spikes
  • NaN/Inf/backward-timestamp guards on push, KDoc contract, per-signal lastPushedTs (T13)

Test gap fill:

  • NumberFormat extracted from AxisRenderer to internal object (T14) — 38 tests
  • TieredBufferTest +15 tests (tier roll, window crossing, clear, edge cases) (T15)
  • LodDecimatorTest +10 LTTB-specific tests (T15)
  • iosSimulatorArm64Test: 33 → 107 tests (target was ≥60)

Capacity changes:

  • TIER1_CAPACITY 12k → 24k (×2 → ×4)
  • TIER2_CAPACITY 5.4k → 10.8k (×2 → ×4)
  • TOTAL_CAPACITY 77.4k → 94.8k
  • Memory per signal: ~1.14 MB (was ~760 KB pre-bump)

Deviations / deferred to v0.5.0:

  • kotlinx-datetime 0.6.2 (not 0.7.x) — Kotlin 2.1.0 compat
  • ChartConfig.targetFps @Deprecated but kept for ABI stability
  • NumberFormat Long overflow @ 1e19 (safe via current routing)
  • LTTB upper-bound inclusive vs snapshot half-open semantic mismatch

v0.5.0 — Architecture + Interaction (SHIPPED)

Plan: .paul/phases/v0.5.0-architecture-plan.md (v2 deltas) Executed via: kmp-manager 6-phase flow, 6 P-groups, ~14 agent calls Completed: 2026-05-21

Delivered

Toolchain (D1):

  • Kotlin 2.1.0 → 2.3.21
  • Compose-Multiplatform 1.8.0 → 1.11.0
  • Android Gradle Plugin 8.7.3 → 9.2.0
  • Gradle 8.11.1 → 9.5.1
  • coroutines 1.9.0 → 1.11.0, kotlin-test → 2.3.21
  • Dropped kotlinx-datetime; swapped to stdlib kotlin.time.Clock (Kotlin 2.3 stable)
  • Dropped iosX64 target (Compose-MP 1.11.0 incompat — Intel Mac sims deprecated)

Architecture (T1-T6, T10):

  • T1: Bisect tier snapshot (TieredBuffer.snapshotWindow) — 26 new tests
  • T2: LodStrategy interface + MinMaxLodStrategy + LttbLodStrategy + MinMaxLttbLodStrategy (SOTA, 1.80× faster than pure LTTB on iOS sim; arXiv 2305.00332 ratio=4)
  • T3: SignalRenderer interface (public) + LineSignalRenderer default impl
  • T4: AxisFormatter interface + TimeAxisFormatter + DecimalAxisFormatter + DateTimeAxisFormatter + UnitAxisFormatter
  • T5: HARD BREAK — deleted LodMode enum + LodDecimator + ChartConfig.targetFps. Split ChartConfigDataConfig + AxisConfig + RenderConfig + FrameRate sealed
  • T6: @Immutable/@Stable annotations — 0 unstable public types
  • T10: RealtimeChartState.clear() API

Interaction layer (T7):

  • ChartInteractionState + rememberChartInteractionState() hoistable state
  • ViewportMode sealed: Following / Frozen / History(anchorMs)
  • CrosshairState + SignalValueAt (List, not Map — zero boxing)
  • InteractionConfig (zoomEnabled/panEnabled/tapCrosshairEnabled + clamps)
  • Pinch zoom + drag pan + tap crosshair + swipe-to-edge-resumes-Following
  • InverseProjection: pixel→ms + bisect-based nearest-sample

Perf finishing (T8, T9, D3):

  • T8: LineSignalRenderer.Stroke cache + AxisRenderer.TextStyle cache (16-entry cap, single-thread UI)
  • T9: Pair<Float,Float> removed (FloatArray out-param) + signalsArray cached on RealtimeChartState (invalidated by addSignal/removeSignal only)
  • D3: LTTB upper-bound aligned to half-open [start, start+windowMs) snapshot semantic

Correctness (D4):

  • D4: NumberFormat.formatFixed Long overflow guard @ |v|≥1e19 → routes to scientific

Test gap fill:

  • 107 → 207 tests (+100)
  • New suites: lod/ (24 tests), interaction/ (29 tests), model/ChartConfigTest (8), render/AxisFormatterTest (10), render/AxisRendererTest (3), render/LineSignalRendererTest (2)

ABI:

  • Baseline regenerated; apiCheck GREEN
  • chart-realtime.api: 161 → 428 LOC
  • chart-realtime.klib.api: 211 → 501 LOC

Deviations from plan:

  • iosX64 target dropped (Compose-MP 1.11.0 dropped Intel iOS sim variant)
  • MinMaxLttb perf 1.80× (paper claims 10× — gap = scalar Kotlin/Native vs Rust+SIMD; ≥1× acceptance met)
  • signalsArray cache NOT invalidated by clear() (entries preserved per T10 contract)

Highlights vs plan goals:

  • Interaction layer S0 shipped (was "fatale senza")
  • HARD BREAK on T2/T5 per intake decision (no deprecated bridges)
  • All v0.4.0 deferred items (D1/D3/D4) absorbed

v1.0.0 — Public Release (PLANNED)

Plan: .paul/phases/v1.0.0-public-release-plan.md Effort: 2-3 giorni full-time + manual ops, ~10 agent calls Prereq: v0.4.0 + v0.5.0 shipped

Highlights

  • Maven Central publishing (dev.dtrentin:chart-realtime:1.0.0)
  • GitHub Actions CI (Android + iOS)
  • SwiftUI iOS demo app consuming XCFramework
  • Benchmark vs Vico/KoalaPlot/MPAndroidChart con numeri reali
  • README professionale: GIF + badge + benchmark + onesta comparison table
  • CHANGELOG.md + CONTRIBUTING.md + CODE_OF_CONDUCT.md
  • Performance budget docs (frame time, memory, battery)
  • Logo + banner + demo GIF
  • Tag v1.0.0 + GitHub release con artifact

Backlog (post v1.0.0)

  • Multi-Y axis / log scale
  • Annotations / threshold lines / regions
  • Headless data export (PNG/CSV)
  • CocoaPods publication
  • Swift Package Manager (SPM)
  • Wasm + desktop (jvm) targets
  • ScatterRenderer / AreaRenderer / BarRenderer (sfrutta SignalRenderer interface)
  • Legend composable

Roadmap updated: 2026-05-21 — post 3-reviewer audit