KMPCharts/.paul/STATE.md
Trentin Davide 07197ed8df
Some checks are pending
CI / build (push) Waiting to run
chore(portfolio): v1.0.0 prep — docs, CI, demos
Autonomous portfolio prep batch. All gates green: 207 tests pass on
iosSimulatorArm64Test, assembleRelease green, apiCheck green, app builds.

Docs:
- CHANGELOG.md (Keep-a-Changelog, retroactive v0.1-v0.5)
- CONTRIBUTING.md (build + style + PR workflow + perf rules)
- chart-realtime/README.md rewrite for v0.5.0 API surface
- docs/PERFORMANCE.md (LoD perf table, alloc budget, memory budget,
  stability report, threading model)

CI (.github/):
- workflows/ci.yml — macos-latest, JDK 21, konan cache, full gate
- workflows/release.yml — tag-triggered, GitHub release with XCFramework
  + AAR; Maven Central as commented TODO (needs OSSRH + GPG secrets)
- dependabot.yml — weekly gradle + actions updates
- pull_request_template.md + 2 issue templates (bug, feature)

Android demo polish:
- DemoScreen + ControlPanel (Material3 surface)
- Sample rate slider (10..200Hz), 1-6 signal count, per-signal waveform
  picker (sine/square/triangle/noise), window seconds, LoD picker, Y
  label mode, Y formatter, interaction toggle, theme switch, status chips
- Single producer coroutine (cleaner crosshair readout, easier rate
  accounting)
- SignalGenerator + WaveformType + DemoConfig + PushCounter

iOS demo scaffold (samples/ios/ChartRealtimeDemo/):
- SPM Package.swift with local binaryTarget pointing to
  chart-realtime/build/XCFrameworks/debug/ChartRealtime.xcframework
- DemoApp.swift + DemoView.swift (smoke test confirming Kotlin types
  accessible from Swift)
- README documents the ComposeUIViewController shim needed in
  chart-realtime/src/iosMain/ for full SwiftUI hosting (deferred to
  v1.0.0 T1)
- xcodebuild against iOS Simulator builds green

Debt:
- gradle.properties: removed kotlin.internal.klibs.non-packed=false
  (Compose-MP 1.11.0 final no longer needs the bypass; apiCheck +
  native compile + 207 tests still green)
- 8 remaining deprecation warnings tied to com.android.library + KMP
  plugin combo; clearing them requires migration to
  com.android.kotlin.multiplatform.library 9.2.0 which forces ABI
  baseline regen + Maven publish restructure + XCFramework hierarchy
  expansion. Deferred as dedicated ticket.

Skipped (need user):
- CODE_OF_CONDUCT.md (content classifier blocks Contributor Covenant
  verbatim)
- Maven Central T1 (Sonatype + GPG)
- Benchmark vs competitors T4 (methodology + Maven coords)
- Logo/banner/GIF T7 (design assets)
- Tag + GitHub release T10 (rename pending)

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

14 KiB
Raw Permalink Blame History

Project State

Project Reference

See: .paul/PROJECT.md (updated 2026-05-21)

Core value: Android devs plot 200Hz+ multi-signal sensor data without frame drops Current focus: v0.4.0 portfolio hardening SHIPPED — next v0.5.0 architecture + interaction

Current Position

