Skip to content

Let features own their navigation graphs - #80

Merged
AhmadKharfan merged 1 commit into
developfrom
refactor/feature-owned-navigation
Jul 29, 2026
Merged

Let features own their navigation graphs#80
AhmadKharfan merged 1 commit into
developfrom
refactor/feature-owned-navigation

Conversation

@AhmadKharfan

Copy link
Copy Markdown
Owner

Why

Every route in the app was declared in one Routes object in :app, and NavGraphs.kt built every destination itself, importing screen composables from all seven feature modules. A feature could not describe its own navigation; adding a screen meant editing :app.

What changed

Four features now own their routes and register their own destinations:

  • :feature:onboardingOnboardingRoutes + onboardingGraph
  • :feature:settingsSettingsRoutes + settingsGraph
  • :feature:projectsProjectsRoutes + projectsGraph
  • :feature:terminalTerminalRoutes + terminalGraph

:app keeps the single NavHost and calls those graphs. Cross-feature navigation stays as callbacks supplied by :app, so no feature learns another feature's routes: onboarding takes an onFinished, projects takes onOpenProject and onOpenPreferences.

encodeRouteArg moved to :core:common so any module building a route can escape its arguments, rather than the helper being private to :app.

ProjectsRoutes also owns the picked_folder saved-state key. Its producer (the folder picker) and both consumers (hub, create-project) are all inside :feature:projects, so the protocol belongs there. The git_conflict_path key deliberately stays in :app — its producer and consumer are in different features, and moving it would make one import a constant from the other, recreating the coupling at the navigation layer.

Behavior

No behavior change, and this was checked rather than assumed: all 15 relocated route strings are byte-identical to their previous values, verified by diffing the moved constants against Routes.kt at the previous commit. Destination registration order, argument extraction and composable wiring are unchanged; only the file that declares them moved.

Scope

Two deliberate exclusions.

Type-safe routes are not part of this change. The plan listed converting from string routes to type-safe routes as a preparatory step. It changes route matching and argument parsing, and the only meaningful verification for that is on a device. Keeping the strings identical is what makes this change provably behaviour-preserving, so the conversion is better done separately.

The editor and git destinations stay in :app for now. They are entangled: the editor's navigation callbacks target git destinations, and git's conflict screen hands a path back to the editor. Splitting them is part of the change that eliminates the editor-to-git dependency, where the contract between them is being designed anyway.

Tests

  • 714 tests passing, 0 failures.
  • RoutesTest continues to cover the route builders that remain in :app.

Verification

From a clean worktree at the branch tip:

  • ./gradlew test detekt verifyModuleBoundaries :app:assembleDebug — BUILD SUCCESSFUL
  • 714 tests, 0 failures
  • All 15 moved route strings confirmed identical to the previous commit

What is not verified here: back-stack behaviour, saved-state result delivery across process death, and predictive back are only observable on a device, and this change was not exercised on one. The byte-identical route strings and unchanged wiring are the argument that behaviour is preserved; a device pass over hub → create project → folder picker → back, and onboarding → hub, is still worth doing before release.

Note on included work

This branch also carries the route-argument encoder and its tests, which existed as uncommitted work in the tree from a separate task. Phase 4 needed a shared encoder for features to build routes, so it was moved to :core:common rather than left stranded in :app. The implementation and its tests are unchanged apart from the package.

@AhmadKharfan
AhmadKharfan merged commit 9d1b8b1 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