Skip to content

Give feature-private implementations feature-owned packages - #79

Merged
AhmadKharfan merged 4 commits into
developfrom
refactor/feature-owned-packages
Jul 29, 2026
Merged

Give feature-private implementations feature-owned packages#79
AhmadKharfan merged 4 commits into
developfrom
refactor/feature-owned-packages

Conversation

@AhmadKharfan

Copy link
Copy Markdown
Owner

Why

Two Kotlin package roots were each declared by more than one Gradle module:

  • data.local — by :data:local, :data:git and :feature:terminal
  • data.buildsystem — by :data:build and :feature:buildrun

A split package hides real coupling. Files in different modules appear to be in the same package, so they reference each other with no import at all, and no tooling that reasons about imports can tell which module owns a symbol. It also makes package names actively misleading: :feature:terminal owned files called data.local.pty.*, and :feature:buildrun owned data.buildsystem.install.*.

This bit during the earlier dependency audit, where a package-level search could not distinguish a :data:git symbol from a :data:local one and produced the wrong answer.

What changed

Feature-private implementations now live under the package of the module that owns them:

  • data.local.pty.*feature.terminal.pty.*
  • data.local.ShellTerminalRepositoryfeature.terminal.ShellTerminalRepository
  • core.linux.*feature.terminal.linux.*
  • data.buildsystem.install.*feature.buildrun.install.*
  • data.onboarding.*feature.onboarding.data.*
  • :data:git moved off data.local onto its own data.git root

:app no longer declares components implemented by :feature:buildrun. InstallConfirmActivity, InstallStatusReceiver and RemoteBuildKeepAliveService are declared in the buildrun manifest and reach the app through manifest merging.

Nothing moved between modules. Every file stayed in the module it was already in; only its package changed.

The terminal rename is not a Kotlin-only refactor

The PTY package name is written down in three places that must agree, and only one of them is Kotlin:

  • the NativePty object
  • the JNI function names in app/src/main/cpp/asl_pty.c, which encode the package into the symbol
  • the R8 keep rule in app/src/main/keepRules/rules.keep

A mismatch does not fail the build. It fails at runtime with UnsatisfiedLinkError the first time a terminal is opened, or only in a release build if the keep rule stops matching and R8 strips the native methods. All three were changed together.

Behavior

No behavior change. Package declarations, imports, JNI symbol names, one keep rule, and manifest ownership.

One latent problem surfaced and was fixed: JGitGitRepository used FileChangeBus and DefaultWorkspaceWriteGate from :data:local with no import, which only compiled because the split package made them look local. They are now explicit imports. That is exactly the coupling a split package conceals.

Scope

:data:git and :data:local remain separate modules with a real dependency between them; only the package overlap is gone.

Tests

  • New NativePtyBindingTest in :app ties the JNI symbols in asl_pty.c to the class named by the R8 keep rule, and asserts every declared native method has an implementation. A future package rename that updates Kotlin but forgets the C or the keep rule now fails a test instead of a device.
  • 709 tests passing, 0 failures.

Verification

From a clean worktree at the branch tip:

  • ./gradlew test detekt verifyModuleBoundaries :app:assembleDebug — BUILD SUCCESSFUL
  • 709 tests, 0 failures

Two checks the build alone does not perform:

JNI symbols were read back out of the compiled library. libasl_pty.so exports exactly the four expected symbols under the new package, matching the four external fun declarations. The guard test was also verified to fail when the C symbols are deliberately reverted.

The merged manifest was inspected, not just the assemble result. All three relocated components appear in app/build/intermediates/merged_manifests/debug/.../AndroidManifest.xml with their names, exported flags, theme and foregroundServiceType intact. Manifest merging can succeed while producing a subtly wrong component.

@AhmadKharfan
AhmadKharfan merged commit ba174aa into develop Jul 29, 2026
3 checks passed
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