Milestone: v1.0.0-public-release (IN PROGRESS — autonomous tasks done, user-blocked tasks pending) Phase: Portfolio prep autonomous batch shipped (debt partial, docs, CI, demos). User-side tasks pending. Plan: .paul/phases/v1.0.0-public-release-plan.md Status: 5/10 plan tasks done (T2, T3, T5, T6, T8 partial + T9 perf docs + partial debt). 5 blocked on user (T1 Maven Central, T4 benchmark vs competitors, T7 assets, T10 tag/release, app rename). Last completed: v1.0.0 portfolio prep autonomous batch (2026-05-22) Last activity: 2026-05-22 — Autonomous portfolio prep batch. 1 debt warning cleared (klib non-packed compat removed; 8 remain blocked behind com.android.kotlin.multiplatform.library migration). Docs: CHANGELOG.md, CONTRIBUTING.md, chart-realtime/README.md rewrite, docs/PERFORMANCE.md. CI: .github/workflows/{ci,release}.yml + dependabot + PR template + 2 issue templates. Android demo (T8) polished: Material3 control panel (sample rate slider, signal count, waveform per signal, window seconds, LoD picker, axis label mode, formatter picker, interaction toggle, theme switch, status row). iOS SwiftUI demo (T3 scaffold) via SPM Package.swift + sources; xcodebuild builds green; smoke test confirms XCFramework consumable from Swift; ComposeUIViewController shim deferred to v1.0.0 T1.

Progress:

  • v0.1.0 milestone: [██████████] 100% SHIPPED
  • v0.2.0 milestone: [██████████] 100% SHIPPED
  • v0.3.0 milestone: [██████████] 100% SHIPPED
  • v0.4.0 milestone: [██████████] 100% SHIPPED
  • v0.5.0 milestone: [██████████] 100% SHIPPED
  • v1.0.0 milestone: [█████░░░░░] 50% portfolio prep done, user-blocked items pending

Progress:

  • v0.1.0 milestone: [██████████] 100% SHIPPED
  • v0.2.0 milestone: [██████████] 100% SHIPPED
  • v0.3.0 milestone: [██████████] 100% SHIPPED
  • v0.4.0 milestone: [██████████] 100% SHIPPED
  • v0.5.0 milestone: [██████████] 100% SHIPPED

Loop Position

PLAN ──▶ APPLY ──▶ UNIFY
  ✓        ◐        ✓     [v1.0.0 partial — autonomous batch done, user-blocked items pending]

v1.0.0 deferred (need user)

  • T1 Maven Central publish — Sonatype OSSRH account + GPG key + DNS TXT proof for dev.dtrentin namespace
  • T4 Benchmark vs competitors — methodology decision (JMH? androidx.benchmark? both?), Maven coords for Vico/KoalaPlot/MPAndroidChart, hardware target (Pixel 6 / iPhone 13 vs CI runners)
  • T7 Logo / banner / GIF — design assets (manager cannot generate raster/vector art)
  • T10 Tag + release + push to GitHub — user wants to wait on GitHub (potential project rename). Currently remote = ssh://3nt-git.duckdns.org:222/davide.trentin/KMPCharts.git
  • App rename — user mentioned considering rename; deferred
  • ComposeUIViewController shim (T1 dependency) — small Kotlin shim in chart-realtime/src/iosMain/ exporting RealtimeChartViewController(state, xWindowSeconds): UIViewController to enable real SwiftUI demo

v1.0.0 deferred (out-of-scope debt)

  • Remaining 8 deprecation warnings (KGP 2.3 / AGP 9 :app plugin + libraryVariants API + AGP-newDsl) blocked behind migration to com.android.kotlin.multiplatform.library 9.2.0. Migration forces ABI baseline regen + restructured Maven publication graph + XCFramework hierarchy expansion. Deferred as dedicated post-1.0.0 ticket.

Accumulated Context

Decisions

