149 lines
19 KiB
Markdown
149 lines
19 KiB
Markdown
# 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.
|
|
|
|
- [ ] **R2 — Verify a clean `v0.1.0-beta.1` install against a direct gateway**
|
|
- On one named real device or emulator, build the source-current versioned debug APK, confirm package metadata, uninstall prior app data, install and launch it, configure an upstream Hermes gateway directly, and record the connection result plus APK SHA-256.
|
|
- Verification: required preflight and Android checks pass in the install environment; device/install evidence is recorded in `docs/ROADMAP.md`; no release or final beta criterion is claimed from source-only checks.
|
|
|
|
## Externally blocked
|
|
|
|
- **R1 — Execute and document the adaptive/accessibility device matrix**
|
|
- Source safeguards and 61 cached source-current unit tests were verified on 2026-07-24, but small-phone, normal-phone, tablet, TalkBack, contrast, motion, and IME evidence still requires a permitted real device or emulator.
|
|
- Unblock evidence is maintained in `docs/BLOCKERS.md`; do not mark R1 complete from source review alone.
|
|
- **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.
|
|
|
|
## 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] **P0 — First-run Setup Wizard** — 2026-07-24, commits `5b3263e` (`feat: add first-run setup wizard`) and `7780760` (`fix: prevent setup restart bypass`).
|
|
- Added a Compose first-run flow covering welcome/privacy guidance, required Remote HTTPS and optional Local HTTPS routes, None/Bearer/username-password authentication, local-first connection testing, and completion only after a successful test.
|
|
- Added direct password login through `/auth/password-login`, allowlisted session-cookie extraction, encrypted cookie/password storage, credential redaction, replacement/retention rules, and cookie-authenticated gateway probes without persisting raw credentials in UI state or connection forms.
|
|
- Added Settings entry for editing the same profile, skip behavior for existing valid configurations, one-time legacy/current-profile setup-version migration, and accessible adaptive Compose semantics plus phone/tablet/light/dark previews.
|
|
- Fixed a restart bypass: a profile saved by a failed first-run connection test remains in mandatory setup until setup completion is durably recorded; valid migrated and completed profiles still skip setup.
|
|
- Repaired the setup gate so only valid profiles with recorded legacy provenance receive the one-time completion migration; untested first-run None and Bearer profiles remain mandatory after restart.
|
|
- Follow-up regression repair on 2026-07-25: removed bearer-token-only self-migration, which could classify a failed/offline first-run Bearer profile as a historical profile. Added a focused restart regression covering that state.
|
|
- Verification for the follow-up repair: offline focused `SetupWizardReducerTest` plus `:app:assembleDebug` passed (43 tasks, 9 executed); `git diff --check` passed.
|
|
- Verification for the migration repair: source-current offline focused `SetupWizardReducerTest` plus `:app:assembleDebug` passed (43 tasks, 9 executed); `git diff --check` passed.
|
|
- Verification on 2026-07-24: source-current offline `:app:testDebugUnitTest :app:assembleDebug` passed (43 tasks, 4 executed; all unit tests passed), including the focused restart-regression test; `git diff --check` passed. No device, release, or real gateway credential test is claimed.
|
|
- Task 5 source-current re-verification on 2026-07-24: `/root/.openclaw/workspace/scripts/hermes-mobile-preflight.sh` passed; the documented offline `:app:testDebugUnitTest :app:assembleDebug` command passed (43 tasks, 1 executed); the generated Gradle XML report records 106 tests with 0 failures/errors, including 29 `SetupWizardReducerTest` cases; and `git diff --check` passed. A localhost gateway health probe returned HTTP 200, establishing reachability only. The debug APK reports package `cloud.molberg.hermesmobile`, version `0.1.0-beta.1` (`10001`), SHA-256 `45e4ab4783a74eb766070b0f670a2631a9ebdc044468bbe03087788f8106f150`. No ADB device/emulator or `/dev/kvm` was available; no device installation, real authentication, release, or end-to-end gateway verification is claimed.
|
|
- Later Task 5 current-sandbox rerun on 2026-07-24: the exact `docs/ANDROID_BETA_BUILD.md` Gradle command was blocked before project execution with `Could not determine a usable wildcard IP`, and the recorded offline/no-daemon variant was blocked while opening Gradle's daemon TCP socket with `java.net.SocketException: Operation not permitted`. Direct JUnit execution of the existing source-current compiled `SetupWizardReducerTest` classes passed all 29 tests. The existing source-current XML reports remain 106 tests with 0 failures/errors, and the existing ignored APK was unchanged by both Gradle attempts; `aapt` and SHA-256 inspection reconfirmed the package/version/hash above. ADB 37.0.0 was available only by explicit path and could not start its daemon because smart-socket/netlink sockets are prohibited; `/dev/kvm` and `/dev/bus/usb` were absent. This adds fresh focused reducer execution only: no fresh Gradle compilation/test task, APK assembly, device installation, credential flow, release, or end-to-end verification is claimed.
|
|
- Task 5 delivery verification on 2026-07-25: `/root/.openclaw/workspace/scripts/hermes-mobile-preflight.sh` passed; from `apps/mobile/android`, `GRADLE_USER_HOME=/root/hermes-mobile/.gradle-user ./gradlew --offline --no-daemon -Dorg.gradle.jvmargs= -Dorg.gradle.daemon=false -Pkotlin.compiler.execution.strategy=in-process :app:testDebugUnitTest :app:assembleDebug` passed (43 tasks, 1 executed); `git diff --check` passed. Current Gradle XML reports record 107 tests with 0 failures/errors/skips. The ignored debug APK reports package `cloud.molberg.hermesmobile`, version `0.1.0-beta.1` (`10001`), size 17,710,960 bytes, and SHA-256 `714e8e9cdd3f2eed93b285f233b1f4573d5d13fc2078bb717631f9b38f8428d4`. ADB reports no attached devices; no emulator/AVD or `/dev/kvm` is available. No device installation, release, or gateway end-to-end verification is claimed.
|
|
- [x] **R2a — Establish beta source packaging and install documentation** — 2026-07-24, commit `3b883bd` (`build: establish Android beta packaging`).
|
|
- Set Android package metadata to semantic `versionName` `0.1.0-beta.1` with monotonic `versionCode` `10001`, enabled generated metadata coverage, and named the debug APK `hermes-mobile-v0.1.0-beta.1-debug.apk`.
|
|
- Added a direct-gateway-only build, metadata inspection, clean-install, launch, configuration, evidence, and known-limitations guide; changed the npm Android build command to run the native Gradle project without Capacitor synchronization.
|
|
- Source verification on 2026-07-24: required recovery preflight passed; `:app:testDebugUnitTest :app:assembleDebug` passed with the cached offline Gradle setup (43 tasks, 5 executed); and `git diff --check` passed. No APK was published, uploaded, or installed, so the remaining R2 device task and all final release criteria remain unclaimed.
|
|
- [x] **B5 — Tool, artifact, and output experience** — 2026-07-24, commit `feat: add gated tool output experience`.
|
|
- Added capability-gated decoding and reduction for the existing `tool.started`, `tool.progress`, `tool.completed`, and `tool.failed` direct gateway SSE events, with compact lifecycle/status cards for referenced and stream-only tools.
|
|
- Added full-output copy plus deterministic collapsed/expanded display windows capped at 5/80 lines and 24,000 expanded characters, avoiding same-direction nested scrolling while retaining the complete output in state.
|
|
- Persisted expanded tool IDs in durable chat state so rotation/process resume restores presentation state; artifact share/save remains unavailable because the direct gateway advertises no verified artifact handoff URI/download capability.
|
|
- Verification: the recovery preflight passed before this run; cached Kotlin 2.0.21 compilation passed for all Android Kotlin/Compose main and unit-test sources; direct JUnit passed 57 tests including large-output bounds, capability gating, lifecycle merging, and rotation/resume restoration; `git diff --check` passed. A direct Gradle `:app:testDebugUnitTest :app:assembleDebug` retry was blocked before project execution because the sandbox prohibits Gradle's TCP control socket (`java.net.SocketException: Operation not permitted`).
|
|
|
|
- [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.
|