Guidance for AI coding agents (Copilot, Cursor, Aider, Claude, etc.) working in Stream’s Android Core repo. Humans are welcome too; tone is optimised for tools.
This project houses Stream Android Core, the shared runtime used by Stream Chat, Video, and Feeds SDKs. It concentrates connection/session lifecycle, auth/token plumbing, job orchestration, retry/backoff logic, batching utilities, logging, and other cross-product primitives. Treat it as infrastructure: focus on correctness, binary compatibility, and low regression risk.
- Language: Kotlin (JVM target 11 inside Gradle; toolchain 17 for builds)
- Android min/target/compile: see
io.getstream.core.Configuration - Coroutines: kotlinx 1.8+
- Networking: OkHttp, Retrofit, Moshi (codegen via KSP)
- Build: Gradle Kotlin DSL, shared logic in
buildSrc/ - Static analysis: Spotless + ktfmt, Detekt, Kotlin explicit-api, Kover coverage
- Tests: JUnit4, MockK, Robolectric, kotlinx-coroutines-test, MockWebServer
stream-android-core/– main library (models, socket/session, token handling, processors, batching, retry, queueing)stream-android-core-annotations/– annotations + KSP helpers consumed by corestream-android-core-lint/– lint checks shipped with the libraryapp/– demo/debug client used for manual verificationbuildSrc/– centralised Gradle plugins & configuration (publishing, dependency versions, coverage)config/– license templates, detekt rules, formatting configsscripts/– publishing and tooling helpers
Modules are published; avoid leaking impl-only types across module boundaries without updating versioning docs.
- Format & headers:
./gradlew spotlessApply - Static analysis:
./gradlew detektAll(or module-specific:module:detekt) - JVM/unit tests (core module):
./gradlew :stream-android-core:test - Full verification:
./gradlew check - Sample app install:
./gradlew :app:installDebug(requires device/emulator) - Coverage:
./gradlew koverHtmlReport
Prefer running module-scoped tasks when iterating (:stream-android-core:test, :stream-android-core:detekt) to keep the cycle fast. All public changes should pass ./gradlew check before PR.
- Threading: Coroutines wrap most workflows; keep blocking I/O off main thread. Respect existing dispatcher usage and structured concurrency.
- State propagation: Connection state, batch processing, and retry flows feed
StreamSubscriptionManager. Maintain idempotency and guard callback ordering (seeStreamSocketSession). - Binary compatibility:
explicitApi()is enabled. Avoid signature breaking changes to public/internal APIs without coordinating version bumps. - Logging: Use tagged
StreamLogger. Keep error logs actionable; avoid leaking secrets/tokens. - Configurability: Many processors expose factories; prefer adding configuration points over ad-hoc branching.
- Kotlin official code style (Spotless enforces). 4 spaces, no wildcard imports.
- Limit
internalsurface area; favour private helpers where possible. - Backtick test names for clarity (
fun `serial queue delivers items in order`()). - Public APIs require KDoc; significant internal flows get concise comments explaining the “why” (avoid narrating what the code already states).
- Keep licence headers intact; regenerate via
./gradlew generateLicenseif the year changes.
- Unit tests live under each module’s
src/test/java. Use MockK and coroutines-test to control timing. - Networking/socket flows: exercise with MockWebServer or fake listeners; ensure heartbeats, retries, and cleanup happen (see
StreamSocketSessionTest). - When touching concurrency tools (batcher, single-flight, retry), add deterministic tests that cover edge cases like cancellation, dedupe, and failure retries.
- Prefer lightweight Robolectric tests only when Android primitives are involved; otherwise keep pure JVM.
- Always run
./gradlew :stream-android-core:testfor touched code; broaden to./gradlew checkbefore publishing.
- Update
README.mdif you change major workflows or public setup code. - Keep processor/queue behaviour documented via KDoc or dedicated docs when semantics evolve.
- For new configuration flags or environment expectations, note them in
config/or module READMEs.
- Never hardcode API keys or tokens; samples rely on local
local.properties/env vars. - Sanitise logs: token payloads, JWTs, or user IDs should not leak to release logs.
- Be mindful of TLS/OkHttp settings—core is shared across products; changes ripple widely.
- Commits should be focused and imperative (e.g.,
fix(socket): guard reconnect cleanup). - Before PR: run Spotless + relevant tests. Include test/task output in the PR description when significant.
- Coordinate with the release owner before modifying publishing metadata or version numbers.
- New APIs require documentation + unit tests; mention migration notes for behavioural changes.
- Understand which module you’re editing and run its tests.
- Maintain explicit API boundaries; prefer additive changes.
- Ensure cleanup/teardown paths handle cancellation and failure (important for sockets, queues, retries).
- Keep concurrency deterministic—use structured coroutines and avoid global scope.
- Run Spotless (formatting) before finishing.
- Add tests for new APIs or behaviour changes.
- Coordinate with the release owner before modifying versioning metadata.
- Document new APIs and significant changes in
README.mdor module-specific docs. - Sanitise logs to avoid leaking sensitive information.