Crosswake Web-To-Mobile Migration Guide
View SourceThis is an operational route inventory guide for existing Phoenix SaaS teams. It is not a mobile rewrite essay. The working rule is simple: default most routes to Phoenix/LiveView, then promote only when a route has a concrete owner reason.
Use this guide after guides/user_flows.md and guides/route_policy.md. Those guides explain the route-owner mental model; this one turns it into a migration pass you can run against a real Phoenix product.
Default
Default most routes to :live_view.
That is not a compromise. It is the expected outcome for dashboards, accounts, settings, approval queues, billing history, admin screens, and normal SaaS forms. Phoenix already owns the data, auth, rendering, and validation loop for those routes. Crosswake makes that ownership explicit when the product crosses into a mobile shell.
Promote only for one of these reasons:
- degradable bounded native affordance
- cached read-only
- true local mutation/replay
- native-owned device session
- backend/provider authority
- explicit defer
Migration Passes
Pass 1: Inventory routes by user job
List routes by what the user is trying to do, not by controller, LiveView module, or navigation location. Start with the three adopter profiles:
- Phoenix SaaS Portal: mostly LiveView product routes with one narrow native affordance.
- Selective Native Flow: mostly Phoenix-owned routes with one native-owned device-heavy corridor.
- Local-First Study Flow: cached read-only neighbors plus one offline island with local mutation and replay.
Pass 2: Assign the initial owner
Give every route an initial owner before adding seams:
:live_viewfor ordinary Phoenix product routes.:live_viewplus one bounded bridge affordance when native help is low-frequency and degradable.:live_viewplus cached read-only posture when stale reads are useful and safe.:offline_islandonly when the route truly owns local mutation/replay.:native_screenonly when native code owns the session loop.- backend/provider seam when device or provider evidence feeds Phoenix/backend authority.
- explicit defer when support would outpace proof.
Pass 3: Add only required seams
After owner assignment, add the smallest route-local contract that owner needs:
- bounded bridge capabilities for one-shot actions such as haptics, app info, or a transfer-backed file picker
cache_contractfor cached read-only routesisland_contract,packs, andsyncfor true offline islandspacksandtransfersfor native-owned capture or transfer corridorsauth_min_level,requires_recent_auth,auth_posture, andauth_returnfor backend-owned auth posture- commerce and notification-open declarations only as provider-neutral evidence or activation seams
gated_byandon_unavailablefor explicit defers or rollout gates
Pass 4: Run doctor and support checks
Run the route through manifest and support truth before turning it into public proof:
mix crosswake.doctor --router Elixir.YourAppWeb.Router
mix crosswake.doctor --router Elixir.YourAppWeb.Router --native-checks
Use guides/support_matrix.md for proof classes and guides/compatibility.md for runtime-line and rebuild guidance. The support matrix is the canonical dense reference; this guide is only the inventory workflow.
Pass 5: Capture evidence for owner classes you use
Do not try to prove every possible mobile path. Capture evidence only for the owner classes your app actually uses:
- Phoenix-owned LiveView route activation
- one bounded bridge affordance, if declared
- cached read-only degradation, if declared
- one offline island replay path, if declared
- one native-owned route-unavailable or native-screen path, if declared
- backend/provider reconciliation posture, if declared
Phase 118 owns the command-verified quick start and adoption rewrite. Treat
examples/QUICK_START.md and guides/adoption.md as hands-on
follow-up surfaces, not as the source of this route-inventory contract.
Worksheet
| Route | User job | Initial owner | Promotion reason | Required seams | Evidence |
|---|---|---|---|---|---|
/saas/dashboard | check current work | :live_view | none | manifest route, auth posture | Phoenix-owned route activation |
/saas/approvals/:id | approve one item | :live_view | degradable bounded native affordance | capabilities: ["haptics.impact"], entry | approval still succeeds without haptics |
/study/history | review previous work | :live_view | cached read-only | offline: :cached_read_only, cache_contract | stale read is labeled, no mutation |
/study/session | keep studying offline | :offline_island | true local mutation/replay | island_contract, packs, sync | queued/replayed outbox with explicit outcome |
/native/claims/:id/capture | capture claim evidence | :native_screen | native-owned device session | media pack, native-capture transfers, security | fail-closed route or native capture evidence |
/commerce/purchase | start purchase intent | backend/provider seam | backend/provider authority | commerce corridor and backend reconciliation | device/provider event remains evidence |
/scan/document | policy-heavy scanner | :native_screen | explicit defer | gated_by, on_unavailable | unavailable posture until proof exists |
Promotion Reasons
degradable bounded native affordance
Use this when Phoenix owns the route and the shell can help with one low-frequency semantic action. Read guides/bridge.md for the request/reply bridge contract and denial reasons.
cached read-only
Use this when a stale snapshot is useful but mutation would be dishonest. Read guides/offline.md for the difference between cached read-only and an offline island.
true local mutation/replay
Use this only when the route owns local writes, durable outbox or journal entries, and explicit replay/reconciliation outcomes. Nearby routes can still stay Phoenix-owned cached reads.
native-owned device session
Use this when native code owns a capture, scan, or device-heavy session loop. Read guides/native_shell.md for manifest-first activation, route-unavailable behavior, and native-owned routes.
backend/provider authority
Use this when device, storefront, webhook, notification, or provider data is evidence for Phoenix/backend authority. Read guides/capabilities.md for backend seam vocabulary and guides/compatibility.md for rebuild and compatibility truth.
explicit defer
Use this when the route is real but the proof or support contract is not. A defer
with gated_by and on_unavailable is clearer than a silent fallback.
Do Not Migrate This
- Do not move normal SaaS forms native just because the app is mobile.
- Do not push high-frequency client authority through the bridge.
- Do not call cached read-only pages offline mutation.
- Do not treat device or provider events as authority without backend reconciliation.
- Do not present local native hosts as generated public-coordinate proof.
Reference Map
- guides/route_policy.md - owner decisions and current route DSL examples
- guides/bridge.md - bounded bridge envelope, command families, and denials
- guides/offline.md - cached read-only versus offline-island contracts
- guides/native_shell.md - native shell activation and route-unavailable posture
- guides/capabilities.md - capability family ownership vocabulary
- guides/compatibility.md - compatibility axes and rebuild classes
- guides/support_matrix.md - canonical proof and support status