KMPCharts/.paul/ROADMAP.md
Trentin Davide 0e3b79e2da feat(chart): v0.4.0 portfolio hardening
Ship 15-task plan via kmp-manager group-by-group flow. Library moves
from "prototype" to "senior-engineer-built KMP library" per 3-reviewer
audit (android-reviewer + mobile-performance-reviewer + mobile-architect).

Critical fixes:
- LICENSE MIT (T1) — legal blocker resolved
- explicitApi() Strict + BCV 0.16.3 + klib enabled (T2) — ABI locked
  baseline: chart-realtime.api 161 LOC + chart-realtime.klib.api 211 LOC
- Internal lockdown: CircularBuffer, TieredBuffer, LodDecimator,
  dataVersion, rememberFrameTick (T3)
- kotlinx-datetime 0.6.2 replaces expect/actual currentTimeMs (T4)
  Platform.kt + .android.kt + .ios.kt deleted
- CircularBuffer.writeIndex Int -> Long (T5) — fix 124d overflow @ 200Hz
- Remove !! and @Suppress("UNUSED_EXPRESSION") from commonMain (T6)
- Remove dead config: maxBufferSeconds, xLabelDecimals,
  SignalConfig.label (T7)

Correctness:
- POSITIVE_INFINITY/Float.MAX_VALUE sentinels -> hasData flag (T8)
- Snapshot.withMutableSnapshot + SnapshotApplyConflictException retry
  in push/addSignal/removeSignal (T9)
- dataVersion mutableLongStateOf — Compose-observable, idle = 0
  recompositions (T10) — RenderLoop.kt deleted, targetFps @Deprecated
- Single buffer.snapshot() per signal per frame via SignalEntry
  per-signal scratch arrays (T11) — render-time snapshot cost halved
- M4 binning Tier1/Tier2: firstTs/V + minTs/V + maxTs/V + lastTs/V
  with chronological dedup (T12) — no C7 vertical spike artifacts.
  TIER1/TIER2 CAPACITY x2 -> x4 (TOTAL 77.4k -> 94.8k)
- NaN/Inf/backward-timestamp guards on push (T13) — silent drop,
  per-signal lastPushedTs tracking, KDoc contract

Test gap fill:
- NumberFormat extracted from AxisRenderer to internal object (T14)
  +38 tests pinning rounding/scientific/time-format behavior
- TieredBufferTest +15 tests: tier roll, window-crossing, clear,
  edge cases (T15)
- LodDecimatorTest +10 LTTB-specific branch coverage tests (T15)

Test count: 33 -> 107 (+74).
ABI: 211/272 -> 161/211 LOC (locked, baseline committed).

Deferred to v0.5.0:
- Bump kotlinx-datetime 0.6.2 -> 0.7.x (needs Kotlin 2.1.20+)
- Remove ChartConfig.targetFps (currently @Deprecated, ignored)
- NumberFormat Long overflow @ 1e19 (safe via current routing)
- LTTB upper-bound inclusive vs snapshot half-open semantic mismatch
- Macrobench setup for -30% frame-time verification

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

181 lines
6.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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 | - | Planned | - |
| 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)
- `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:
- `@Volatile``kotlin.concurrent.Volatile` import
- `@JvmName``kotlin.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 (PLANNED)
Plan: .paul/phases/v0.5.0-architecture-plan.md
Effort: 5-7 giorni full-time, ~12 agent calls
Prereq: v0.4.0 shipped
### Highlights
- Interaction layer S0 (pinch zoom + drag pan + crosshair tap) — fatale senza
- LodStrategy/SignalRenderer/AxisFormatter interfaces (apri estensibilità)
- MinMaxLTTB SOTA algorithm (10x faster than LTTB puro, arXiv 2305.00332)
- Bisect tier snapshot per timestamp window (30x perf win)
- ChartConfig split DataConfig/AxisConfig/RenderConfig
- FrameRate sealed (replace `targetFps: Int?` nullable-as-sentinel)
- Compose @Stable/@Immutable annotations
- Cache Stroke + TextStyle + TextLayoutResult (zero-alloc render reale)
- Replace Pair<Float,Float> return + Map iter alloc
- RealtimeChartState.clear() API
---
## 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*