Decision Phase Impact
Canvas custom rendering (no lib) Init Core architecture
Render/data decoupled withFrameNanos Init RenderLoop design
TieredBuffer over flat ring v0.2.0 O(N×constant) memory, 20+ signals
MIN_MAX tier binning (not MEAN) v0.2.0 Composable, no artifacts at tier boundaries
MEAN removed from LodMode v0.2.0 Cleaner API, no synthetic values
LodDecimator as class (pre-alloc scratch) v0.2.0 Zero GC at render time
Sensor cap 200Hz (5_000μs) v0.2.0 Thermal/battery optimization
Package dev.dtrentin v0.2.0 Personal project, not company
iOS targets shipped v0.3.0 iosX64/iosArm64/iosSimulatorArm64 + Platform.ios.kt (iosX64 dropped in v0.5.0 D1)
Drop iosX64 target v0.5.0 D1 Compose-MP 1.11.0 no ios_x64 variant; Apple deprecated Intel Mac sims Xcode 15+; iosArm64 + iosSimulatorArm64 cover modern devs
iOS build pipeline green v0.3.0 iOS 33 tests pass on iOS sim, XCFramework "ChartRealtime" produced
timeIntervalSinceReferenceDate + epoch offset v0.3.0 iOS K/Native binding doesn't expose timeIntervalSince1970; use 978_307_200s constant
applyDefaultHierarchyTemplate() explicit v0.3.0 iOS iosMain/iosTest auto-wired across 3 iOS targets
Manual format helpers in AxisRenderer v0.3.0 iOS String.format JVM-only; private formatFixed/formatScientific/pow10
kotlin.concurrent.Volatile + kotlin.jvm.JvmName imports v0.3.0 iOS Multiplatform replacements for JVM-default annotations
MIT License v0.4.0 T1 Personal project, permissive, no patent grant required
explicitApi() Strict + BCV 0.16.3 + klib enabled v0.4.0 T2 ABI lockdown active; chart-realtime.api 161 LOC + .klib.api 211 LOC baseline
kotlinx-datetime 0.6.2 (NOT 0.7.x) v0.4.0 T4 Kotlin 2.1.0 compat; bump to 0.7.x when Kotlin → 2.1.20+ in v0.5.0
CircularBuffer.writeIndex Long v0.4.0 T5 Overflow 124d @ 200Hz fixed
hasData boolean flag v0.4.0 T8 Replace Float.MAX_VALUE/POSITIVE_INFINITY sentinels in TieredBuffer + RealtimeChart
_signals as @Volatile copy-on-write Map v0.4.0 T9 Replace MutableState; eliminate snapshot writes from data thread for collection
dataVersion as mutableLongStateOf v0.4.0 T10 Compose-observable; drives recomposition data-driven; idle = 0 recompositions
applySnapshot retry helper (Snapshot.withMutableSnapshot + SnapshotApplyConflictException) v0.4.0 T9 Thread-safe push from background thread
RenderLoop.kt deleted v0.4.0 T6/T10 rememberFrameTick removed; targetFps deprecated (KDoc + @Deprecated)
Per-signal scratch arrays in SignalEntry v0.4.0 T11 Single buffer snapshot per signal per frame (was 2); ~1.14 MB/signal
M4 binning (firstTs/V, minTs/V, maxTs/V, lastTs/V) v0.4.0 T12 Replace double-push min/max sentinel; TIER1/2 CAPACITY *4; no vertical spikes at bin boundaries
NaN/Inf/backward-timestamp guards on push v0.4.0 T13 Silent drop; KDoc contract added; per-signal lastPushedTs tracking
NumberFormat internal object v0.4.0 T14 Extracted from AxisRenderer for testability; 38 tests pinned behavior

Deferred Issues

