Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: Lx-204 <15377891490@163.com> Co-authored-by: AlexDev_ <56083016+alexdev03@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Nikhil Nair <32665919+nikhilnair31@users.noreply.github.com> Co-authored-by: marciodps <95321123+marciodps@users.noreply.github.com> Co-authored-by: ThomasCrund <39323104+ThomasCrund@users.noreply.github.com>
8.1 KiB
Contributing to Hermes Mobile
Thank you for your interest in contributing to Hermes Mobile! To maintain high code quality and consistency, we ask all contributors to follow the workflow and guidelines outlined below.
Before You Start: Search First
A quick duplicate check saves your time and keeps the PR queue clean:
gh search prs --repo Hy4ri/hermes-mobile --state open "<your feature>"
gh search prs --repo Hy4ri/hermes-mobile --state closed "<your feature>"
gh search issues --repo Hy4ri/hermes-mobile --state open "<your feature>"
If an open PR already covers it, review or improve that one instead of opening a competing duplicate. For larger work, comment on the issue to signal you're on it.
PR Workflow
Every change must go through a pull request (PR) targeting the dev staging branch. Do not push directly to main or dev.
Keep PRs focused: one feature or one fix per PR. Unrelated changes belong in separate PRs so each one stays easy to review, test, and revert.
- Pick or open an issue to discuss the changes you want to make.
- Create a branch off
devusing the following naming convention:- Features:
feat/short-description(optionallyfeat/issue-N-description) - Bug fixes:
fix/short-description(optionallyfix/issue-N-description) - CI/chore:
ci/...,chore/...
- Features:
- Implement your changes and format them locally (see Code Style).
- Submit a PR targeting the
devbranch, filling the PR template. - Ensure all CI checks pass (ktlint, Android Lint, unit tests, build).
- Rebase onto
devbefore merge. Rungit fetch origin dev && git rebase origin/dev, resolve conflicts carefully, and re-run verification on the rebased result. Maintainers squash-merge; green CI does not by itself authorize a merge.
Commit Messages
We follow Conventional Commits to keep the history scannable:
feat:— New feature for the userfix:— Bug fixrefactor:— Code change with no functional changedocs:— Documentation onlytest:— Adding or fixing testsci:— CI configuration or scriptschore:— Maintenance, deps, tooling
Keep commits atomic: one subject line (≤72 chars) + max 2 lines of body. If it needs more, split into multiple commits.
fix(#431): resume last session from Room cache on cold start
Shows cached messages before WS connects and resumes the last
session on GatewayReady instead of creating a blank one.
AI Tool Usage
If you use AI coding tools (including agents) to contribute:
- Never add the AI tool as author, co-author, or
Co-Authored-Byin commit metadata. - Direct AI agents to
AGENTS.mdandDESIGN.mdin the repo root —AGENTS.mdcontains operational and architecture conventions, whileDESIGN.mddefines visual, interaction, and accessibility requirements and links to the token sources. Theme implementation is documented inTHEMES.md.
Translations
Translations for Hermes Mobile are managed through Hosted Weblate.
- Source strings are located in
app/src/main/res/values/strings.xml. - Localized strings are stored in
app/src/main/res/values-<locale>/strings.xml. - You can contribute translations directly on Weblate without modifying code or opening manual PRs.
Code Style
We enforce Kotlin coding conventions and Jetpack Compose best practices.
Kotlin Formatting
- Code formatting is checked and enforced by ktlint 1.8.0 in CI. The authoritative command is the Gradle task (not the standalone binary):
./gradlew ktlintCheck # check (CI-equivalent) ./gradlew ktlintFormat # auto-fix, then re-check with ktlintCheck⚠️ Hand-patching indentation/import order routinely fails the real CI gate (multiline-expression-wrapping and ASCII import order are the usual casualties). Always run
ktlintFormat, never fix formatting by hand. - Import ordering is strictly ASCII-lexicographic (uppercase before lowercase: e.g.,
LaunchedEffectbeforecollectAsState).
Compose Guidelines
- Standard screen structures must use
HermesScaffoldrather than raw Material3Scaffold. - Follow existing Compose APIs: required parameters first,
modifieras the first optional parameter, then other optional parameters; keep trailing composable content last. - Do not apply
paddingValueson inner content insideHermesScaffold— the scaffold already handles top bar padding. SeeAGENTS.mdfor the full breakdown of this recurring bug. - Every data screen must implement
LoadingState,ErrorState, andEmptyStatebranches in itswhen { }block.
Color Usage
- No hardcoded
Color(0x...)literals outsidetheme/,*Preview.kt, and auth screens. A Gradle task (checkColorLiterals) enforces this in CI. - Use
MaterialTheme.colorScheme.<token>orLocalHermesStatusColors.current.<semantic>instead. Color.TransparentandColor.Unspecifiedare allowed.
Tests
- Unit tests:
./gradlew testDebugUnitTest(MockK). Instrumented Compose UI tests run in CI on an emulator. - Tests must not touch real on-device storage. Use
@get:Rule TemporaryFolder(or RoominMemoryDatabaseBuilder) — never write to real app data or external storage. CI/emulator environments are ephemeral; leaking state breaks the next run. - Mock time/dispatchers explicitly (
StandardTestDispatcher+Dispatchers.setMain), restoreDispatchers.Mainin teardown, and cancel test-owned scopes. Follow neighboring tests for the affected component. - For UI changes, exercise the changed flow on a device or emulator and record the behavior observed. A built or downloaded CI APK alone is not behavioral verification. If device verification is unavailable, state that gate explicitly rather than marking it passed.
Gateway contract snapshot
GatewayContractTest checks WsMethods, handled server requests and literal param keys against a vendored copy of
hermes-agent's gateway-contract.openrpc.json (app/src/test/resources/gateway-contract/, provenance in SOURCE).
It is test-only. Refresh it with scripts/sync-gateway-contract.sh [ref], then run
./gradlew testDebugUnitTest --tests '*GatewayContract*' and review the diff. Allowlist exceptions need a reason in
GatewayContractAllowlist.
Dependency & Build Hygiene
- Pin build tooling. AGP/Kotlin/SDK versions are pinned in
gradle/libs.versions.tomlandbuild.gradle.ktson purpose. Unpinned versions silently break the build on the next dependabot/CI refresh (the "SDK not writable" AGP trap). Bump deliberately, not via wildcard ranges. - Never add a dependency without updating the version catalog; CI validates the Gradle wrapper.
PR Checklist
Before submitting your PR, please verify:
- I searched open/closed PRs + issues for duplicates (see Before You Start).
- PR is focused on one feature or one fix (no unrelated changes).
- Branch is rebased onto current
dev(git rebase origin/dev). ./gradlew ktlintCheckpasses (ranktlintFormatfirst — no hand edits).checkColorLiteralspasses (no hardcoded Color literals outside theme/)../gradlew testDebugUnitTestpasses locally (or CI unit-tests job is green).- No unused imports, unused parameters, or dead code.
- Meaningful images and icon-only actions have localized accessible labels; decorative images/icons use
contentDescription = nullto avoid redundant announcements. - New screens use
HermesScaffoldand implement Loading/Error/Empty states. - UI changes follow
DESIGN.md, including applicable contrast, touch-target, font-scaling, and RTL requirements. - Changed UI behavior was exercised on a device/emulator; the PR records the result or explicitly names the unverified gate.
- New components match the UI/UX style of similar existing screens.
- Commit messages follow Conventional Commits and are atomic (subject + ≤2 lines body).