AGENTS.md@apps/android · git:20261107.9e8528a · 2026-11-07 · sha256 12f10f3fdfed75c5
AGENTS.md@apps/android git:20261107.9e8528aA
Immutable. This exact content never changes and is served at /api/v1/blob/12f10f3fdfed75c5.
# Android app (`apps/android`)
Native Kotlin + Jetpack Compose app. The root `AGENTS.md` still applies. Decisions, module owners and status are in
`PLAN.md`. The design handoff (storyboard screens, notes, tokens) lives outside git at
`/Users/amruth/Projects/reactive-resume/.agents/handoff/android-app/` on the owner's machine.
## Build and test
```sh
export JAVA_HOME=/opt/homebrew/opt/openjdk@21/libexec/openjdk.jdk/Contents/Home # JDK 21
echo "sdk.dir=$HOME/Library/Android/sdk" > local.properties # once per checkout (gitignored)
./gradlew assembleDebug
./gradlew :feature:today:testDebugUnitTest # unit + screenshot tests of one module
./gradlew :feature:today:recordRoborazziDebug # write screenshot goldens
./gradlew :feature:today:verifyRoborazziDebug # fail on screenshot changes
./gradlew :feature:today:lintDebug
```
- Several builds may run at once on a 32 GB machine: keep the heap settings in `gradle.properties`, send long output to
a log file, and never run `./gradlew --stop` (it kills every checkout's daemon). If a build reports "stop command
received", run it again.
- No emulator for feature work: Robolectric and Roborazzi only. One device at a time on this machine.
## Modules
- `:app`: activity, `AppGraph` (constructor injection, no DI framework), `RrApp` NavHost, tab bar, toasts.
- `:core:navigation`: `Routes.kt` (every screen as a type-safe route), `Shell.kt` (`screen<T>()`, `tabRoot<T>()`,
`modal<T>()`, `sheet<T>()`, `RrNavMotion`, `LocalToasts`, slot locals), `DeepLinks.kt`.
- `:core:designsystem`: tokens, `RrTheme`, every component (`Rr*`). `:core:testing`: screenshot harness (README there).
- `:core:network`, `:core:data`, `:core:database`: generated API client, repositories (`LocalRepositories`, real and
fake), offline drafts. `:core:auth`: sign-in (`AuthService`). `:core:page`: `PageRenderer`. `:core:strings`: catalogs.
- `:feature:<area>`: screens. Each exposes `fun NavGraphBuilder.<area>Graph(nav: NavController, motion: RrNavMotion)`,
called from `RrApp`. Builder Share mode and workspace tabs are slots (`LocalBuilderShareMode`, `LocalWorkspaceTab`).
## Feature rules
- Own your modules only. Changes to `:core:*`, `:app`, `gradle/libs.versions.toml` or `build-logic` are requests to the
lead. A missing component is a request, never a local look-alike.
- Design-system components and tokens only: no Material components or colours, no raw `Color(...)`, no `dp` values
that aren't on the boards or in tokens. Paper is white in both themes.
- Strings: `me.rxresu.core.strings.R.string.*` (search the English text in `core/strings/src/main/res/values/strings.xml`).
Copy missing from the catalogs goes in the feature's own string resources (`src/main/res/values/`), in English, copied from the web
app (`apps/web/src`); terms follow `GLOSSARY.md`. Sentence case, no exclamation marks, no emoji.
- Screens take state and callbacks (stateless) and a `ViewModel` owns loading. Create view models with
`viewModel { XViewModel(LocalRepositories.current.x) }`; previews and tests pass fake repositories.
- Accessibility: TalkBack labels on icon-only controls, 44 dp targets, start/end layout (RTL), font scale 200%, a menu
alternative for every drag or swipe.
- Haptics through `rememberRrHaptics()`, one per gesture, never on scroll, typing or plain navigation. Motion through
`RrMotion`; honour `RrTheme.reducedMotion`.
- One Roborazzi test per screen and theme: `captureScreen("<ID>-<Name>", dark)` with the storyboard file stem
(`T1-Today`, `J6-Detail`, …) so the diff report pairs it with `screens/<ID>-<theme>.png`. Unit-test view models and
non-trivial logic with fakes and virtual time; never real waits.
- Commits: Conventional Commits (`feat(android): …`), no AI attribution. If signing fails:
`git -c commit.gpgsign=false commit`.