Some checks are pending
CI / build (push) Waiting to run
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>
155 lines
5.8 KiB
Markdown
155 lines
5.8 KiB
Markdown
# 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.
|