# Contributing to chart-realtime Thanks for considering a contribution. This document covers the build process, project conventions, and the PR workflow. ## Project layout ``` chart-realtime/ Kotlin Multiplatform library module ├── src/commonMain/ Shared code (Android + iOS) ├── src/commonTest/ Shared tests ├── src/androidMain/ Android-only code (currently empty) ├── src/iosMain/ iOS-only code (currently empty post-v0.5.0) └── api/ ABI baseline (managed by BCV) app/ Android demo app (sandbox) samples/ios/ SwiftUI iOS demo (planned for v1.0.0) .paul/ Project planning state (PAUL workflow) ``` ## Prerequisites - JDK **21** (Temurin recommended) - Xcode (for iOS targets; macOS only) - Android SDK with `compileSdk 35` - No global Gradle install needed — wrapper provided ## Building ```bash # Library — Android variant ./gradlew :chart-realtime:assembleRelease # Library — iOS variants ./gradlew :chart-realtime:compileKotlinIosArm64 ./gradlew :chart-realtime:compileKotlinIosSimulatorArm64 # XCFramework (consumes the library from Swift) ./gradlew :chart-realtime:assembleChartRealtimeXCFramework # Android demo app ./gradlew :app:installDebug # Publish library to local Maven (for consumer testing) ./gradlew :chart-realtime:publishToMavenLocal ``` ## Testing ```bash # Shared common tests run on iOS simulator (canonical CI gate) ./gradlew :chart-realtime:iosSimulatorArm64Test # ABI compatibility check (must stay green; regen via apiDump only on intentional break) ./gradlew :chart-realtime:apiCheck ./gradlew :chart-realtime:apiDump ``` 207 tests on the canonical run as of `v0.5.0`. Any PR must keep that count from shrinking. ## Code style - Kotlin official conventions; `explicitApi() Strict` enforced on `chart-realtime` - 4-space indent, no tabs - 120-column soft limit; wrap when readability suffers - Avoid `!!` and `@Suppress` in `commonMain` (audit blockers since v0.4.0) - `@Immutable` on data classes participating in `@Composable` parameter inference; `@Stable` on mutable state holders observing through `MutableState` - Use `kotlin.time.Clock` for current time; do not reintroduce `kotlinx-datetime` ## Architecture conventions - **Buffer layer** (`buffer/`) — zero-alloc data structures. Pre-allocated scratch arrays. No allocations on the render hot path - **LoD layer** (`lod/`) — pluggable `LodStrategy` impls. Each constructor takes `maxCount` + `maxPixelWidth` to size its scratch arrays once - **Render layer** (`render/`) — `internal object` for the Canvas extension funs. Cache `Stroke` / `TextStyle` instances; rely on Compose `TextMeasurer` LRU for text layout - **Interaction layer** (`interaction/`) — hoistable `@Stable` state holders. Mutations route through Compose-observable backing state. Pointer-input lambdas read from `LongArray` cache slots populated inside the draw lambda (1-frame stale is acceptable for gesture handling) ## Pull request workflow 1. **Open an issue first** for non-trivial changes (new features, API additions, performance work). Bug fixes can skip this 2. **Branch from `master`**. Name: `feat/`, `fix/`, `refactor/` 3. **Write tests**. Coverage requirements: - Bug fix: regression test reproducing the bug, then the fix - New public API: unit tests for happy path + edge cases - Performance change: benchmark before/after numbers in PR body 4. **Run the full gate locally** before pushing: ```bash ./gradlew :chart-realtime:iosSimulatorArm64Test \ :chart-realtime:assembleRelease \ :chart-realtime:apiCheck \ :app:assembleDebug ``` 5. **ABI changes** — if `apiCheck` fails: - If the change is intentional and you accept the break, regenerate the baseline with `./gradlew :chart-realtime:apiDump`, commit the diff, and call it out in the PR description - If the change is unintentional, fix the code instead 6. **Commit format** — `: ` for the subject. Body lists touched modules and intent. Example: ``` feat(interaction): add momentum scrolling to drag pan Modules: chart-realtime (interaction, RealtimeChart) ``` 7. **No `--no-verify`** on commit hooks. Investigate and fix the underlying issue if a hook fails 8. **Squash on merge** for small PRs; preserve commits for large feature work ## Issue triage Bug reports must include: - Affected version - Platform (Android + API level / iOS + sim/device + iOS version) - Reproducer (smallest snippet that triggers the issue) - Expected vs actual behavior Feature requests must include: - Use case (the underlying problem, not the proposed solution) - Why the existing API doesn't already solve it - Acceptance criteria (what "done" looks like) ## Performance philosophy Library targets sustained 200Hz × multi-signal sensor data with bounded memory. The hot path is the per-frame Compose recomposition + Canvas draw lambda. Three rules: 1. **No allocations on the render hot path.** All scratch arrays pre-allocated; `Stroke` / `TextStyle` cached; `Pair` and `Map` iterators replaced with `FloatArray` out-params and cached arrays 2. **Snapshot once per signal per frame.** `SignalEntry.scratchTs` / `scratchV` pre-allocated; the Y-range scan and the path generation read the same snapshot 3. **Bounded memory regardless of session length.** `CircularBuffer.writeIndex` is `Long`; tier ring buffers overwrite oldest first; 1h buffer = ~1.14 MB per signal Any change that breaks one of these requires explicit justification + benchmark numbers + tests that pin the new behavior. ## Reporting security issues Email `davide.trentin@henesis.eu` directly. Do not open a public issue. ## License By contributing, you agree your contributions are licensed under the MIT License of this project.