Skip to content

ADFA-5368 refactor(identity): publish under org.appdevforall.k2go - #517

Merged
luisguzman-adfa merged 16 commits into
mainfrom
refactor/ADFA-5368-app-identifier
Sep 3, 2026
Merged

ADFA-5368 refactor(identity): publish under org.appdevforall.k2go#517
luisguzman-adfa merged 16 commits into
mainfrom
refactor/ADFA-5368-app-identifier

Conversation

@luisguzman-adfa

@luisguzman-adfa luisguzman-adfa commented Sep 1, 2026

Copy link
Copy Markdown
Collaborator

What

Moves the applicationId and the Java namespace from org.iiab.controller to
org.appdevforall.k2go, per ADR-5368. 529 source files move for the rename itself; the compiler is
the verifier. (The full diff is larger — see What else travels with it below.)

Why

The identifier is a reverse-DNS namespace claim — it declares which organisation publishes the app.
android:label has said "Knowledge to Go" for a long time; only the technical identifier still says
org.iiab. This is an administrative decision, not a technical necessity.

It is done now because it cannot be done later: Android offers no migration between application
IDs, and a device carries 2.9 GB of installed rootfs, so the cost is (devices in the field) × (a
full reinstall). Fewer than ten today.

What the change contains

  • applicationId + namespace in build.gradle; 529 files moved to org/appdevforall/k2go.
  • The manifest's deprecated package= attribute is deleted, not updated — AGP takes the
    namespace from Gradle.
  • The lint baseline has its file paths rewritten rather than regenerated, so the recorded issue
    set stays exactly what it was instead of silently absorbing whatever lint finds today.
  • build.gradle's instrumentation-runner reference is corrected; it is printed in a message and
    would have handed a reader the wrong adb command.

What else travels with it

Four changes landed on this branch after the rename itself, each because it is the same fact
changing:

  • One owner for the download origins. The host was spelled out at eight call sites — four for
    the rootfs store, four for the APK repo — one of them inside a shell script the app generates as
    text, where a missed edit is invisible to the compiler. config/DownloadEndpoints owns them now,
    with the two buckets as separate named constants: they have different names and different
    lifecycles, and collapsing them would make the next move a search-and-replace again.
  • The APKs carry the product's name, not the identifier: K2Go-v0.8.0-beta-v8a-release.apk.
    The identifier still lives inside the APK, where it belongs. The release manifest finds each APK
    by filename, so its three patterns move with the rename.
  • The Firebase version name drops the ticket key. App Distribution already shows the full branch
    under every build; the key restated it, and after the project-key move it degraded to a slug
    truncated mid-word ("chore-K2GO-377-d"). The short SHA stays — it pins the commit, which is the
    one thing the branch name does not.
  • A package sweep after merging main's dashboard-log migration, which arrived without a conflict
    while still carrying old-package references.

Deliberately left alone

The 42 "org.iiab.controller.SOME_ACTION" intent-action literals. They are unique-by-convention
strings, not identity, and renaming them is a safe optional follow-up. See check 13 — this is now
observed, not argued.

tools/proot-builder/build_static.sh references data/data/com.termux/…: that is the Termux build
container, not this app. Touching it would break the native-binary build.

Verification — did the change happen

# Check Result
1 New identity resolves /data/app/org.appdevforall.k2go-…/base.apk
2 Old identity gone ✅ not installed
3 Private dir
4 No stale identifier in code ✅ only the 42 literals, docs and archive/
5 Deprecated package= removed
6 Firebase ✅ console entry created; CI assembleRelease on this branch built and signed under the new identity

Verification — nothing else changed

Run on two devices: API 28 / kernel 3.18 and API 36 / kernel 6.12.

# Area Result
7 Clean install end to end ✅ 2.2 GB download → extract → 4.6 GB rootfs → serving
8 Cold boot ✅ 0 kills, 1 launch, UP
9 proot capability (ADFA-5362) ✅ no verdict written on either kernel
10 FileProvider ✅ share sheet opened with the APK attached — authority resolves under the new identifier
11 OTA updater ✅ reached the server, compared versions
12 Terminal + generated iiab CLI ✅ every path under the new identifier
13 Intent actions ✅ stop→start, backup and rebuild all ran on the untouched old-named actions
14 Notifications ✅ channels created under the new identity
15 Device-to-device clone ✅ 4.6 GB in ~6 min; receiver booted to a healthy environment
16 Backup ✅ 2.2 GB archive, verified an intact gzip; holder released, server returned on its own
17 Dashboard rebuild ✅ v1.2.11 → v1.2.12, server stayed up throughout
18 Layouts with custom Views ✅ portal, module detail and maps landing all inflate
19 Unit tests + lint ✅ green
20 tools/ untouched ✅ the 11 com.termux paths intact

