# Pathrule Pattern: Android with Jetpack Compose (1.0.0)
# ::pathrule:package:android-jetpack-compose

### [RULE] Render immutable state and emit events upward  (path: /app/src/main/java)
<!-- scope: folder | priority: high | strict -->

Recomposition can happen at any time and in any order allowed by the runtime. A composable that mutates shared objects while rendering or reaches into repositories directly makes repeated execution produce new side effects.

- Expose one immutable screen state from the ViewModel and derive display-only values with stable calculations rather than maintaining parallel booleans that can contradict each other.
- Pass state down and events up. Keep navigation, repository calls, and domain transitions behind event handlers owned by the screen or ViewModel.
- Use stable keys for lazy lists and do not mutate collection instances in place; replace state with a new value so Compose can observe the transition.
- Hoist reusable component state to the lowest common owner, but keep transient visual details local when no other component or process needs them.

See /app/src/test for the adjacent decision or procedure that completes this constraint.

---

### [RULE] Launch effects only through lifecycle-aware APIs  (path: /app/src/main/java)
<!-- scope: folder | priority: high | strict -->

A composable body is not a lifecycle callback. Starting a coroutine, registering a listener, or sending analytics directly during composition repeats work whenever the function is recomposed.

- Use `LaunchedEffect` with keys that describe when work must restart, and keep changing callbacks current without restarting long-lived work unnecessarily.
- Register disposable listeners in `DisposableEffect` and remove the exact listener in cleanup. Do not rely on activity destruction to clean a screen-level resource.
- Collect flows with lifecycle-aware collection so stopped screens do not continue expensive work or mutate invisible UI state.
- Use `rememberCoroutineScope` only for jobs triggered by UI events and expected to end with the current composition; durable work belongs in a ViewModel or WorkManager.

See /app/src/main/java for the adjacent decision or procedure that completes this constraint.

---

### [MEMORY] remember, saved state, and persistence solve different lifetimes  (path: /app/src/main/java)

Android has several independent loss boundaries. Treating `remember` as persistence works in a preview and fails when the device rotates, the process is reclaimed, or the user returns after hours.

- Use `remember` for state that only needs to survive recomposition inside the current composition instance.
- Use saveable state for small UI values that can be represented in a bundle and should return after recreation; do not place large objects or active resources there.
- Keep screen state and asynchronous work in a ViewModel so configuration changes do not restart the operation.
- Persist durable user or domain data in a database or other repository. Saved state is a reconstruction hint, not a replacement for durable storage.

See /app/src/test for the rule or workflow that puts this decision into practice.

---

### [MEMORY] Navigation carries stable identifiers, not object graphs  (path: /app/src/main/java)

Passing serialized mutable objects through navigation duplicates data, exceeds platform limits, and delivers stale state when the destination finally renders. A route should describe identity and intent rather than transport a database snapshot.

- Define typed route arguments for stable identifiers and small values needed to construct the destination.
- Load the current entity in the destination ViewModel and handle missing or unauthorized identifiers as normal navigation outcomes.
- Return results through an explicit saved-state or shared-owner contract when a prior destination must react, rather than mutating an object both screens reference.
- Keep deep links on the same route parsing and authorization path as in-app navigation so an external URI cannot bypass validation.

See /app/src/main/java for the rule or workflow that puts this decision into practice.

---

### [MEMORY] Durable background work belongs to WorkManager  (path: /app/src/main/java)

A coroutine launched from an activity or ViewModel ends when its owner is cleared or the process disappears. That is correct for UI work but incorrect for a required upload, synchronization, or retry that must eventually complete.

- Schedule deferrable guaranteed work with constraints and an idempotent worker whose input is a stable identifier, not a large object snapshot.
- Use unique work and an explicit replacement or keep policy when repeated user actions refer to the same logical operation.
- Report progress through durable state the UI can observe after recreation instead of holding a callback to the original screen.
- Use a foreground service only when the user-visible ongoing task and platform policy require it; it is not a generic escape hatch for unlimited background execution.

See /app/src/test for the rule or workflow that puts this decision into practice.

---

### [SKILL] review-compose-lifecycles  (path: /)

---
name: review-compose-lifecycles
description: Review a Jetpack Compose feature after changes to state, effects, navigation, or background work.
---

# Review Compose Lifecycles

Run this procedure when the affected surface changes, before the result is promoted to production. Record evidence for every step instead of accepting a plausible-looking result.

- [ ] Enable recomposition diagnostics and verify rendering does not start network calls, analytics, listeners, or mutations more than the intended number of times.
- [ ] Navigate away and back while effects and collectors are active; confirm cleanup occurs and state ownership matches the screen lifecycle.
- [ ] Recreate the activity and restore from saved state, then simulate process recreation and verify durable data reloads from the repository.
- [ ] Open the destination through both in-app navigation and a deep link with valid, missing, stale, and unauthorized identifiers.
- [ ] Interrupt each background operation, relaunch the app, and prove WorkManager or the chosen owner resumes idempotently without duplicating external effects.

## Exit criteria

The change is complete only when the expected behavior, failure behavior, and rollback path have all been exercised with representative data. Preserve the evidence with the change so the next operator can repeat the same checks.