Issue Origin Effort Revisit
Dirty flag render (skip frame if no new data) v0.2.0 S ✓ v0.3.0
targetFps=30 default v0.2.0 S ✓ v0.3.0
iOS expect/actual beyond currentTimeMs Init L ✓ v0.3.0
KDoc on public API surface v0.2.0 M ✓ v0.3.0
README with integration example v0.2.0 S ✓ v0.3.0
iOS build/test pipeline (compile + iosTest + XCFramework) v0.3.0 plan M ✓ v0.3.0 iOS
Publish to Maven Central / GitHub Packages v0.2.0 M v1.0.0
iOS demo app (SwiftUI) v0.3.0 iOS L v1.0.0
CI workflow for iOS build (GitHub Actions) v0.3.0 iOS M v1.0.0
Compose compiler unstable warning on SignalConfig v0.3.0 iOS S v0.5.0
LICENSE missing v0.4.0 plan S ✓ v0.4.0 T1 (MIT)
explicitApi + BCV v0.4.0 plan M ✓ v0.4.0 T2
Internal lockdown 6 leaked symbols v0.4.0 plan M ✓ v0.4.0 T3
Drop expect/actual currentTimeMs v0.4.0 plan S ✓ v0.4.0 T4
CircularBuffer.writeIndex Long v0.4.0 plan S ✓ v0.4.0 T5
@Suppress + !! cleanup v0.4.0 plan S ✓ v0.4.0 T6
Dead config removal v0.4.0 plan S ✓ v0.4.0 T7
POSITIVE_INFINITY sentinel replace v0.4.0 plan S ✓ v0.4.0 T8
Compose snapshot thread safety v0.4.0 plan M ✓ v0.4.0 T9
dataVersion mutableLongStateOf v0.4.0 plan M ✓ v0.4.0 T10
Single snapshot per signal per frame v0.4.0 plan M ✓ v0.4.0 T11
M4 binning Tier1/Tier2 v0.4.0 plan M ✓ v0.4.0 T12
NaN/Inf/backward-timestamp guards v0.4.0 plan S ✓ v0.4.0 T13
AxisRenderer format helpers test v0.4.0 plan M ✓ v0.4.0 T14 (38 tests)
TieredBuffer + LTTB test gap v0.4.0 plan M ✓ v0.4.0 T15 (25 tests)
Bump kotlinx-datetime 0.6.2 → 0.7.x v0.4.0 T4 S v0.5.0 (needs Kotlin 2.1.20+)
Long overflow at value ≥ 1e19 in NumberFormat.formatFixed v0.4.0 T14
LTTB upper bound inclusive vs snapshot half-open semantic mismatch v0.4.0 T15 S v0.5.0
Remove ChartConfig.targetFps (now @Deprecated, ignored) v0.4.0 T10 S v0.5.0
Memory budget: ~1.14 MB/signal post-CAPACITY *4 (was ~760 KB pre-bump) v0.4.0 T12 doc document in README post-v0.4.0

Reviewer Audit Findings (2026-05-21)

Source: 3 parallel reviewer agents (android-reviewer + mobile-performance-reviewer + mobile-architect).

S0 (block portfolio) findings:

  • LICENSE missing → legalmente nessuno può usare la lib
  • Compose snapshot violation: push() mutate MutableState da thread arbitrario senza withMutableSnapshot
  • dataVersion @Volatile NON è Compose-observable, recomposition driven solo da frameTick side-effect
  • Render NON zero-alloc: Pair + Stroke + TextLayoutResult + Strings per tick per frame (~50-100 KB/s)
  • Snapshot doppia per signal per frame (8x lavoro su 4 segnali)
  • No interaction layer (touch/zoom/pan) → fatale per "real-time chart" piece

S1 (visibili) findings:

  • CircularBuffer.writeIndex Int overflow dopo 124 giorni @ 200Hz
  • Tier1/Tier2 binning C7 artifacts (push doppio min poi max stesso ts)
  • 6 leaked internal symbols pubblici (CircularBuffer, TieredBuffer, LodDecimator, dataVersion, rememberFrameTick, capacity constants)
  • 3 dead config field (maxBufferSeconds, xLabelDecimals, SignalConfig.label)
  • @Suppress("UNUSED_EXPRESSION") hack + !! non-null assertion in render hot path
  • Test gap: AxisRenderer format helpers untested + buggy (Q4 formatFixed Long overflow), TieredBuffer untested (143 LOC), LTTB branch untested
  • expect/actual currentTimeMs over-engineered — kotlinx-datetime 0.7.0+ replaces it
  • iOS targets build ma README + samples Android-only

Claims aggiustati:

  • "Zero GC at render time" → falso oggi, vero post v0.5.0 T8/T9
  • "Lock-free SPSC ring buffer" → impreciso, fragile JMM su Native
  • "200Hz sustained" → plausibile JVM, borderline mid Android multi-signal pre-v0.5.0 T11
  • "1h buffer" → vero ma costa 720KB+ per signal (non documentato)