A note on check 10, since it was the one that resisted. The updater's install path could not be
reached: the update server had no newer build to offer, which left the FileProvider authority
confirmed only by inspection. Rebuilding the app against a local update server would have verified a
binary other than the one being merged, so the same mechanism was reached through a door the app
already has — the clone screen's "can't scan the QR code" fallback runs
getUriForFile(ctx, getPackageName() + ".provider", apk) on the installed APK, the identical call,
authority and file class as the updater. The share sheet opened with the APK attached; a mismatched
authority would have thrown before it ever appeared. provider_paths.xml also names no package
anywhere, so the rename cannot move a file outside a configured root.

Incidental but reassuring: ADFA-5343's holder mechanism behaved throughout — holder=INSTALL,
holder=DASHBOARD, holder=BACKUP — standing down and releasing cleanly each time.

Changeover

  • The release that changes the identifier cannot ship through the in-app updater — Android sees
    a new app, not an update. It is installed by hand.
  • Both apps must not be installed at once, and this is functional rather than hygiene. Liveness
    is answered by probing a fixed loopback port that carries no identity, while the process half of
    the same question matches our own rootfs path — so an app whose own box is down reads UP off the
    other app's server. Observed on device: the old identifier counted the new one's server as its own
    and its install deadlocked. The runbook is uninstall first, and §9 says why. Leaving the old
    app also strands its 2.9 GB.
  • Download once, clone the rest. A clean install pulls 2.2 GB; a clone moves the finished 4.6 GB
    library between two devices in about six minutes.
  • Cloning across the identifier boundary should install a second app on the receiver. That half
    is reasoned, not observed.

Known gap, tracked separately

Distribution to internal testers is currently failing on a permissions issue that follows from the
new identifier. It is being handled outside this PR and does not affect the release pipeline: that
workflow has no distribution step, and its assembleRelease on this branch is what verifies
check 6.

No server change, no new strings, no UI change.

Records the decision to publish under org.appdevforall.k2go, the recon behind
it, and what it costs. The identifier is a namespace claim; this is an
administrative decision rather than a technical necessity, recorded because --
unlike most naming -- it cannot be undone once devices are in the field.

The measured part: Android offers no migration between application IDs and a
device carries 2.9 GB of installed rootfs, so the cost is (devices) x (a full
reinstall). Fewer than ten today, and only ever more.

Includes the verification matrix and the boundary this is not: the rename
removes no IIAB attribution anywhere.
Mechanical rename of applicationId and namespace, per ADR-5368. 529 source
files move from org/iiab/controller to org/appdevforall/k2go, and the compiler
is the verifier -- 1637 reference points are not reviewable by eye.

Deliberately left alone: the 42 intent-action literals, which are unique-by-
convention strings rather than identity and keep working untouched. The
manifest's deprecated package= attribute is deleted rather than updated, since
AGP takes the namespace from Gradle. The lint baseline has its file paths
rewritten rather than regenerated, so the recorded issue set stays exactly what
it was instead of silently absorbing whatever lint finds today.

Also corrects build.gradle's instrumentation-runner reference, which is printed
in a message and would have handed a reader the wrong adb command.

Device-verified: a clean install under the new identifier downloads, extracts
and boots to HTTP 200.
Two claims in the ADR were wrong, and the runs that found them also changed the
changeover plan.

Backups are not private: "Make a copy" opens the system document picker and the
archive lands in shared storage, so an export/import bridge between identifiers
is feasible after all. It still is not worth building -- the round trip needs
room for two copies of the library and a human at each device either way -- but
the reason is arithmetic, not plumbing.

Cloning is the cheap path, and it reshapes the changeover: 4.6 GB moves between
two devices over a local hotspot in about six minutes, so the fleet downloads
once and clones the rest instead of downloading per device.

Adds the result section. Every check passed on device across three Android
generations except FileProvider, which stayed structural only because the update
server had no newer build to offer. The intent-action literals the rename left
alone are now observed inert rather than argued so -- backup, dashboard rebuild
and the stop/start cycle all run on the old-named actions.
The updater's install path could not be reached -- the update server had no
newer build to offer -- so the FileProvider authority was left confirmed only by
inspection. Rebuilding against a local update server would have verified a
binary other than the one being merged, so the same mechanism was reached
through a door the app already has: the clone screen's "can't scan" fallback
runs getUriForFile with the identical call, authority and file class as the
updater. The share sheet opened with the APK attached, which a mismatched
authority would have prevented by throwing first.

Every check in the matrix now passed on device. Firebase remains outstanding and
is not an engineering step.
…efactor/ADFA-5368-app-identifier

