navigation — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited navigation (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
File-based routing. Every file in app/ is a route. Never string-concatenate paths.
app/
├── _layout.tsx ← root layout (providers, fonts, theme)
├── (auth)/ ← guest-only group
│ ├── _layout.tsx ← redirects logged-in users to (app)
│ ├── login.tsx
│ └── register.tsx
├── (app)/ ← protected group
│ ├── _layout.tsx ← redirects guests to (auth)/login
│ └── (tabs)/
│ ├── _layout.tsx ← tab bar
│ ├── index.tsx ← Home tab
│ └── profile.tsx ← Profile tab
└── +not-found.tsxapp/(auth)/_layout.tsx -- guest only:
import { Redirect, Stack } from 'expo-router'
import { useSession } from '@/hooks/use-session'
export default function AuthLayout() {
const { session } = useSession()
if (session) return <Redirect href="/(app)/(tabs)/" />
return <Stack screenOptions={{ headerShown: false }} />
}app/(app)/_layout.tsx -- protected:
import { Redirect, Stack } from 'expo-router'
import { useSession } from '@/hooks/use-session'
export default function AppLayout() {
const { session } = useSession()
if (!session) return <Redirect href="/(auth)/login" />
return <Stack screenOptions={{ headerShown: false }} />
}app/(app)/(tabs)/_layout.tsx:
import { Tabs } from 'expo-router'
import { HomeIcon, UserIcon } from 'lucide-react-native'
export default function TabLayout() {
return (
<Tabs screenOptions={{ tabBarActiveTintColor: '#6366f1', headerShown: false }}>
<Tabs.Screen
name="index"
options={{ title: 'Home', tabBarIcon: ({ color }) => <HomeIcon color={color} size={24} /> }}
/>
<Tabs.Screen
name="profile"
options={{ title: 'Profile', tabBarIcon: ({ color }) => <UserIcon color={color} size={24} /> }}
/>
</Tabs>
)
}<Stack>
<Stack.Screen name="index" options={{ title: 'Home' }} />
<Stack.Screen name="[id]" options={{ title: 'Detail', headerBackTitle: 'Back' }} />
</Stack>Enable in app.json:
{ "expo": { "experiments": { "typedRoutes": true } } }import { router, Link } from 'expo-router'
// ✅ Typed -- compile-time checked
router.push({ pathname: '/(app)/(tabs)/', params: { tab: 'home' } })
router.push({ pathname: '/(app)/[id]', params: { id: item.id } })
<Link href={{ pathname: '/(app)/[id]', params: { id: item.id } }}>
Open
</Link>
// ❌ Never string concatenation
router.push(`/items/${item.id}`)import { useLocalSearchParams, useGlobalSearchParams } from 'expo-router'
// Local -- only params from current route segment
const { id } = useLocalSearchParams<{ id: string }>()
// Global -- params from any segment in the URL
const { tab } = useGlobalSearchParams<{ tab: string }>()// app/(app)/modal.tsx
import { router } from 'expo-router'
import { View, Text } from 'react-native'
import { Pressable } from 'react-native'
export default function Modal() {
return (
<View className="flex-1 items-center justify-center">
<Text className="text-lg font-semibold">Modal</Text>
<Pressable onPress={() => router.back()}>
<Text>Close</Text>
</Pressable>
</View>
)
}
// In _layout.tsx
<Stack.Screen name="modal" options={{ presentation: 'modal' }} />app.json:
{
"expo": {
"scheme": "myapp",
"ios": { "associatedDomains": ["applinks:myapp.com"] },
"android": { "intentFilters": [{ "action": "VIEW", "data": [{ "scheme": "myapp" }], "category": ["BROWSABLE", "DEFAULT"] }] }
}
}Expo Router handles deep links automatically based on file structure -- no extra config needed.
import { router } from 'expo-router'
router.push({ pathname: '/(app)/[id]', params: { id } })
router.replace({ pathname: '/(auth)/login' })
router.back()
router.canGoBack() // check before calling back()app/+not-found.tsx:
import { Link, Stack } from 'expo-router'
import { View, Text } from 'react-native'
export default function NotFound() {
return (
<>
<Stack.Screen options={{ title: 'Not Found' }} />
<View className="flex-1 items-center justify-center gap-4">
<Text className="text-xl font-bold">Page not found</Text>
<Link href="/(app)/(tabs)/" className="text-primary">Go home</Link>
</View>
</>
)
}~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.