# Hermes Mobile — Delivery Plan > **Operational contract:** Codex must read this file before each work loop. Work exactly one coherent milestone at a time. Move completed items to the completed section in the same commit that implements them. Add a concise entry to `CHANGELOG.md` in the same commit. Never mark work complete without running its listed verification. ## Product guardrails - Native Android client for upstream Hermes Agent: **Kotlin + Jetpack Compose**. - Material 3 base, deliberately customized through a reusable design system. - Thin client: Hermes remains the authority for auth, sessions, streaming, tool semantics, and backend behavior. - No WebView-first app, no cloned Hermes backend, no embedded credentials. - Adaptive, accessible, compact-but-readable UI: phone, tablet, light/dark, font scaling, IME/keyboard, long code/output must remain safe. - Keep DTO/transport, domain/UI models, repositories, view models/state, and composables separated. - Never commit APKs, build outputs, credentials, or generated local environment files. ## Release policy - Start artifacts at `v0.1.0-beta.1`. - Patch: compatible fixes/polish. Minor: a coherent feature milestone. Major: stable `v1.0.0+` only. - A release requires a pushed source commit, passing relevant checks, a versioned APK artifact, and a Telegram release post. --- ## Now — direct gateway pivot Complete the Android/client contract pivot before implementing backend behavior. The next release path targets a remote upstream Hermes Agent gateway/API server directly; `apps/companion` is legacy compatibility only. ## Beta path — upstream-connected MVP ## Externally blocked - **B3 — Direct gateway session contract and durable chat state** - Foundation verified 2026-07-24: repository search contract and fake implementation, capability-gated session list/create/get/delete mapping, durable selected-session/draft/pending-send storage, and duplicate-send reducer/UI protection. - Not complete: official upstream documentation still provides no session-search HTTP contract, and the configured local API server was not listening for a live compatibility exercise on 2026-07-24. - Unblock evidence is maintained in `docs/BLOCKERS.md`; do not mark B3 complete until documented search and real gateway behavior are verified. - [ ] **B5 — Tool, artifact, and output experience** - Compact tool cards with lifecycle/status, expandable output, copy/share/save affordances. - Safe rendering for large logs and code; no unbounded nested scroll failures. - Artifact/file handoff only where upstream capability is actually supported. - Verification: large-output and rotation/resume checks. ## Beta hardening and release - **Queued — R1: Adaptive/accessibility quality pass** - Small phone, normal phone, tablet/two-pane behavior; portrait/landscape. - Font scaling, TalkBack semantics, contrast, touch targets, reduced motion, IME behavior. - Verification: emulator/device matrix evidence documented in `docs/ROADMAP.md`. - **Queued — R2: Beta packaging and install guide** - Version `v0.1.0-beta.1`, reproducible debug/beta APK build, install/run guide, known limitations. - Publish source commit, APK, release notes, and verification result. - Verification: clean install on a real device or emulator plus full required checks. ## V2 backlog - Multiple server profiles and fast switching. - Rich file/artifact workflows based on upstream capability. - Notifications for task completion. - Mature tablet/two-pane session + optional terminal/log pane. - Offline cache/search and diagnostic export with privacy controls. - Remove legacy companion Android routes and deprecate `apps/companion` install/runtime docs after direct gateway chat/session/streaming is verified. --- ## Completed _Move finished items here with date, commit, and verification. Keep this section factual; do not claim unverified work._ - [x] **B4c — Wire gateway SSE into durable chat sends** — 2026-07-24, commit `feat: wire durable gateway SSE sends`. - Added a direct-gateway send coordinator that captures the selected session, draft, route lease, and capability-built SSE request before asynchronous execution; it never reselects a route or switches an active stream. - Publishes only the currently reserved request's intermediate and terminal reducer states; terminal completion, gateway failure, and transport failure clear only the matching durable reservation while retaining the selected session and draft. - Verification: `/root/.openclaw/workspace/scripts/hermes-mobile-preflight.sh` passed (Android `:app:assembleDebug`, 35 tasks, 4 executed); direct cached Kotlin/JUnit verification passed 52 tests including deterministic B4c success, gateway failure, transport failure, and stale request-ID tests; `git diff --check` passed. - [x] **B4b — Execute session SSE through the Android repository** — 2026-07-24, commit `feat: execute gateway session SSE`. - Added a thin OkHttp repository that executes the capability-built session stream request and parses SSE frames into the existing decoder and lease-bound reducer. - Kept the route generation, request URL, session ID, endpoint, and event gate immutable for the full call; transport/protocol failure invalidates and fails only that original lease. - Kept gateway `error` events separate from transport failure and excluded reconnect, cancellation, tool/artifact handling, and UI integration. - Verification: focused cached Kotlin compilation and direct JUnit execution passed 15 reducer, stream-contract, and fake HTTP/SSE repository tests; Android Gradle build attempt and final checks are recorded in `CHANGELOG.md`. - [x] **B4a — Capability-gated direct streaming foundation** — 2026-07-24, commit `feat: add gateway streaming foundation`. - Added capability-derived session SSE request construction using the advertised `POST` method and `{session_id}` path only. - Bound each stream to an immutable route generation and session ID, decoded documented core lifecycle/delta/final events, and gated structured tool events on `tool_progress_events`. - Made final assistant responses authoritative over accumulated deltas and ignored events from a different session lease. - Verification: focused cached Kotlin compilation plus direct JUnit execution passed 12 reducer/stream contract tests; full checks recorded in `CHANGELOG.md`. - [x] **B2 — Direct gateway connection, authentication, and local-first routing UX** — 2026-07-24, commit `e5cc27e` (`feat: add local-first gateway routing`). - Added one logical profile with optional Local HTTPS, required Remote HTTPS, Android-keystore-protected bearer/no-auth configuration, and active Local/Remote diagnostics. - Added bounded redirect-disabled `GET /health` probes followed by bearer-authenticated `GET /v1/models`, deterministic Local preference/Remote fallback, and immutable request leases. - Added foreground, explicit reconnect, connectivity-change, and request-failure re-evaluation hooks; removed Android calls to undocumented legacy companion `/api/*` routes without changing `apps/companion`. - Recorded the upstream stable gateway identity-binding need in `docs/BLOCKERS.md`. - Verification: direct Kotlin/JUnit execution of `ConnectionStateTest` and `GatewayHttpProbeTest` passed 12 tests; `GRADLE_USER_HOME=/root/hermes-mobile/.gradle-user ./gradlew --no-daemon -Pkotlin.compiler.execution.strategy=in-process :app:compileDebugUnitTestKotlin :app:assembleDebug` passed from `apps/mobile/android` with temporary restored sandbox-only Gradle socket shims (37 tasks, 6 executed); `git diff --check` passed. - Unhappy paths verified deterministically: missing/cleartext/credential-bearing/path-bearing/duplicate URLs, missing bearer token, incompatible or offline Local fallback, Remote offline, unauthorized compatibility response, stored legacy HTTP rejection, and failed-route lease invalidation. Device UI walkthrough was attempted but ADB could not start because this sandbox prohibits local control sockets. - [x] **F0 — Audit and safely land existing work** — 2026-07-24, commit `da79d48` (`feat: land native mobile foundation`). - Reviewed existing Android and companion edits; preserved the native Android chat/workspace/settings work and companion chat hardening. - Confirmed generated build outputs are ignored and added ignores for `.gradle-user/` and `.dev/`. - Reconciled `docs/ROADMAP.md` to the actual native Android beta state and remaining validation gaps. - Verification: `git diff --check` passed; `npm run typecheck --workspace @hermes-mobile/companion` passed; `npm run lint --workspace @hermes-mobile/companion` passed; `npm run build --workspace @hermes-mobile/companion` passed; `npm run typecheck` passed; `npm run lint` passed; `npm run build` passed. - Android debug build attempted with `GRADLE_USER_HOME=/root/hermes-mobile/.gradle-user ./gradlew :app:assembleDebug` from `apps/mobile/android`; blocked before compilation because network socket creation is not permitted while downloading Gradle 8.11.1. - [x] **F1 — Compose design system extraction** — 2026-07-24, commit `9ed0969` (`feat: extract compose design system`). - Extracted color, semantic status, spacing, typography, shape, elevation, and motion tokens. - Added light and dark Hermes Compose themes backed by Material 3 color schemes. - Split shared Compose cards, rows, controls, notices, message bubbles, list scaffolds, and code surfaces out of `MainActivity.kt`. - Added Compose previews for small phone, normal phone, tablet width, light theme, and dark theme component coverage. - Verification: `git diff --check` passed; `GRADLE_USER_HOME=/root/hermes-mobile/.gradle-user ./gradlew :app:assembleDebug` from `apps/mobile/android` passed. - [x] **F2 — Polished fake-data chat proving ground** — 2026-07-24, commit `f65d4ac` (`feat: add fake chat proving ground`). - Added a local fake chat inbox with session rail/list, context controls, keyboard-safe composer, and send/cancel/retry interactions. - Added typed user, assistant, system, error, and streaming message models; selectable markdown-friendly text, horizontally scrollable copyable code, and expandable tool results. - Added seeded empty, loading, reconnecting, and error states plus reducer tests for send, cancel, retry, and tool expansion. - Verification: `git diff --check` passed; `GRADLE_USER_HOME=/root/hermes-mobile/.gradle-user ./gradlew :app:testDebugUnitTest :app:assembleDebug` from `apps/mobile/android` passed. - [x] **B1 — Transport/domain contracts** — 2026-07-24, commit `feat: add transport domain contracts`. - Added transport DTOs and domain repository contracts for server profiles, authentication, sessions, messages, stream events, tool results, and artifacts. - Added DTO-to-domain/UI mappers, stream reducer behavior, and a fake Hermes repository for previews and unit tests. - Kept the companion path as an optional adapter capability without changing upstream Hermes semantics. - Verification: parent preflight immediately before B1 completion passed: script PASS; Android `:app:assembleDebug` BUILD SUCCESSFUL, 35 tasks total and 6 executed. Parent also verified `git diff --check` passed. - [x] **B2a — Direct gateway planning and compatibility contract** — 2026-07-24, commit `docs: pivot mobile contract to direct gateway`. - Added `docs/DIRECT_GATEWAY_ARCHITECTURE.md` with documented upstream endpoints separated from unverified assumptions. - Reprioritized the beta path around direct gateway auth, sessions, streaming, and later companion removal. - Updated Android transport/domain/fake capability contracts and connection copy so new code no longer assumes a companion server. - Verification: `git diff --check` passed; Android `:app:testDebugUnitTest :app:assembleDebug` passed from `apps/mobile/android`. ## Work-loop checklist Before coding: 1. Read `PROJECT_PLAN.md`, `CHANGELOG.md`, `docs/ROADMAP.md`, git status, and the prior commit. 2. Choose the single highest-value unchecked item or a narrowly scoped sub-item. 3. Inspect existing implementation before rewriting it. Before committing: 1. Run relevant checks; record exact pass/fail scope in the changelog. 2. Update this plan: check the completed item, move it into **Completed**, and leave the next executable item unchecked. 3. Add a concise `CHANGELOG.md` entry under **Unreleased**. 4. Commit using Conventional Commits and push `origin/main`. 5. Do not claim a release without an uploaded, verified versioned APK.