Posizionamento mercato: Nicchia genuinamente vuota: nessun KMP chart lib (Vico, KoalaPlot, AAY-chart, netguru) targeta real-time 200Hz multi-signal con bounded memory. Differentiation reale ma serve interaction layer per essere prendibile sul serio.

Blockers/Concerns

  • LICENSE missing → ✓ MIT shipped in v0.4.0 T1
  • API surface non lockdown → ✓ explicitApi + BCV shipped in v0.4.0 T2-T3
  • Compose snapshot thread safety → ✓ withMutableSnapshot + retry shipped in v0.4.0 T9
  • No interaction layer (touch/zoom/pan) → blocking "real-time chart" claim (v0.5.0 scope)
  • targetFps deprecated but not removed → minor noise in public API (v0.5.0)

Session Continuity

Last session: 2026-05-21 (v0.4.0 portfolio hardening shipped via kmp-manager group-by-group flow) Stopped at: v0.4.0 closed. 15 plan tasks done. 107 tests pass. Full verify suite green. Next action: kickoff v0.5.0 architecture + interaction layer Resume context:

  • Library: chart-realtime, dev.dtrentin:chart-realtime:0.5.0-SNAPSHOT (version unbumped post-v0.4.0; bump deferred to v1.0.0 release)
  • App: AndroidChartsApp consuming library, builds clean
  • Remote: ssh://git@3nt-git.duckdns.org:222/davide.trentin/KMPCharts.git
  • Package: dev.dtrentin.chart
  • iOS: 3 targets compile, link, test (107 tests). XCFramework name ChartRealtime, baseName CamelCase.
  • Local Mac setup: Xcode.app at /Applications/Xcode.app, iOS sim runtime installed (iPhone 17 family), local.properties points to ~/Library/Android/sdk, JDK 21 Temurin via JAVA_HOME.
  • ABI baseline committed: chart-realtime/api/chart-realtime.api (161 LOC) + chart-realtime.klib.api (211 LOC)
  • Plans: v0.4.0 ✓ SHIPPED · v0.5.0 + v1.0.0 PLANNED
  • Effort residuo verso v1.0.0: ~7-10 giorni full-time

v0.4.0 close summary (2026-05-21)

15 plan tasks executed across 8 P-groups via kmp-manager:

  • P0: T1 (LICENSE MIT) + T7 (dead config removal)
  • P1: T2 (explicitApi + BCV 0.16.3 + klib enabled)
  • P2: T3 (internal lockdown) + T4 (kotlinx-datetime 0.6.2)
  • P3: T5 (writeIndex Long) + T8 (hasData boolean flag)
  • P4: T6 (RealtimeChart cleanup) + T9 (snapshot thread safety) + T10 (mutableLongStateOf)
  • P5: T11 (single snapshot per signal) + T12 (M4 binning)
  • P6: T13 (NaN/Inf/backward guards)
  • P7: T14 (NumberFormat extract + 38 tests) + T15 (TieredBuffer/LTTB +25 tests)

Test count: 33 → 107 (+74). ABI: 211/272 → 161/211 LOC (locked, baseline committed). RenderLoop.kt deleted. Platform.kt + .android.kt + .ios.kt deleted. 0 !! assertions in commonMain. 0 @Suppress hacks. explicitApi Strict.

Deviations from plan:

  • T4 kotlinx-datetime 0.6.2 instead of 0.7.x (Kotlin 2.1.0 compat — 0.7.x needs Kotlin 2.1.20+). Bump deferred to v0.5.0.
  • T7 ChartConfig.targetFps NOT removed (would break ABI of data class). Marked @Deprecated, ignored at runtime. Full removal in v0.5.0.
  • T11 memory per signal ~1.14 MB (plan estimate was ~760 KB based on pre-bump TOTAL_CAPACITY). Acceptable.
  • T13 unknown-signal push no longer bumps dataVersion (was bumping in v0.3.0). Existing test push_unknownSignal_stillIncrementsDataVersion replaced with pushIgnoresUnknownSignalName per new contract.

STATE.md — Updated after every significant action