ADFA-5368 merge: main into the identifier rename

Nine files conflicted, all with the same shape: main refactored them (ADFA-5339
dashboard update, ADFA-5364 content-admission collapse) while this branch only
renamed their package. Verified per file that our side carried no semantic
change, then took main's version and reapplied the rename. DashboardCardStateTest
arrived at the old package path as a new file and was moved.

Tests and lint green.
Section 9 said installing both apps merely stranded 2.9 GB. Device testing showed the second
app does not work: liveness is a probe of a fixed loopback port that carries no identity, while
the process half of the same question matches our own rootfs path, and the answering services
win — so an app whose own box is down reads UP off the other app's server. The old identifier
counted the new one's box as its own and deadlocked its install. That makes "uninstall first" a
functional requirement rather than hygiene.
…p-identifier

main brought K2GO-372 (restore progress, early rejection, disk check) written against the old
package. Rename detection carried it onto the new paths; the conflicting hunks were all the same
shape — this branch held the pre-K2GO-372 code with renamed packages — so main's side wins
throughout. RootfsIdentity and its test arrived as new files inside a renamed directory and moved
with their package line. Intent action literals are left as they are, matching this branch's rule
of renaming code references only.
…p-identifier

main brought K2GO-95: RebuildPhase and RebuildProgress resolve a determinate rebuild bar, and
DashboardDetailFragment drives it. The three new files landed in the old dashboard/domain path;
that package already exists renamed on this branch, so rename detection placed them correctly and
only their package line needed fixing. The only content conflict was the import block the two new
domain types were added to.
…p-identifier

main brought K2GO-374: a reusable LiveLogPanel, with the module-install and maps logs migrated onto
it. Its package is new, so rename detection had nothing to follow and git placed the three new files
under the old package with no conflict at all — they had to be moved by hand. LiveLogPanel is a
custom view, so the two layouts name it by fully-qualified class: that reference is invisible to the
compiler and would only fail when the screen inflates, which is a resource surface this rename had
not had until now.
…o the new package

The K2GO-374 merge brought DashboardDetailFragment's LiveLogPanel migration in without a conflict,
so its two references to the old package came along unmarked and the branch did not compile.
…t the rootfs at its bucket

The host was spelled out at eight call sites — four for the rootfs store, four for the APK repo —
so moving either meant finding them all, and one lived inside a shell script this app generates as
text, where a missed edit is invisible to the compiler. config/DownloadEndpoints owns them now,
with the two buckets as separate named constants: they have different names and different
lifecycles, and collapsing them would make the next move a search-and-replace again.

The rootfs store moves to the k2go-rootfs bucket, which serves from its root (the old host kept an
/android/rootfs path; only the static proot-distro base is in a folder). The builder and the two
preBuild size tasks follow.

The workflow published metalinks naming k2go-download.appdevforall.org/rootfs, a path-based route
that was never created: the .meta4 resolved and then the tarball 404'd, with no mirror left to fall
back to. Its default now names the bucket that actually holds the artifacts.
main brought the bucket migration: the rootfs artifacts renamed to k2go_, the APK workflows
repointed at k2go-apk-repo, the two remaining Node 20 actions, and the transitional sweep landing
and then leaving. The only conflict was ROOTFS_PUBLIC_BASE, where the same fix had arrived from
both sides — resolved on main's ticket key with this branch's comment, which describes the bucket
that exists rather than the path-based route that was never created.
These files are downloaded by people from a public bucket, so they carry the product's name:
K2Go-v0.8.0-beta-v8a-release.apk instead of org.appdevforall.k2go-v0.8.0-beta-arm64-v8a-release.apk.
The identifier still lives inside the APK, where it belongs.

The split's ABI is abbreviated in the same block that already overrides the version code per output,
so one place owns the outputs. archivesBaseName cannot do that part: Gradle names outputs after the
split.

The release manifest finds each APK by filename, so its three patterns move with the rename, now
anchored on hyphens — a bare "v8a" would one day match a version string and publish a manifest
pointing at the wrong architecture, silently.
The version name carried the branch's Jira key (ADFA-4810), which the project-key move left behind:
a K2GO branch no longer matches the ADFA-only pattern and falls through to a slug truncated
mid-word — "chore-K2GO-377-d", "fix-K2GO-377-roo". App Distribution already shows the full branch
under each build, so the label restated it, and worse.

The short SHA stays: it pins the exact commit, which is the one thing the branch name does not.
@luisguzman-adfa
luisguzman-adfa merged commit b57d507 into main Sep 3, 2026
3 of 5 checks passed
@luisguzman-adfa
luisguzman-adfa deleted the refactor/ADFA-5368-app-identifier branch September 3, 2026 07:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant