KMPCharts/CONTRIBUTING.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

155 lines
5.8 KiB
Markdown
Raw Permalink 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.

# 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/<short>`, `fix/<short>`, `refactor/<short>`
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** — `<scope>: <imperative summary>` 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.