diff --git a/CHANGELOG.md b/CHANGELOG.md index ba77fa1..b843b46 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -57,6 +57,7 @@ All notable Hermes Mobile source changes are recorded here. Entries are added du - Ignored `.gradle-user/` and `.dev/` alongside existing generated build artifacts. ### Fixed +- Fixed first-run setup so a profile saved by a failed/offline compatibility test cannot bypass mandatory setup after process restart; only completed or migrated valid profiles skip the wizard. - Fixed durable stream completion so a stale request ID cannot publish a terminal state or clear a newer reservation. - Fixed stream failure handling so HTTP, I/O, and malformed-SSE failures are reported and reduced only against the original lease, while upstream `error` events remain session failures rather than route failures. - Fixed bearer handling so `/health` is checked without credentials and the token is sent only to the documented authenticated `/v1/models` compatibility route. @@ -64,6 +65,9 @@ All notable Hermes Mobile source changes are recorded here. Entries are added du - Fixed companion/mobile lint issues from missing Node globals and an unused React settings value. ### Verification +- `./gradlew --offline --no-daemon -Dorg.gradle.jvmargs= -Dorg.gradle.daemon=false -Pkotlin.compiler.execution.strategy=in-process :app:testDebugUnitTest --tests 'cloud.molberg.hermesmobile.onboarding.SetupWizardReducerTest'` — passed (28 tests), after the new regression first failed as expected. +- `./gradlew --offline --no-daemon -Dorg.gradle.jvmargs= -Dorg.gradle.daemon=false -Pkotlin.compiler.execution.strategy=in-process :app:testDebugUnitTest :app:assembleDebug` — passed (43 tasks, 4 executed). +- `git diff --check` — passed for the setup restart-bypass fix. - First-run Setup Wizard source verification: the source-current offline Android `:app:testDebugUnitTest :app:assembleDebug` run passed on 2026-07-24 with 43 tasks (4 executed, 39 up-to-date) and 104 tests (0 failures); a final identical rerun was blocked before project execution by the sandbox TCP restriction. `git diff --check` passed. No device, release, or real gateway credential test is claimed. - `/root/.openclaw/workspace/scripts/hermes-mobile-preflight.sh` — passed; Android `:app:assembleDebug` BUILD SUCCESSFUL (37 tasks, 14 executed). - `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, 5 executed), including `BetaPackageMetadataTest` for application ID `cloud.molberg.hermesmobile`, `versionName` `0.1.0-beta.1`, and `versionCode` `10001`. diff --git a/PROJECT_PLAN.md b/PROJECT_PLAN.md index 01d6d4a..70aac82 100644 --- a/PROJECT_PLAN.md +++ b/PROJECT_PLAN.md @@ -53,11 +53,12 @@ Complete the Android/client contract pivot before implementing backend behavior. _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, commit `1b4abfa` (`feat: add first-run setup wizard`). +- [x] **P0 — First-run Setup Wizard** — 2026-07-24, commits `1b4abfa` (`feat: add first-run setup wizard`) and pending follow-up (`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. - - Verification on 2026-07-24: the source-current offline `:app:testDebugUnitTest :app:assembleDebug` run passed (43 tasks, 4 executed; 104 tests, 0 failures), and `git diff --check` passed. A final identical Gradle rerun was blocked before project execution by the sandbox TCP restriction (`java.net.SocketException: Operation not permitted`). No device, release, or real gateway credential test is claimed. + - 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. + - 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. - [x] **R2a — Establish beta source packaging and install documentation** — 2026-07-24, commit `98b183e` (`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. diff --git a/apps/mobile/android/app/src/main/java/cloud/molberg/hermesmobile/onboarding/SetupWizardModels.kt b/apps/mobile/android/app/src/main/java/cloud/molberg/hermesmobile/onboarding/SetupWizardModels.kt index 5e96b46..8676b2a 100644 --- a/apps/mobile/android/app/src/main/java/cloud/molberg/hermesmobile/onboarding/SetupWizardModels.kt +++ b/apps/mobile/android/app/src/main/java/cloud/molberg/hermesmobile/onboarding/SetupWizardModels.kt @@ -262,7 +262,11 @@ object SetupWizardReducer { hasLegacyConfiguration && existing return SetupGateResolution( - entry = entry, + entry = if (setupVersion >= CURRENT_SETUP_VERSION || migrateExistingUser) { + entry + } else { + SetupEntry.Mandatory(firstRun()) + }, migrateExistingUser = migrateExistingUser ) } diff --git a/apps/mobile/android/app/src/test/java/cloud/molberg/hermesmobile/onboarding/SetupWizardReducerTest.kt b/apps/mobile/android/app/src/test/java/cloud/molberg/hermesmobile/onboarding/SetupWizardReducerTest.kt index 650cbe0..4af2b96 100644 --- a/apps/mobile/android/app/src/test/java/cloud/molberg/hermesmobile/onboarding/SetupWizardReducerTest.kt +++ b/apps/mobile/android/app/src/test/java/cloud/molberg/hermesmobile/onboarding/SetupWizardReducerTest.kt @@ -214,7 +214,22 @@ class SetupWizardReducerTest { } @Test - fun currentOrNonLegacyExistingConfigDoesNotRequestMigration() { + fun failedFirstRunSavedProfileCannotSkipMandatorySetupAfterRestart() { + val resolution = SetupWizardReducer.resolveEntry( + config = ConnectionConfig( + remoteUrl = REMOTE, + authMode = ConnectionAuthMode.None + ), + setupVersion = 0, + hasLegacyConfiguration = false + ) + + assertTrue(resolution.entry is SetupEntry.Mandatory) + assertFalse(resolution.migrateExistingUser) + } + + @Test + fun completedExistingConfigSkipsWithoutRequestingMigration() { val config = ConnectionConfig( remoteUrl = REMOTE, authMode = ConnectionAuthMode.None @@ -225,16 +240,9 @@ class SetupWizardReducerTest { setupVersion = SetupWizardReducer.CURRENT_SETUP_VERSION, hasLegacyConfiguration = true ) - val nonLegacy = SetupWizardReducer.resolveEntry( - config = config, - setupVersion = 0, - hasLegacyConfiguration = false - ) assertTrue(current.entry is SetupEntry.ExistingConfiguration) assertFalse(current.migrateExistingUser) - assertTrue(nonLegacy.entry is SetupEntry.ExistingConfiguration) - assertFalse(nonLegacy.migrateExistingUser) } @Test diff --git a/docs/ROADMAP.md b/docs/ROADMAP.md index 6518da8..199043c 100644 --- a/docs/ROADMAP.md +++ b/docs/ROADMAP.md @@ -7,7 +7,7 @@ - Welcome/privacy guidance precedes configuration; Remote HTTPS is required and Local HTTPS is optional for the same logical gateway profile. - Authentication choices are None, Bearer token, or username/password. Password login exchanges credentials directly with the configured remote gateway and stores encrypted credentials/session state outside Compose UI models. - Test-and-finish uses the existing local-first route selection, requires a successful compatibility check before completion, and exposes the same profile for later Settings edits. -- Existing valid profiles skip mandatory setup; legacy/current valid profiles receive a one-time setup-version migration without forcing re-entry. +- Existing completed valid profiles skip mandatory setup; legacy/current valid profiles receive a one-time setup-version migration without forcing re-entry. A failed/offline first-run test cannot create a restart bypass. - Compose semantics, minimum touch targets, adaptive layout behavior, and phone/tablet/light/dark previews cover the wizard source contract. - Source verification passed with 104 unit tests and debug assembly. No real device, release, or real gateway credential walkthrough has been performed; those remain part of R1/R2 evidence.