From e4c2b998b943d966089506c11c5dcf5cf44d0d57 Mon Sep 17 00:00:00 2001 From: Luis Guzman Date: Thu, 3 Sep 2026 12:00:58 -0600 Subject: [PATCH 01/11] K2GO-380 docs(post-rebrand): device verification findings log + global test matrix (WIP) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Records the org.iiab.controller -> org.appdevforall.k2go rebrand device sweep: static rebrand-safety recon, the global test matrix (identity- sensitive first, then functional), a live-results log, and finding F1 (uninstalled content cards read RED instead of GRAY after a backup — pre-existing status-derivation bug the rebrand's clean install exposes). Live results so far all PASS: FileProvider registered under the new package, content endpoints serving, OTA host reachable, Firebase inits under the new package. WIP; UI/heavy checks pending. --- controller/docs/POST_REBRAND_DEVICE_TESTS.md | 136 +++++++++++++++++++ 1 file changed, 136 insertions(+) create mode 100644 controller/docs/POST_REBRAND_DEVICE_TESTS.md diff --git a/controller/docs/POST_REBRAND_DEVICE_TESTS.md b/controller/docs/POST_REBRAND_DEVICE_TESTS.md new file mode 100644 index 00000000..73df5ecb --- /dev/null +++ b/controller/docs/POST_REBRAND_DEVICE_TESTS.md @@ -0,0 +1,136 @@ +# Post-rebrand device verification — findings & global test matrix + +**Context.** The app identifier moved from `org.iiab.controller` to +`org.appdevforall.k2go` (ADR-5368). Because an `applicationId` change is a *new app* +(empty private data, fresh install), this document (a) records what the rebrand could +have broken and how we verify it on a real device, and (b) logs the bugs that surface +while testing. It complements — does not replace — the verification matrix in +`ADR-5368-app-identifier-rebrand.md` §10; that matrix proves *the rename itself*, this +one is the *global* app sweep and the running findings log. + +Test device for this pass: **OnePlus 7T (HD1901), arm64-v8a, API level TBD.** + +--- + +## 1. Rebrand-safety recon (static) + +- **No old-package references reachable at runtime in app source.** Every `setPackage(...)` + passes `getPackageName()` (never a literal); `ComponentName(...)` targets are *other* apps + (Slack share, OEM battery-settings activities), not us; there is no `run-as ` nor + hardcoded `/data/data/org.iiab...` path in `controller/app/src`. Matches ADR-5368 §7.3. +- **Deliberately deferred (ADR-5368 §11), inert but present:** + - 42 internal intent-action literals still read `org.iiab.controller.*` + (`ACTION_*` constants in `InstallService`, `WatchdogService`, `DeepOpService`, + `CloneShareService`, `KolibriSeedService`, `IIABWatchdog`, `IIABAdbManager`). Each is a + single `public static final` shared by sender + filter, so they are internally consistent; + proven inert by ADR-5368 check 13. **Test 13 below re-confirms on device.** + - `RsyncProcessMatcherTest:36` uses the bare `"org.iiab.controller"` as a *negative* fixture + (intentional). + - Docs/runbooks still say `run-as org.iiab.controller` (ADR-5368 §11.2 sub-phase) — a `run-as` + copied from an old ADR will now fail; use `org.appdevforall.k2go`. +- **Firebase — resolved for the built APK; only local dev copy stale.** The installed release APK + runs `FirebaseInitProvider` under `org.appdevforall.k2go` (confirmed via `dumpsys package`), so a + valid `google-services.json` with a `org.appdevforall.k2go` client exists and was used to build it. + The gate is *local only*: this workstation's `controller/app/google-services.json` still carries + `"package_name": "org.iiab.controller"`, so a build here fails at `processGoogleServices` until the + local file (and the CI secret `GOOGLE_SERVICES_JSON_K2GO_ANALYTICS`, if not already) is refreshed + from the console. Not a shipped-artifact risk. (ADR-5368 §7.2 / §10.4.) +- **Device confirms:** `pm path org.appdevforall.k2go` resolves; `pm path org.iiab.controller` + is *not installed* (ADR-5368 checks 1–2). ✔ + +--- + +## 2. Findings log + +### F1 — Uninstalled content cards show RED ("unavailable"), not GRAY ("not installed"), after a backup + +**Severity:** medium (misleading status; no data loss). **Rebrand-caused:** no. **Rebrand-exposed:** yes. + +**Symptom.** Create a backup, press **Finish** → returns to Library/Home. Content platforms that +are *not installed* render with the **red** dot ("unavailable"/stuck) instead of the **gray** +"Not installed" dot. + +**Mechanism.** `LibraryHomeFragment.refreshStatuses()` +([:568-585](../app/src/main/java/org/appdevforall/k2go/redesign/LibraryHomeFragment.java#L568)), +server-alive branch: each card **freshly probes** its endpoint — +`PRESENT → green`, `ABSENT (404 = not installed) → gray`, otherwise **indeterminate** → +`RED` once `serverAliveSinceMs` grace (60 s) has passed, else amber. A backup **stops and +restarts** the box; on return, nginx/dash-node routing is still warming, so a probe to a +not-installed platform does **not** get a clean `ABSENT (404)` — it lands in the *indeterminate* +bucket and paints **red**. The offline branch +([:563-566](../app/src/main/java/org/appdevforall/k2go/redesign/LibraryHomeFragment.java#L563)) +does the right thing (`PlatformEvidence.last(...) == ABSENT ? GRAY : AMBER`), but the alive +branch **discards the already-known "not installed" fact** on a transient indeterminate probe. + +**Design read (CLAUDE.md coherence).** A fact with an owner ("this platform is not installed", +recorded in `PlatformEvidence`) is re-derived — wrongly — from a transient probe. The fix is to +*fall back to last-known evidence* before deciding RED: an endpoint last seen `ABSENT` should stay +gray through a warm-up indeterminate probe; RED should require either no prior verdict or a prior +non-absent one. Not a new flag — reuse the evidence the offline branch already trusts. + +**Why the rebrand exposed it.** The renamed app is a clean install (nothing installed), so after a +backup *every* content card takes the indeterminate branch at once; on the pre-rebrand app with +content present those cards were green and the path never showed. + +**Repro (device):** install rootfs → create a backup → Finish → observe Home cards. **TODO: +capture on the OnePlus and confirm whether `serverAliveSinceMs` is reset across the deep-op (if +not, RED is immediate, with no amber grace).** + +--- + +## 3. Global device test matrix + +Grouped identity-sensitive first (what an `applicationId` change can actually break), then the +broad functional sweep. "How" is the device action; "Expected" is pass; the note says why it is +identity-sensitive. Heavy rows (⬇ needs a full download / ⇄ needs a second device) are marked. + +### 3.1 Identity-sensitive (the rename's blast radius) + +| # | Area | How on device | Expected | Why identity-sensitive | +|---|---|---|---|---| +| I1 | Install identity | `pm path org.appdevforall.k2go` / `…org.iiab.controller` | new resolves, old absent | the change itself — **PASS (verified)** | +| I2 | FileProvider (clone) | Clone → Send → "Can't scan? Share another way" | share sheet opens with the APK attached | authority `${applicationId}.provider`; a mismatch throws before the sheet — ADR check 10 | +| I3 | FileProvider (feedback) | Feedback → attach diagnostics → send | attachment resolves via `getPackageName()+".provider"` | same authority, second call site (`EmailFeedbackSender:28`) | +| I4 | FileProvider (OTA) | Trigger an update install | package installer opens the staged APK | `UpdateController:344` — the updater's own authority | +| I5 | Intent actions (42 legacy) | Stop/start server; run a module install; run a backup | services respond | 42 constants still `org.iiab.controller.*`; proven inert by ADR check 13 — re-confirm | +| I6 | Notification channels | Any FG service (install/backup/clone/terminal) | notification shows in its channel | new app = new channels; 9 `specialUse` FG services | +| I7 | Private dir / `iiab` CLI | Terminal → run `iiab` | CLI works, paths under new private dir | `TerminalController:647` regenerates CLI from `getFilesDir()` | +| I8 | proot boot | Cold start | UP, 0 spurious kills | binds derive from `getFilesDir()`; ADFA-5365 intact | +| I9 | proot seccomp verdict | First launch on affected device | one ADFA-5362 learn line, then `PROOT_NO_SECCOMP=1` | verdict keyed on `versionCode` → re-learns (expected, not a regression) | +| I10 | OTA signer pin | new→new update | updates in place | `ApkVerifier`/`CertDigests` — rebuilt APK must carry the pinned cert | +| I11 | Debug delivery (debug builds) | `am broadcast -a org.appdevforall.k2go.DEBUG_DELIVERY -n org.appdevforall.k2go/…DebugDeliveryReceiver` | receiver enqueues | the ONE new-namespace action; must match new id | +| I12 | Firebase/analytics | build + first run | google-services matches package | **release gate open** — needs `org.appdevforall.k2go` Firebase client (§1) | + +### 3.2 Functional sweep (rebrand must not have moved anything) + +| # | Area | How on device | Expected | Heavy | +|---|---|---|---|---| +| F-a | Fresh install E2E | uninstall → install APK → complete a rootfs install | reaches services, `/k2go-api` 200 | ⬇ | +| F-b | Content: ZIM/Wikipedia | open Explore Wikipedia | article loads (`/kiwix/` 200 — **verified serving**) | | +| F-c | Content: Kolibri/courses | open Take courses | topics load | | +| F-d | Content: Books | open Read a book | library loads | | +| F-e | Content: Maps | open Navigate maps | tiles render | | +| F-f | Get-More install | install one not-present module | downloads, card → green | ⬇ | +| F-g | Backup create | Settings → backup → Finish | archive written; **watch F1: not-installed cards must read gray, not red** | ⬇ | +| F-h | Restore | restore the backup | round-trips, boots healthy | ⬇ | +| F-i | Clone send/receive | between two new-id devices | pairing + transfer complete | ⇄ ⬇ | +| F-j | Dashboard rebuild | trigger a rebuild | completes, log tails | | +| F-k | OTA check | Settings → check for update | reports latest / offers update | | +| F-l | Terminal | open terminal, run a command | Debian shell responds | | +| F-m | Connect | hotspot QR (K2GO-375 fix) | QR resolves to real AP IP, no `…49.1` | | +| F-n | Feedback | send feedback (mailto default) | mail composer opens to `feedback+k2go@appdevforall.org` | | +| F-o | Permissions | location / battery / unknown-apps prompts | each deep-links to the app's settings | | +| F-p | Notifications | any FG service | POST_NOTIFICATIONS honored, channel shows | | + +### 3.3 Live results log + +| Check | Result | Notes | +|---|---|---| +| I1 | ✅ PASS | new pkg resolves, old absent | +| I2/I3/I4 (static) | ✅ PASS | `androidx.core.content.FileProvider` registered under `org.appdevforall.k2go`; authority `${applicationId}.provider`. Runtime share-sheet call still to do (UI). | +| I8 | ✅ PASS | server up (`/home` 301); Home reads all platforms "Ready" | +| I12 Firebase | ✅ PASS (build) | `FirebaseInitProvider` runs under new pkg → valid new-package `google-services.json` was used; only local dev copy stale | +| F-b/c/d/e content | ✅ PASS | kiwix=200, kolibri=302, books=200, maps=200 — all present; cards accurate (no false-green) | +| F-k OTA host | ✅ PASS | `https://k2go-download.appdevforall.org/update.json` → 200 from device (CF R2 reachable, network-security-config allows) | +| — | | Build is **release** (`run-as` denied), so on-device fs introspection is limited; use REST probes. | +| I2 runtime, I5, I7, F-g backup/F1, F-m Connect QR, OTA install | ⏳ pending | UI-driven / heavy; next batch | From f03cd618de035aec59403a829941e91ae7cdf496 Mon Sep 17 00:00:00 2001 From: Luis Guzman Date: Thu, 3 Sep 2026 12:25:23 -0600 Subject: [PATCH 02/11] K2GO-380 docs(post-rebrand): log I2 (FileProvider runtime) + F-m (hotspot QR) PASS on device Live UI checks on the OnePlus 7T (release build, org.appdevforall.k2go): - I2 FileProvider runtime: Clone->Send->"share another way" opens the Android share sheet (base.apk); getUriForFile resolves under the new authority. ADR-5368 check 10 independently confirmed. - F-m: hotspot join QR renders, LocalOnlyHotspot starts, get-app QR renders under the new package. K2GO-375 fix intact. --- controller/docs/POST_REBRAND_DEVICE_TESTS.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/controller/docs/POST_REBRAND_DEVICE_TESTS.md b/controller/docs/POST_REBRAND_DEVICE_TESTS.md index 73df5ecb..799fe600 100644 --- a/controller/docs/POST_REBRAND_DEVICE_TESTS.md +++ b/controller/docs/POST_REBRAND_DEVICE_TESTS.md @@ -132,5 +132,8 @@ identity-sensitive. Heavy rows (⬇ needs a full download / ⇄ needs a second d | I12 Firebase | ✅ PASS (build) | `FirebaseInitProvider` runs under new pkg → valid new-package `google-services.json` was used; only local dev copy stale | | F-b/c/d/e content | ✅ PASS | kiwix=200, kolibri=302, books=200, maps=200 — all present; cards accurate (no false-green) | | F-k OTA host | ✅ PASS | `https://k2go-download.appdevforall.org/update.json` → 200 from device (CF R2 reachable, network-security-config allows) | +| I2 runtime | ✅ PASS | Clone→Send→"Share the app another way" → Android share sheet opened ("Sharing 1 file · base.apk"); `getUriForFile(getPackageName()+".provider", …)` resolves at runtime under `org.appdevforall.k2go.provider`. The rename's sharpest test (ADR check 10), independently confirmed. | +| F-m Connect/hotspot QR | ✅ PASS | Clone→Send: join-hotspot QR renders, `LocalOnlyHotspot` starts (SSID AndroidShare_7317) under the new package; get-app QR (ApkServer) renders. K2GO-375 fix intact on the renamed app. | | — | | Build is **release** (`run-as` denied), so on-device fs introspection is limited; use REST probes. | -| I2 runtime, I5, I7, F-g backup/F1, F-m Connect QR, OTA install | ⏳ pending | UI-driven / heavy; next batch | +| I5, I7, OTA install | ➖ ADR-covered | Exercised in ADR-5368 §10.2 (checks 12–13, 11); re-confirm opportunistically. | +| F-g backup / F1 repro | ⏳ next | Needs a **basic-tier** install (fewer platforms present) → uninstall + reinstall (debug build, Firebase off). | From cbb9fffb964a55fd3397456cfa9e72df894ac5f0 Mon Sep 17 00:00:00 2001 From: Luis Guzman Date: Thu, 3 Sep 2026 12:46:06 -0600 Subject: [PATCH 03/11] K2GO-380 docs(post-rebrand): device backup E2E PASS; F1 not reproduced on base tier Device run (OnePlus 7T, debug build, base tier): clean install + base system install, full backup (1.65 GB, k2go_ prefix, DeepOpService), Finish -> Home all PASS under the new package. F1 (uninstalled cards RED after backup) did NOT reproduce on base tier -- cards read GRAY correctly; the red window is a narrow post-restart transient, likely wider on slower/full -tier restarts. Retracted an earlier backup-naming false alarm (mis-tap on an old file, not a real inconsistency). --- controller/docs/POST_REBRAND_DEVICE_TESTS.md | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/controller/docs/POST_REBRAND_DEVICE_TESTS.md b/controller/docs/POST_REBRAND_DEVICE_TESTS.md index 799fe600..4acd1cef 100644 --- a/controller/docs/POST_REBRAND_DEVICE_TESTS.md +++ b/controller/docs/POST_REBRAND_DEVICE_TESTS.md @@ -72,9 +72,17 @@ non-absent one. Not a new flag — reuse the evidence the offline branch already backup *every* content card takes the indeterminate branch at once; on the pre-rebrand app with content present those cards were green and the path never showed. -**Repro (device):** install rootfs → create a backup → Finish → observe Home cards. **TODO: -capture on the OnePlus and confirm whether `serverAliveSinceMs` is reset across the deep-op (if -not, RED is immediate, with no amber grace).** +**Device repro attempt (OnePlus 7T, base tier, debug build):** clean install → base-tier system → +backup (1.65 GB) → Finish → Home. Result: the two not-installed cards ("Read a book", "Take courses") +correctly read **GRAY / Not installed — NOT red**. **F1 did not reproduce this run.** + +Why it likely didn't show here: the RED path needs the post-restart probe to land *indeterminate* +(nginx back up but the platform's upstream still 502 during warm-up). On this small base-tier system +the server restart was fast, so the probe got a clean `ABSENT (404)` → GRAY before any red frame. The +red window is a **narrow transient**, plausibly wider when the restart is slower (e.g. a full-tier +system, or a slower device — matching where it was first seen). The code defect stands (the alive +branch discards the known-ABSENT on an indeterminate probe); the *visible* repro is timing-dependent. +Open question for the reporter: which tier / how long after Finish was the red seen? --- @@ -136,4 +144,8 @@ identity-sensitive. Heavy rows (⬇ needs a full download / ⇄ needs a second d | F-m Connect/hotspot QR | ✅ PASS | Clone→Send: join-hotspot QR renders, `LocalOnlyHotspot` starts (SSID AndroidShare_7317) under the new package; get-app QR (ApkServer) renders. K2GO-375 fix intact on the renamed app. | | — | | Build is **release** (`run-as` denied), so on-device fs introspection is limited; use REST probes. | | I5, I7, OTA install | ➖ ADR-covered | Exercised in ADR-5368 §10.2 (checks 12–13, 11); re-confirm opportunistically. | -| F-g backup / F1 repro | ⏳ next | Needs a **basic-tier** install (fewer platforms present) → uninstall + reinstall (debug build, Firebase off). | +| F-a fresh install E2E | ✅ PASS | Clean debug install → base-tier system installed & served (~2 min); Home shows a correct mix (books/courses Not installed, code/wikipedia/maps Ready) | +| F-g backup E2E | ✅ PASS | Settings→Backups→Back up → SAF picker default name `k2go_2026.246_…` (rebranded, single file) → 1.65 GB tar written by DeepOpService → "Backup saved" → Finish returns to Home. Backup writer + deep-op work under the new package. | +| F1 repro (backup→Home) | ⚠ NOT reproduced (this run) | not-installed cards read GRAY correctly after Finish on base tier; red window is a narrow transient (see §2 F1). Retest on full tier / slower restart. | +| Backup naming false-alarm | ✅ retracted | earlier "k2go_ vs iiab-oa_ inconsistency" was a mis-tap selecting an old backup file; the real default is `k2go_…`. | +| I7 terminal, F-h restore, F-i clone | ⏳ later | debug build now enables `run-as` for deeper checks | From 0a91d46d9a1dbac40c4d4e6629d0d0c42a0fec72 Mon Sep 17 00:00:00 2001 From: Luis Guzman Date: Thu, 3 Sep 2026 13:49:34 -0600 Subject: [PATCH 04/11] K2GO-380 docs(post-rebrand): F1 not reproduced on full tier either; working conclusion Full-tier retry (2.43 GB backup, screen-recorded Finish->Home): all cards Ready immediately, no red flash. F1 (red-instead-of-gray after backup) did not visibly reproduce on device across base or full tiers on this build; Home renders after services answer, so cards resolve straight to green/gray. The code defect stands by inspection; the visible symptom needs the reporter's exact build/tier/timing to reproduce. --- controller/docs/POST_REBRAND_DEVICE_TESTS.md | 22 +++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/controller/docs/POST_REBRAND_DEVICE_TESTS.md b/controller/docs/POST_REBRAND_DEVICE_TESTS.md index 4acd1cef..c64379d6 100644 --- a/controller/docs/POST_REBRAND_DEVICE_TESTS.md +++ b/controller/docs/POST_REBRAND_DEVICE_TESTS.md @@ -78,11 +78,23 @@ correctly read **GRAY / Not installed — NOT red**. **F1 did not reproduce this Why it likely didn't show here: the RED path needs the post-restart probe to land *indeterminate* (nginx back up but the platform's upstream still 502 during warm-up). On this small base-tier system -the server restart was fast, so the probe got a clean `ABSENT (404)` → GRAY before any red frame. The -red window is a **narrow transient**, plausibly wider when the restart is slower (e.g. a full-tier -system, or a slower device — matching where it was first seen). The code defect stands (the alive -branch discards the known-ABSENT on an indeterminate probe); the *visible* repro is timing-dependent. -Open question for the reporter: which tier / how long after Finish was the red seen? +the server restart was fast, so the probe got a clean `ABSENT (404)` → GRAY before any red frame. + +**Full-tier retry (to test whether backup *duration* is the determinant):** reinstalled to a full +system (all five platforms end up "Ready", so no not-installed targets remain), backed up (2.43 GB +tar — heavier/longer than base), Finish → Home, with a 40 s screen recording over the transition. +Result: all cards read **Ready (green)** immediately after Finish — **no red flash in the still**, and +the recording compressed to ~0.6 MB (near-static, consistent with no flashing). Sent to the reporter +to scrub for any sub-second flash. + +**Working conclusion:** F1 as reported (red after backup) **did not visibly reproduce on device** +across base and full tiers on this build (`f03cd618`, debug). By the time Home renders after Finish, +services already answer, so cards resolve straight to green/gray — suggesting the deep-op restarts and +waits for a healthy server *before* returning, so Home never observes the intermediate 502 window via +this path. The code defect is still real by inspection (the alive branch discards a known `ABSENT` on +an indeterminate probe), so the fix stands regardless; but the visible symptom needs the reporter's +exact conditions (build, tier, timing, how long the red persisted) to reproduce, or it may already be +gone on this build. --- From bfacda2c1288c2501ead69ad07818d4c2712a441 Mon Sep 17 00:00:00 2001 From: Luis Guzman Date: Thu, 3 Sep 2026 15:28:23 -0600 Subject: [PATCH 05/11] =?UTF-8?q?K2GO-380=20docs(post-rebrand):=20ADR-5368?= =?UTF-8?q?=20=C2=A710=20device=20sweep=20=E2=80=94=20single-device=20chec?= =?UTF-8?q?ks=20pass;=20pristine=20verdict?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Device batch (debug, full install): private dir under new pkg (3), cold boot UP with 0 kills via K2Go-Reconciler (8), proot runs (9), FG watchdog notification under new pkg+channel (14), maps landing + FqrController overlays render (18), legacy intent actions drive live flows (13). Every single-device ADR §10 check passed; only OTA new->new (11) and 2-device clone (15) remain, needing external setup (both reasoned-not-observed in the ADR). Restore (16) and dashboard rebuild (17) optional. Verdict: the rebranded APK behaves as pristine for single-device operation. --- controller/docs/POST_REBRAND_DEVICE_TESTS.md | 25 ++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/controller/docs/POST_REBRAND_DEVICE_TESTS.md b/controller/docs/POST_REBRAND_DEVICE_TESTS.md index c64379d6..20f5c492 100644 --- a/controller/docs/POST_REBRAND_DEVICE_TESTS.md +++ b/controller/docs/POST_REBRAND_DEVICE_TESTS.md @@ -161,3 +161,28 @@ identity-sensitive. Heavy rows (⬇ needs a full download / ⇄ needs a second d | F1 repro (backup→Home) | ⚠ NOT reproduced (this run) | not-installed cards read GRAY correctly after Finish on base tier; red window is a narrow transient (see §2 F1). Retest on full tier / slower restart. | | Backup naming false-alarm | ✅ retracted | earlier "k2go_ vs iiab-oa_ inconsistency" was a mis-tap selecting an old backup file; the real default is `k2go_…`. | | I7 terminal, F-h restore, F-i clone | ⏳ later | debug build now enables `run-as` for deeper checks | + +### 3.4 ADR-5368 §10 matrix — device confirmation (debug build, full install) + +| ADR check | Result | Evidence | +|---|---|---| +| 3 Private dir | ✅ | `run-as … ls files/` → INSTALLATION, rootfs, usr, server_log.txt, watchdog_heartbeat_log.txt — all under the new package | +| 8 Cold boot → UP, 0 kills | ✅ | force-stop+relaunch: `K2Go-Reconciler` ticks `desired=UP actual=UP intent=NOOP [holder=NONE]` steadily; kolibri/nginx/php-fpm start; zero kill/STOP actions | +| 9 proot seccomp | ✅ (effective) | proot runs cleanly (env boots); verdict was learned once at install (keyed on versionCode), now cached — no re-learn line on relaunch, as expected | +| 12 Terminal `iiab` CLI | ➖ risk-covered | CLI is generated lazily at terminal launch from `getFilesDir()`; private dir is already under the new package, so the path it would embed is correct. Live run pending a terminal entry point | +| 14 FG notification | ✅ | `dumpsys notification`: `pkg=org.appdevforall.k2go` channel `watchdog_channel` "K2Go Watchdog Service", title "K2Go Watchdog Active", FOREGROUND_SERVICE | +| 13 Intent actions | ✅ (effective) | the 42 legacy `org.iiab.controller.*` actions drive live flows under the new package: backup ran on `…DEEPOP_*`, boot/services on the reconciler + `INSTALL_*`, and the `watchdog_channel` FG runs on `…WATCHDOG_*` — all worked | +| 18 Custom-View screens | ✅ | Maps landing renders fully (satellite map + FqrController Material3 overlays); its custom-View FQNs resolve under the new namespace (no ClassNotFoundException) | +| 11 OTA new→new | ⛔ | needs an update server offering a newer build (ADR left this reasoned-not-observed) | +| 15 Device-to-device clone | ⛔ | needs a second device | +| 16 restore, 17 dashboard rebuild | ⏳ optional | deeper round-trips; 16 overwrites the current system | + +### 3.5 Pristine verdict (this build, debug, device) + +Every ADR-5368 §10 check that can be run on a single device **passed**: identity side complete +(1–6), and functionally 7, 8, 9, 10, 12(covered), 13, 14, 18, 19, 20 — plus content serving +(kiwix/kolibri/books/maps) and a full backup E2E. **No rebrand-caused breakage was found.** The only +unconfirmed checks need external setup and were already reasoned-not-observed by the ADR: **11 (OTA +new→new)** needs an update server with a newer signed build, **15 (device-to-device clone)** needs a +second device. **16 (restore)** and **17 (dashboard rebuild)** remain as optional deeper round-trips. +On the evidence gathered, the rebranded APK behaves as pristine for single-device operation. From a3446d77cb61d2ff7af0c38a65b4f12289dfc2f3 Mon Sep 17 00:00:00 2001 From: Luis Guzman Date: Thu, 3 Sep 2026 15:34:16 -0600 Subject: [PATCH 06/11] K2GO-380 docs(post-rebrand): device-to-device clone (check 15) PASS; only OTA new->new remains MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Monitored a real 2-device clone on the OnePlus (receiver): pairing with the peer, CloneShareService FG on clone_channel, the CLONE holder quiesced the server (reconciler desired=DOWN holder=CLONE), rsync transfer, and the received system booted healthy (home=301, kiwix=200) with the holder released back to UP [holder=NONE], 0 kills. Benign non-rebrand SELinux denial noted (librsync ioctl TCGETS on a pipe). Every single-device ADR §10 check plus clone now confirmed; only OTA new->new (11) remains, needing an update server. Verdict: the rebranded APK is pristine. --- controller/docs/POST_REBRAND_DEVICE_TESTS.md | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/controller/docs/POST_REBRAND_DEVICE_TESTS.md b/controller/docs/POST_REBRAND_DEVICE_TESTS.md index 20f5c492..b7d60a1e 100644 --- a/controller/docs/POST_REBRAND_DEVICE_TESTS.md +++ b/controller/docs/POST_REBRAND_DEVICE_TESTS.md @@ -173,16 +173,19 @@ identity-sensitive. Heavy rows (⬇ needs a full download / ⇄ needs a second d | 14 FG notification | ✅ | `dumpsys notification`: `pkg=org.appdevforall.k2go` channel `watchdog_channel` "K2Go Watchdog Service", title "K2Go Watchdog Active", FOREGROUND_SERVICE | | 13 Intent actions | ✅ (effective) | the 42 legacy `org.iiab.controller.*` actions drive live flows under the new package: backup ran on `…DEEPOP_*`, boot/services on the reconciler + `INSTALL_*`, and the `watchdog_channel` FG runs on `…WATCHDOG_*` — all worked | | 18 Custom-View screens | ✅ | Maps landing renders fully (satellite map + FqrController Material3 overlays); its custom-View FQNs resolve under the new namespace (no ClassNotFoundException) | +| 15 Device-to-device clone | ✅ | OnePlus received a full library from a peer (`scanned payload host=192.168.1.160 rootfs=true arch=64`): CONNECTING→CALCULATING→CONFIRM→TRANSFERRING; `CloneShareService` FG on `clone_channel`; the CLONE holder quiesced the server (`desired=DOWN … holder=CLONE`), rsync ran, received system booted healthy (`home=301`, `kiwix=200`) and the reconciler released the holder back to `UP [holder=NONE]`, 0 kills. Post-clone Home shows a not-installed card GRAY (F1 again absent). Benign non-rebrand SELinux denial noted: `librsync.so avc: denied { ioctl }` (TCGETS on a pipe, permissive=0) — non-fatal, rsync completes. | | 11 OTA new→new | ⛔ | needs an update server offering a newer build (ADR left this reasoned-not-observed) | -| 15 Device-to-device clone | ⛔ | needs a second device | | 16 restore, 17 dashboard rebuild | ⏳ optional | deeper round-trips; 16 overwrites the current system | ### 3.5 Pristine verdict (this build, debug, device) -Every ADR-5368 §10 check that can be run on a single device **passed**: identity side complete -(1–6), and functionally 7, 8, 9, 10, 12(covered), 13, 14, 18, 19, 20 — plus content serving -(kiwix/kolibri/books/maps) and a full backup E2E. **No rebrand-caused breakage was found.** The only -unconfirmed checks need external setup and were already reasoned-not-observed by the ADR: **11 (OTA -new→new)** needs an update server with a newer signed build, **15 (device-to-device clone)** needs a -second device. **16 (restore)** and **17 (dashboard rebuild)** remain as optional deeper round-trips. -On the evidence gathered, the rebranded APK behaves as pristine for single-device operation. +**Every ADR-5368 §10 check has now been confirmed on device except one.** Identity side complete +(1–6); functionally 7, 8, 9, 10, 12(covered), 13, 14, 18, 19, 20; content serving +(kiwix/kolibri/books/maps); a full backup E2E; and **15 device-to-device clone** end to end with a +real second phone (pairing → CLONE-holder quiesce → rsync → healthy boot → holder released, 0 kills). +**No rebrand-caused breakage was found.** The **only** unconfirmed check is **11 (OTA new→new)**, which +needs an update server offering a newer signed build — the ADR itself left it reasoned-not-observed, and +its runtime pieces (FileProvider authority via getPackageName, signer pinning) are already exercised by +checks 10 and the share path. **16 (restore)** and **17 (dashboard rebuild)** remain optional deeper +round-trips. On the evidence gathered, the rebranded APK is **pristine**: the rename changed identity +and nothing else. From 2e8e5b8ee1cf210ad3dc14d1055799fb1a544c2d Mon Sep 17 00:00:00 2001 From: Luis Guzman Date: Thu, 3 Sep 2026 15:43:16 -0600 Subject: [PATCH 07/11] K2GO-380 docs(post-rebrand): dashboard rebuild (check 17) PASS; log F2 progress-reset bug Monitored a live dashboard rebuild: reconciler held holder=DASHBOARD ~2.5 min (server never dropped, 0 kills), released to holder=NONE on completion -- DashboardRebuildService sound under the new package. Surfaced F2 (not rebrand-related, K2GO-95 area): the rebuild progress bar resets to 0 on return from the notification and the notification shows no percentage, because progress is computed fragment-locally from a wall-clock that resets on recreation and the service broadcasts only coarse state. Design read: progress has no persistent owner. --- controller/docs/POST_REBRAND_DEVICE_TESTS.md | 37 +++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/controller/docs/POST_REBRAND_DEVICE_TESTS.md b/controller/docs/POST_REBRAND_DEVICE_TESTS.md index b7d60a1e..7ffdf9cc 100644 --- a/controller/docs/POST_REBRAND_DEVICE_TESTS.md +++ b/controller/docs/POST_REBRAND_DEVICE_TESTS.md @@ -98,6 +98,40 @@ gone on this build. --- +### F2 — Dashboard-rebuild progress resets to 0 on return from the notification; the notification shows no progress + +**Severity:** low–medium (UX only; the rebuild itself completes correctly). **Rebrand-caused:** no. +**Area:** K2GO-95 (determinate rebuild progress). + +**Symptom (reported on device).** During a dashboard rebuild, minimizing the app → the foreground +notification shows **no percentage and no ETA**; re-entering via the notification while the bar was +~1/3 → the bar **resets to 0** and re-advances from there. The bar does reach 100% when the rebuild +actually finishes (the underlying op is fine). + +**Mechanism.** The determinate bar is computed *client-side* in +`DashboardDetailFragment` (K2GO-95 Phase 2) from `progressPhase` + `progressPhaseStartMs` +(`SystemClock.elapsedRealtime()` measuring time *within* a phase), driven by polling the rebuild log +([:66-68](../app/src/main/java/org/appdevforall/k2go/redesign/DashboardDetailFragment.java#L66), +[:394-405](../app/src/main/java/org/appdevforall/k2go/redesign/DashboardDetailFragment.java#L394)). +Both fields are **fragment-local**, and `progressPhaseStartMs` anchors to when *this fragment instance* +first saw the phase — not when the rebuild actually entered it. On minimize→restore the fragment is +recreated, the fields reset (`progressPhase=NONE`, bar → indeterminate, +[:362-363](../app/src/main/java/org/appdevforall/k2go/redesign/DashboardDetailFragment.java#L362)), and +the time-in-phase clock restarts at 0 → the bar restarts. Meanwhile `DashboardRebuildService` +broadcasts only coarse `STATE_RUNNING/DONE`, and its notification has **no `setProgress()`** → no +percentage in the shade. + +**Design read (CLAUDE.md coherence).** "How far along is the rebuild" has **no persistent owner** — it +is re-derived per fragment instance from a wall-clock that resets. The phase and its *real* start +(derivable from the log's own timestamps) should live in a repository or the service, surviving the +fragment lifecycle **and** feeding the notification (`setProgress`), so the bar resumes where the +rebuild actually is and the notification can show it. Missing-fact → design fix, not a fragment patch. + +**Not rebrand-related.** Candidate for its **own ticket** (the "Pandora's box → separate ticket" rule), +related to **K2GO-95**. + +--- + ## 3. Global device test matrix Grouped identity-sensitive first (what an `applicationId` change can actually break), then the @@ -175,7 +209,8 @@ identity-sensitive. Heavy rows (⬇ needs a full download / ⇄ needs a second d | 18 Custom-View screens | ✅ | Maps landing renders fully (satellite map + FqrController Material3 overlays); its custom-View FQNs resolve under the new namespace (no ClassNotFoundException) | | 15 Device-to-device clone | ✅ | OnePlus received a full library from a peer (`scanned payload host=192.168.1.160 rootfs=true arch=64`): CONNECTING→CALCULATING→CONFIRM→TRANSFERRING; `CloneShareService` FG on `clone_channel`; the CLONE holder quiesced the server (`desired=DOWN … holder=CLONE`), rsync ran, received system booted healthy (`home=301`, `kiwix=200`) and the reconciler released the holder back to `UP [holder=NONE]`, 0 kills. Post-clone Home shows a not-installed card GRAY (F1 again absent). Benign non-rebrand SELinux denial noted: `librsync.so avc: denied { ioctl }` (TCGETS on a pipe, permissive=0) — non-fatal, rsync completes. | | 11 OTA new→new | ⛔ | needs an update server offering a newer build (ADR left this reasoned-not-observed) | -| 16 restore, 17 dashboard rebuild | ⏳ optional | deeper round-trips; 16 overwrites the current system | +| 17 Dashboard rebuild | ✅ | monitored a live rebuild: reconciler held `holder=DASHBOARD` ~2.5 min (self-restarting holder suppressed actuation, server never dropped — `desired=UP actual=UP intent=NOOP`), then released to `holder=NONE`, 0 kills. Surfaced a UX bug (F2, not rebrand-related). | +| 16 restore | ⏳ optional | deeper round-trip; overwrites the current system | ### 3.5 Pristine verdict (this build, debug, device) From 64b36f9d4edaf7b63dc3b31e209310699d577d71 Mon Sep 17 00:00:00 2001 From: Luis Guzman Date: Thu, 3 Sep 2026 16:01:13 -0600 Subject: [PATCH 08/11] K2GO-380 docs(post-rebrand): restore (16) PASS; F3-F6 findings; F6 confirmed + filed as K2GO-381 Restore round-trip (check 16) PASS under the new package (DEEPOP_RESTORE, RESTORE holder quiesce, TarExtractor 2.43 GB, healthy boot, 0 kills). Logged deep-op UX cluster F3-F5 (restore ETA, notification deep-link target, run-in-background nav) and F6: content services don't self-heal after a deep-op because service-heal.ts WATCHED lists only kiwix -- root cause confirmed in code, filed as K2GO-381. F6 also reframes F1 (the red/Unavailable tile is a down service, correct display, not a bug). --- controller/docs/POST_REBRAND_DEVICE_TESTS.md | 56 +++++++++++++++++++- 1 file changed, 55 insertions(+), 1 deletion(-) diff --git a/controller/docs/POST_REBRAND_DEVICE_TESTS.md b/controller/docs/POST_REBRAND_DEVICE_TESTS.md index 7ffdf9cc..3e7f63f9 100644 --- a/controller/docs/POST_REBRAND_DEVICE_TESTS.md +++ b/controller/docs/POST_REBRAND_DEVICE_TESTS.md @@ -132,6 +132,60 @@ related to **K2GO-95**. --- +### F3–F5 — Deep-op process-screen & notification UX (one cluster; not rebrand-related) + +Reported while running the restore; the same pattern spans the deep-op screens (install, backup, +restore, dashboard rebuild, clone). Grouped because they are one cohesive concern, not three unrelated +bugs. + +- **F3 — Restore has a reliable determinate bar but no ETA.** The rootfs install shows a time estimate; + the restore should too. Derivable from `TarExtractor` bytes-extracted vs the archive size (both are + already logged: `Extract start: … archiveCompressed=…`). Area: K2GO-95 / progress. +- **F4 — The foreground notification deep-links to Settings, not the process/progress screen.** Tapping + the restore's notification lands on Settings instead of the running-restore window. Every deep-op's + notification `contentIntent` should open *its own* progress screen — **audit all of them** (install, + backup, restore, dashboard rebuild, clone); `DashboardRebuildService` already deep-links to its card + (Module management → Dashboard), so the target exists for some and not others. +- **F5 — "Run in background" behaves like Back, not "go to Home/Library".** On the process screens the + run-in-background control pops the back stack — reliable, but it can land somewhere unintended; it + should route deliberately to Home/Library. + +**Design read (CLAUDE.md coherence).** These + F2 are one cohesive area: the deep-op process screens and +their notifications lack a consistent contract — a shared progress+ETA model, a notification that opens +its *own* screen, and a defined run-in-background destination. That is a **single "deep-op process-screen +UX" ticket** (with F2), related to **K2GO-95**, not one ticket per symptom (per the "work at the cohesive +-area level" rule). + +--- + +### F6 — Content services don't self-heal after a deep-op; only Kiwix is watched → **K2GO-381** + +**Severity:** medium (a restored/cloned box can have a content service down until manual Retry). +**Rebrand-caused:** no. **Confirmed on device (restore round-trip) and in code.** + +**Symptom.** After a *restore*, Kolibri did not restart on its own: `:8009` unreachable, `/kolibri/` +returned **502**, and "Take courses" showed a red **"Unavailable"** tile. Kiwix/Books/Maps were fine. + +**Root cause (confirmed).** By design the app/reconciler owns box up/down and does **not** manage +individual box services (ADR-5343a §10 layering). Per-service healing is the dashboard's job — +`static/dashboard/sockets/service-heal.ts` probes content services on loopback and issues +`pdsm restart ` on a down one. But its `WATCHED` list contains **only `kiwix`** +([service-heal.ts:28-30](../../static/dashboard/sockets/service-heal.ts#L28)); the comment says the +others "are added here as they are device-verified" and `restartService` already accepts the full set. +So Kolibri/php-fpm/calibre-web are never auto-restarted; Kiwix would have self-healed. + +**Not a bug of the rebrand nor new** — it is the incremental rollout planned in ADFA-5343. Fix = extend +`WATCHED`. **Filed as K2GO-381** (relates K2GO-380). + +**This reframes F1.** The originally-reported "apps show red/Unavailable after a backup" was almost +certainly *this* — a content service not restarting after a deep-op, correctly rendered red +("down/wedged"), **not** a display bug (not-installed→red). The app card and `service-heal.classifyProbe` +([:50-55](../../static/dashboard/sockets/service-heal.ts#L50)) use the same split: `404 → absent +(gray, not installed)`, `5xx/502/timeout → down (red, Unavailable)`. So F1's display is working as +designed; F6 (service not restarting) is the real defect, now owned by K2GO-381. + +--- + ## 3. Global device test matrix Grouped identity-sensitive first (what an `applicationId` change can actually break), then the @@ -210,7 +264,7 @@ identity-sensitive. Heavy rows (⬇ needs a full download / ⇄ needs a second d | 15 Device-to-device clone | ✅ | OnePlus received a full library from a peer (`scanned payload host=192.168.1.160 rootfs=true arch=64`): CONNECTING→CALCULATING→CONFIRM→TRANSFERRING; `CloneShareService` FG on `clone_channel`; the CLONE holder quiesced the server (`desired=DOWN … holder=CLONE`), rsync ran, received system booted healthy (`home=301`, `kiwix=200`) and the reconciler released the holder back to `UP [holder=NONE]`, 0 kills. Post-clone Home shows a not-installed card GRAY (F1 again absent). Benign non-rebrand SELinux denial noted: `librsync.so avc: denied { ioctl }` (TCGETS on a pipe, permissive=0) — non-fatal, rsync completes. | | 11 OTA new→new | ⛔ | needs an update server offering a newer build (ADR left this reasoned-not-observed) | | 17 Dashboard rebuild | ✅ | monitored a live rebuild: reconciler held `holder=DASHBOARD` ~2.5 min (self-restarting holder suppressed actuation, server never dropped — `desired=UP actual=UP intent=NOOP`), then released to `holder=NONE`, 0 kills. Surfaced a UX bug (F2, not rebrand-related). | -| 16 restore | ⏳ optional | deeper round-trip; overwrites the current system | +| 16 Restore | ✅ | monitored a live restore: DEEPOP_RESTORE FG on `deepop_channel`; RESTORE holder quiesced all services (calibre-web/dash-node/kiwix/kolibri/nginx/php-fpm), `TarExtractor` extracted a 2.43 GB archive to `…/org.appdevforall.k2go/files/rootfs` via the new package's `libtar.so`, system booted healthy (`home=301`, `kiwix=200`), holder released to NONE, 0 kills. Surfaced UX findings F3–F5 (not rebrand-related). | ### 3.5 Pristine verdict (this build, debug, device) From 5ccc7f7408057ef2676cc3af49027315fced9aae Mon Sep 17 00:00:00 2001 From: Luis Guzman Date: Thu, 3 Sep 2026 16:10:16 -0600 Subject: [PATCH 09/11] =?UTF-8?q?K2GO-380=20docs(post-rebrand):=20finalize?= =?UTF-8?q?=20report=20=E2=80=94=20conclusion=20+=20pristine=20verdict;=20?= =?UTF-8?q?OTA=20deferred=20to=200.9.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Post-rebrand device verification complete: every single-device ADR-5368 §10 check plus a two-device clone passed on the OnePlus 7T; the rename changed identity and nothing else and the app runs correctly across every exercised mechanism. Only OTA new->new remains, deferred to a publishable 0.9.0. Findings F1-F6 recorded with owners (F6 -> K2GO-381; F2-F5 and a backup/restore progress+cancel standardization to be filed). --- controller/docs/POST_REBRAND_DEVICE_TESTS.md | 32 ++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/controller/docs/POST_REBRAND_DEVICE_TESTS.md b/controller/docs/POST_REBRAND_DEVICE_TESTS.md index 3e7f63f9..380066fe 100644 --- a/controller/docs/POST_REBRAND_DEVICE_TESTS.md +++ b/controller/docs/POST_REBRAND_DEVICE_TESTS.md @@ -278,3 +278,35 @@ its runtime pieces (FileProvider authority via getPackageName, signer pinning) a checks 10 and the share path. **16 (restore)** and **17 (dashboard rebuild)** remain optional deeper round-trips. On the evidence gathered, the rebranded APK is **pristine**: the rename changed identity and nothing else. + +--- + +## 4. Conclusion — K2GO-380 complete + +The app-identifier rebrand (`org.iiab.controller` → `org.appdevforall.k2go`, K2GO-293 / ADR-5368) was +verified end to end on device (OnePlus 7T, debug build; base and full tiers; a real two-device clone). +**The rename changed identity and nothing else — the app runs correctly across every exercised +mechanism.** Every ADR-5368 §10 check runnable without external infrastructure passed: fresh install, +environment boot (0 spurious kills), FileProvider, notifications, custom-View screens, intent actions, +content serving, backup, restore, device-to-device clone, and dashboard rebuild. + +**Sole deferred check — OTA new→new (§10 check 11):** an over-the-air self-update from one signed build +to a newer one needs an update server offering a newer build, so it is deferred to a **0.9.0** that can +be published and pulled. Its runtime pieces (FileProvider authority, signer pinning) are already +exercised by the passing checks above. + +**Findings — none caused by the rebrand, each with an owner:** +- **F1** — reframed: the "red / Unavailable tile" was a content service that had not restarted, + rendered correctly (not a display bug). Explained by F6. +- **F6** — content services do not self-heal after a deep-op (`service-heal.ts` watches only kiwix) → + filed as **K2GO-381**. +- **F2–F5** — deep-op process-screen UX (progress persistence + notification progress, restore ETA, + notification deep-link target, run-in-background nav) → follow-up ticket (to file; blocked on a + transient Atlassian outage at close time). +- **Backup ↔ restore standardization** — progress parity (bar / % / ETA) and a **Cancel** affordance + with differentiated safety (backup: safe → offer to delete the incomplete file, no residue; restore: + strong warning, no clean cancel — a mid-restore cancel likely leaves the system damaged) → follow-up + ticket (to file). + +**Verdict.** K2GO-380 is **complete**: the post-rebrand device verification is done and the app behaves +as pristine; OTA self-update is the only deferred item, pending a newer published build (0.9.0). From 306d45840f22e9e3ca522d470dfdf6e2eb3ff79e Mon Sep 17 00:00:00 2001 From: Luis Guzman Date: Thu, 3 Sep 2026 16:20:09 -0600 Subject: [PATCH 10/11] =?UTF-8?q?K2GO-380=20docs(post-rebrand):=20fix=20st?= =?UTF-8?q?ale=20line=20=E2=80=94=20restore/rebuild=20were=20run=20and=20p?= =?UTF-8?q?assed,=20not=20"optional"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The interim verdict in section 3.5 still said checks 16 (restore) and 17 (dashboard rebuild) "remain optional deeper round-trips" -- written before they were run. Both were subsequently exercised on device and passed (section 3.4 and section 4); correct the line so it no longer contradicts the rest of the report. --- controller/docs/POST_REBRAND_DEVICE_TESTS.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/controller/docs/POST_REBRAND_DEVICE_TESTS.md b/controller/docs/POST_REBRAND_DEVICE_TESTS.md index 380066fe..9f7cda54 100644 --- a/controller/docs/POST_REBRAND_DEVICE_TESTS.md +++ b/controller/docs/POST_REBRAND_DEVICE_TESTS.md @@ -275,9 +275,9 @@ real second phone (pairing → CLONE-holder quiesce → rsync → healthy boot **No rebrand-caused breakage was found.** The **only** unconfirmed check is **11 (OTA new→new)**, which needs an update server offering a newer signed build — the ADR itself left it reasoned-not-observed, and its runtime pieces (FileProvider authority via getPackageName, signer pinning) are already exercised by -checks 10 and the share path. **16 (restore)** and **17 (dashboard rebuild)** remain optional deeper -round-trips. On the evidence gathered, the rebranded APK is **pristine**: the rename changed identity -and nothing else. +checks 10 and the share path. **16 (restore)** and **17 (dashboard rebuild)** were subsequently run on +device and also passed (see §3.4 and §4). On the evidence gathered, the rebranded APK is **pristine**: +the rename changed identity and nothing else. --- From f30bb7aa1e3b3f8c42e11bf4abdf5bef54bed287 Mon Sep 17 00:00:00 2001 From: Luis Guzman Date: Thu, 3 Sep 2026 16:25:27 -0600 Subject: [PATCH 11/11] =?UTF-8?q?K2GO-380=20docs(post-rebrand):=20consiste?= =?UTF-8?q?ncy=20pass=20=E2=80=94=20reconcile=20F1=20with=20F6;=20minor=20?= =?UTF-8?q?fixes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Full read-through for internal consistency. Main fix: the F1 section described F1 as a confirmed display defect ("the code defect is still real ... the fix stands"), which contradicted F6 and the conclusion that F1 is reframed as a down service correctly rendered. Rewrote F1 as investigated-and-reframed: the 404->gray / 502->red split (shared with service-heal.classifyProbe) is correct, a not-installed platform reads gray, and the reported red was a down service (F6). Also aligned the §3.3 F1-repro row and F-g note, softened the I12 "release gate open" wording to match its PASS result, dropped "API level TBD", and changed §4 "runnable" to "exercised" noting check 12 is covered by inspection. --- controller/docs/POST_REBRAND_DEVICE_TESTS.md | 92 +++++++------------- 1 file changed, 33 insertions(+), 59 deletions(-) diff --git a/controller/docs/POST_REBRAND_DEVICE_TESTS.md b/controller/docs/POST_REBRAND_DEVICE_TESTS.md index 9f7cda54..873d5350 100644 --- a/controller/docs/POST_REBRAND_DEVICE_TESTS.md +++ b/controller/docs/POST_REBRAND_DEVICE_TESTS.md @@ -8,7 +8,7 @@ while testing. It complements — does not replace — the verification matrix i `ADR-5368-app-identifier-rebrand.md` §10; that matrix proves *the rename itself*, this one is the *global* app sweep and the running findings log. -Test device for this pass: **OnePlus 7T (HD1901), arm64-v8a, API level TBD.** +Test device for this pass: **OnePlus 7T (HD1901), arm64-v8a.** --- @@ -42,59 +42,32 @@ Test device for this pass: **OnePlus 7T (HD1901), arm64-v8a, API level TBD.** ## 2. Findings log -### F1 — Uninstalled content cards show RED ("unavailable"), not GRAY ("not installed"), after a backup - -**Severity:** medium (misleading status; no data loss). **Rebrand-caused:** no. **Rebrand-exposed:** yes. - -**Symptom.** Create a backup, press **Finish** → returns to Library/Home. Content platforms that -are *not installed* render with the **red** dot ("unavailable"/stuck) instead of the **gray** -"Not installed" dot. - -**Mechanism.** `LibraryHomeFragment.refreshStatuses()` -([:568-585](../app/src/main/java/org/appdevforall/k2go/redesign/LibraryHomeFragment.java#L568)), -server-alive branch: each card **freshly probes** its endpoint — -`PRESENT → green`, `ABSENT (404 = not installed) → gray`, otherwise **indeterminate** → -`RED` once `serverAliveSinceMs` grace (60 s) has passed, else amber. A backup **stops and -restarts** the box; on return, nginx/dash-node routing is still warming, so a probe to a -not-installed platform does **not** get a clean `ABSENT (404)` — it lands in the *indeterminate* -bucket and paints **red**. The offline branch -([:563-566](../app/src/main/java/org/appdevforall/k2go/redesign/LibraryHomeFragment.java#L563)) -does the right thing (`PlatformEvidence.last(...) == ABSENT ? GRAY : AMBER`), but the alive -branch **discards the already-known "not installed" fact** on a transient indeterminate probe. - -**Design read (CLAUDE.md coherence).** A fact with an owner ("this platform is not installed", -recorded in `PlatformEvidence`) is re-derived — wrongly — from a transient probe. The fix is to -*fall back to last-known evidence* before deciding RED: an endpoint last seen `ABSENT` should stay -gray through a warm-up indeterminate probe; RED should require either no prior verdict or a prior -non-absent one. Not a new flag — reuse the evidence the offline branch already trusts. - -**Why the rebrand exposed it.** The renamed app is a clean install (nothing installed), so after a -backup *every* content card takes the indeterminate branch at once; on the pre-rebrand app with -content present those cards were green and the path never showed. - -**Device repro attempt (OnePlus 7T, base tier, debug build):** clean install → base-tier system → -backup (1.65 GB) → Finish → Home. Result: the two not-installed cards ("Read a book", "Take courses") -correctly read **GRAY / Not installed — NOT red**. **F1 did not reproduce this run.** - -Why it likely didn't show here: the RED path needs the post-restart probe to land *indeterminate* -(nginx back up but the platform's upstream still 502 during warm-up). On this small base-tier system -the server restart was fast, so the probe got a clean `ABSENT (404)` → GRAY before any red frame. - -**Full-tier retry (to test whether backup *duration* is the determinant):** reinstalled to a full -system (all five platforms end up "Ready", so no not-installed targets remain), backed up (2.43 GB -tar — heavier/longer than base), Finish → Home, with a 40 s screen recording over the transition. -Result: all cards read **Ready (green)** immediately after Finish — **no red flash in the still**, and -the recording compressed to ~0.6 MB (near-static, consistent with no flashing). Sent to the reporter -to scrub for any sub-second flash. - -**Working conclusion:** F1 as reported (red after backup) **did not visibly reproduce on device** -across base and full tiers on this build (`f03cd618`, debug). By the time Home renders after Finish, -services already answer, so cards resolve straight to green/gray — suggesting the deep-op restarts and -waits for a healthy server *before* returning, so Home never observes the intermediate 502 window via -this path. The code defect is still real by inspection (the alive branch discards a known `ABSENT` on -an indeterminate probe), so the fix stands regardless; but the visible symptom needs the reporter's -exact conditions (build, tier, timing, how long the red persisted) to reproduce, or it may already be -gone on this build. +### F1 — "Unavailable (red)" reported instead of "Not installed (gray)" — investigated, reframed as F6 + +**Severity:** n/a (reframed — see F6). **Rebrand-caused:** no. + +**As reported.** After a backup, content platforms appeared as a red "Unavailable" tile instead of a +gray "Not installed" tile. + +**What the card does.** `LibraryHomeFragment.refreshStatuses()` +([:568-585](../app/src/main/java/org/appdevforall/k2go/redesign/LibraryHomeFragment.java#L568)) paints +each card from a fresh probe: `PRESENT → green`, `ABSENT (404 = not installed) → gray`, otherwise +*indeterminate* → red (after a 60 s grace) / amber. The initial hypothesis was that, in the warm-up +after a deep-op restarts the box, a *not-installed* platform's probe might momentarily miss a clean +404 and land *indeterminate* → red. + +**Resolution — the display is correct; the red was a down service (F6).** The hypothesis did **not +hold up.** (1) It did not reproduce on device: across base and full tiers, not-installed cards read +**gray** correctly after Finish (the deep-op restarts and waits for a healthy server before returning, +so Home never renders mid-warm-up via this path). (2) The classification is sound and shared: the app +card and the box's `service-heal.classifyProbe` +([service-heal.ts:50-55](../../static/dashboard/sockets/service-heal.ts#L50)) use the same split — +**`404 → absent → gray (not installed)`**, **`5xx/502/timeout → down → red (Unavailable)`**. A genuinely +not-installed platform has no nginx route → 404 → gray, so it does not reach the red path. The red tile +the report saw was an *installed* service that had **not restarted** (Kolibri, 502) — rendered +**correctly**. That is **F6** (owned by **K2GO-381**). The offline branch's last-known-evidence fallback +([:563-566](../app/src/main/java/org/appdevforall/k2go/redesign/LibraryHomeFragment.java#L563)) is +already the right pattern; there is no display defect to fix here. --- @@ -207,7 +180,7 @@ identity-sensitive. Heavy rows (⬇ needs a full download / ⇄ needs a second d | I9 | proot seccomp verdict | First launch on affected device | one ADFA-5362 learn line, then `PROOT_NO_SECCOMP=1` | verdict keyed on `versionCode` → re-learns (expected, not a regression) | | I10 | OTA signer pin | new→new update | updates in place | `ApkVerifier`/`CertDigests` — rebuilt APK must carry the pinned cert | | I11 | Debug delivery (debug builds) | `am broadcast -a org.appdevforall.k2go.DEBUG_DELIVERY -n org.appdevforall.k2go/…DebugDeliveryReceiver` | receiver enqueues | the ONE new-namespace action; must match new id | -| I12 | Firebase/analytics | build + first run | google-services matches package | **release gate open** — needs `org.appdevforall.k2go` Firebase client (§1) | +| I12 | Firebase/analytics | build + first run | google-services matches package | keyed on `package_name`; resolved for the shipped build, local dev copy stale (§1, §3.3) | ### 3.2 Functional sweep (rebrand must not have moved anything) @@ -219,7 +192,7 @@ identity-sensitive. Heavy rows (⬇ needs a full download / ⇄ needs a second d | F-d | Content: Books | open Read a book | library loads | | | F-e | Content: Maps | open Navigate maps | tiles render | | | F-f | Get-More install | install one not-present module | downloads, card → green | ⬇ | -| F-g | Backup create | Settings → backup → Finish | archive written; **watch F1: not-installed cards must read gray, not red** | ⬇ | +| F-g | Backup create | Settings → backup → Finish | archive written; not-installed cards read gray (a red tile = a down service, F6) | ⬇ | | F-h | Restore | restore the backup | round-trips, boots healthy | ⬇ | | F-i | Clone send/receive | between two new-id devices | pairing + transfer complete | ⇄ ⬇ | | F-j | Dashboard rebuild | trigger a rebuild | completes, log tails | | @@ -246,7 +219,7 @@ identity-sensitive. Heavy rows (⬇ needs a full download / ⇄ needs a second d | I5, I7, OTA install | ➖ ADR-covered | Exercised in ADR-5368 §10.2 (checks 12–13, 11); re-confirm opportunistically. | | F-a fresh install E2E | ✅ PASS | Clean debug install → base-tier system installed & served (~2 min); Home shows a correct mix (books/courses Not installed, code/wikipedia/maps Ready) | | F-g backup E2E | ✅ PASS | Settings→Backups→Back up → SAF picker default name `k2go_2026.246_…` (rebranded, single file) → 1.65 GB tar written by DeepOpService → "Backup saved" → Finish returns to Home. Backup writer + deep-op work under the new package. | -| F1 repro (backup→Home) | ⚠ NOT reproduced (this run) | not-installed cards read GRAY correctly after Finish on base tier; red window is a narrow transient (see §2 F1). Retest on full tier / slower restart. | +| F1 repro (backup→Home) | ✅ reframed | not-installed cards read GRAY correctly (base + full tiers); the reported red was a down service (F6), not a display bug — see §2 F1 / F6. | | Backup naming false-alarm | ✅ retracted | earlier "k2go_ vs iiab-oa_ inconsistency" was a mis-tap selecting an old backup file; the real default is `k2go_…`. | | I7 terminal, F-h restore, F-i clone | ⏳ later | debug build now enables `run-as` for deeper checks | @@ -286,9 +259,10 @@ the rename changed identity and nothing else. The app-identifier rebrand (`org.iiab.controller` → `org.appdevforall.k2go`, K2GO-293 / ADR-5368) was verified end to end on device (OnePlus 7T, debug build; base and full tiers; a real two-device clone). **The rename changed identity and nothing else — the app runs correctly across every exercised -mechanism.** Every ADR-5368 §10 check runnable without external infrastructure passed: fresh install, +mechanism.** Every ADR-5368 §10 check exercised on a single device passed — fresh install, environment boot (0 spurious kills), FileProvider, notifications, custom-View screens, intent actions, -content serving, backup, restore, device-to-device clone, and dashboard rebuild. +content serving, backup, restore, device-to-device clone, and dashboard rebuild — with the terminal +`iiab` CLI (check 12) covered by inspection (its path derives from `getFilesDir()` under the new package). **Sole deferred check — OTA new→new (§10 check 11):** an over-the-air self-update from one signed build to a newer one needs an update server offering a newer build, so it is deferred to a **0.9.0** that can