Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
5d2d314
feat(evals): expand Expo coverage
grabbou Jun 24, 2026
93e9bd2
chore(testbench): align Expo SDK 56 packages
grabbou Jun 24, 2026
2d904f4
fix(expo-router): configure data loader eval
grabbou Jun 24, 2026
f8b46e9
fix(expo-modules): make inline module note judgeable
grabbou Jun 24, 2026
f80e113
fix(expo-modules): correct web platform eval
grabbou Jun 24, 2026
2e376b9
fix(expo-sdk): tighten media library asset reference
grabbou Jun 24, 2026
d403dc0
chore(expo-modules): seed static eval inputs
grabbou Jun 24, 2026
8d981f9
docs(expo-sdk): add missing official sources
grabbou Jun 24, 2026
5dff70f
docs(whitepaper): clarify eval metadata contract
grabbou Jun 24, 2026
99d3d85
fix(evals): audit fixes across expo-sdk, expo-router, expo-modules, e…
grabbou Jun 30, 2026
868c7cb
fix(evals): rewrite prompts to be scenario-focused, not prescriptive
grabbou Jun 30, 2026
d026b17
chore(evals): remove redundant and low-signal Expo evals
grabbou Jun 30, 2026
c8d7c4b
refactor(evals): reseed Expo starter templates with realistic baselines
grabbou Jun 30, 2026
b26a2c2
refactor(evals): rebuild Expo references as genuine prompt(app) solut…
grabbou Jun 30, 2026
91ff567
fix(evals): exercise Button in expo-ui/02 and real visibility listene…
grabbou Jun 30, 2026
0f4b03d
fix(evals): QA-pass corrections to Expo references and requirements
grabbou Jun 30, 2026
53b5337
fix(evals): correct expo-ui/11 to a valid Jetpack Compose native-stat…
grabbou Jun 30, 2026
e0ee037
chore(evals): drop the expo-modules category entirely
grabbou Jul 1, 2026
6f6f303
fix(evals): frame expo-ui conversion prompts as migrations, not builds
grabbou Jul 1, 2026
26bfb55
refactor(evals): drop redundant expo-ui host-root eval; de-alias Text…
grabbou Jul 1, 2026
9efd191
chore(evals): renumber expo-sdk and expo-ui sequentially
grabbou Jul 1, 2026
827992d
fix(evals): make expo-router/01 a real SDK 56 import migration
grabbou Jul 1, 2026
490b35a
fix(evals): address PR review on expo-router evals
grabbou Jul 1, 2026
9717bc6
fix(evals): apply PR-review guidelines to expo-sdk and expo-ui
grabbou Jul 1, 2026
f92eda2
refactor(evals): merge the two expo-ui datetimepicker evals into one
grabbou Jul 1, 2026
82ee332
fix(evals): correct SDK 56 API usage and eval design across expo cate…
grabbou Jul 3, 2026
d5ea484
fix(evals): make path-based requirements judgeable; second-pass eval …
grabbou Jul 3, 2026
3521c92
fix(evals): ground references in package implementation; enforce guar…
grabbou Jul 3, 2026
ba9aeff
fix(evals): converge on implementation-verified behavior across all e…
grabbou Jul 3, 2026
4e3f428
fix(evals): remove mechanism prescriptions from four prompts
grabbou Jul 3, 2026
c87c5c1
fix(evals): address PR review on router/04 and router/09
grabbou Jul 3, 2026
afc92e2
Update prompt for handling PROFILE_NAME securely
grabbou Jul 3, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 13 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,19 @@ A benchmark suite for evaluating how coding models solve real React Native tasks

## Available Evals

Groups map to top-level folders under `evals/`.

| Group | Path | Status |
| ----------------- | ------------------------- | ------ |
| animation | `evals/animation` | Active |
| async-state | `evals/async-state` | Active |
| navigation | `evals/navigation` | Active |
| react-native-apis | `evals/react-native-apis` | Active |
| expo-sdk | `evals/expo-sdk` | WIP |
| brownfield | `evals/brownfield` | WIP |
| nitro-modules | `evals/nitro-modules` | WIP |
| lists | `evals/lists` | Active |
Groups map to top-level folders under `evals/`. The current suite contains 134 evals.

| Group | Path | Evals | Status |
| ----------------- | ------------------------- | ----: | ------ |
| animation | `evals/animation` | 13 | Active |
| async-state | `evals/async-state` | 13 | Active |
| expo-router | `evals/expo-router` | 11 | Active |
| expo-sdk | `evals/expo-sdk` | 25 | Active |
| expo-ui | `evals/expo-ui` | 7 | Active |
| lists | `evals/lists` | 18 | Active |
| navigation | `evals/navigation` | 13 | Active |
| react-native-apis | `evals/react-native-apis` | 9 | Active |
| skia | `evals/skia` | 25 | Active |

> Want a group that is not listed here? [Open an issue](https://github.com/callstackincubator/evals/issues/new/choose) to request it. Contributions are also welcome.

Expand Down
1,232 changes: 444 additions & 788 deletions bun.lock

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { DefaultTheme, ThemeProvider } from '@react-navigation/native'
import { Stack } from 'expo-router'

export default function Layout() {
return (
<ThemeProvider value={DefaultTheme}>
<Stack screenOptions={{ headerShown: true }}>
<Stack.Screen name="index" options={{ title: 'Home' }} />
</Stack>
</ThemeProvider>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { Text, View } from 'react-native'

export default function Index() {
return (
<View>
<Text>Home</Text>
</View>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Migrate an Expo Router layout to SDK 56 import conventions.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SDK 56+? there is 57 already

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On one hand, correct. On the other, it's good this implementation detail is marked explicitly. We can roll out 57 anytime. Now, those models will have little knowledge of 57.

Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { DefaultTheme, Stack, ThemeProvider } from 'expo-router'

export default function Layout() {
return (
<ThemeProvider value={DefaultTheme}>
<Stack screenOptions={{ headerShown: true }}>
<Stack.Screen name="index" options={{ title: 'Home' }} />
</Stack>
</ThemeProvider>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { Text, View } from 'react-native'

export default function Index() {
return (
<View>
<Text>Home</Text>
</View>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
version: 1
inputs:
files:
- app/_layout.tsx
- app/index.tsx
requirements:
- id: imports-from-expo-router
description: 'Must import router hooks/themes/navigation helpers from expo-router exports rather than direct @react-navigation packages.'
- id: no-direct-react-navigation-imports
description: 'Must not import from @react-navigation/native or @react-navigation/native-stack in app route files.'
- id: keeps-file-system-layout
description: 'Must keep navigation defined through Expo Router layout and route files.'
- id: migrates-theme-import-to-expo-router
description: 'Must import React Navigation theming such as ThemeProvider and DefaultTheme from expo-router itself, and keep wrapping the Stack in it, rather than dropping it, importing from @react-navigation/native, or using the deprecated expo-router/react-navigation subpath.'
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { Slot } from 'expo-router'

export default function Layout() {
return <Slot />
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { StyleSheet, Text, View } from 'react-native'

const PRODUCT = { id: '42', name: 'Aurora Headphones' }

export default function Index() {
return (
<View style={styles.container}>
<Text style={styles.title}>{PRODUCT.name}</Text>
</View>
)
Comment thread
grabbou marked this conversation as resolved.
}

const styles = StyleSheet.create({
container: { flex: 1, gap: 12, padding: 24 },
title: { fontSize: 20, fontWeight: '600' },
})
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Set up a stack layout and add a Details screen. On the Products screen, add a button that navigates to Details and passes the product id as a route parameter. The Details screen must read that id from the route and render it.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { Stack } from 'expo-router'

export default function Layout() {
return (
<Stack>
<Stack.Screen name="index" options={{ title: 'Products' }} />
<Stack.Screen name="details" options={{ presentation: 'card', title: 'Details' }} />
</Stack>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { useLocalSearchParams } from 'expo-router'
import { StyleSheet, Text, View } from 'react-native'

export default function Details() {
const { id } = useLocalSearchParams<{ id?: string }>()

return (
<View style={styles.container}>
<Text style={styles.title}>Details</Text>
<Text>Product {id ?? 'unknown'}</Text>
</View>
)
}

const styles = StyleSheet.create({
container: { flex: 1, gap: 12, padding: 24 },
title: { fontSize: 20, fontWeight: '600' },
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { Link } from 'expo-router'
import { StyleSheet, Text, View } from 'react-native'

const PRODUCT = { id: '42', name: 'Aurora Headphones' }

export default function Index() {
return (
<View style={styles.container}>
<Text style={styles.title}>{PRODUCT.name}</Text>
<Link href={{ pathname: '/details', params: { id: PRODUCT.id } }}>View details</Link>
</View>
)
}

const styles = StyleSheet.create({
container: { flex: 1, gap: 12, padding: 24 },
title: { fontSize: 20, fontWeight: '600' },
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
version: 1
inputs:
files:
- app/_layout.tsx
- app/index.tsx
requirements:
Comment thread
grabbou marked this conversation as resolved.
- id: uses-expo-router-stack
description: 'Must render Stack from expo-router in _layout.tsx and not call createNativeStackNavigator or NavigationContainer.'
- id: navigates-to-details
description: 'Must navigate from the Products screen to the details route using expo-router Link or router navigation.'
- id: passes-id-param
description: 'Must pass the product id as a route parameter when navigating to details.'
- id: details-reads-id-with-hook
description: 'The details screen must read the id from the route with useLocalSearchParams or useGlobalSearchParams.'
- id: details-renders-id
description: 'The details screen must render the received id value.'
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { Stack } from 'expo-router'

export default function Layout() {
return (
<Stack>
<Stack.Screen name="index" />
<Stack.Screen name="details" />
</Stack>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { StyleSheet, Text, View } from 'react-native'

export default function Details() {
return (
<View style={styles.container}>
<Text style={styles.title}>Details</Text>
</View>
)
}

const styles = StyleSheet.create({
container: { flex: 1, gap: 12, padding: 24 },
title: { fontSize: 20, fontWeight: '600' },
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { Link } from 'expo-router'
import { StyleSheet, Text, View } from 'react-native'

export default function Index() {
return (
<View style={styles.container}>
<Text style={styles.title}>Inbox</Text>
<Text>Tap the header action to add a new item.</Text>
<Link href="/details">Open details</Link>
</View>
)
}

const styles = StyleSheet.create({
container: { flex: 1, gap: 12, padding: 24 },
title: { fontSize: 20, fontWeight: '600' },
})
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Configure the Inbox (index) screen header from the Stack layout: set its title and add a right-side header button that shows an alert when pressed. A Details screen is pushed from the Inbox; configure it so its back button reads a custom label ("All mail") instead of the previous screen's title.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { Stack } from 'expo-router'
import { Alert, Pressable, Text } from 'react-native'

export default function Layout() {
return (
<Stack>
<Stack.Screen
name="index"
options={{
title: 'Inbox',
headerRight: () => (
<Pressable onPress={() => Alert.alert('New item')}>
<Text>+</Text>
</Pressable>
),
}}
/>
<Stack.Screen
name="details"
options={{
headerBackTitle: 'All mail',
}}
/>
</Stack>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { StyleSheet, Text, View } from 'react-native'

export default function Details() {
return (
<View style={styles.container}>
<Text style={styles.title}>Details</Text>
</View>
)
}

const styles = StyleSheet.create({
container: { flex: 1, gap: 12, padding: 24 },
title: { fontSize: 20, fontWeight: '600' },
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { Link } from 'expo-router'
import { StyleSheet, Text, View } from 'react-native'

export default function Index() {
return (
<View style={styles.container}>
<Text style={styles.title}>Inbox</Text>
<Text>Tap the header action to add a new item.</Text>
<Link href="/details">Open details</Link>
</View>
)
}

const styles = StyleSheet.create({
container: { flex: 1, gap: 12, padding: 24 },
title: { fontSize: 20, fontWeight: '600' },
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
version: 1
inputs:
files:
- app/_layout.tsx
- app/index.tsx
- app/details.tsx
requirements:
- id: uses-stack-screen-options-for-header
description: "Must configure the Inbox header from the Stack layout (via the screen's options or Stack.Toolbar composition), not by importing React Navigation header components into the screens."
- id: header-right-shows-alert
description: 'Must give the Inbox header a right-side pressable button (via headerRight or a Stack.Toolbar button) whose press handler calls Alert.alert.'
- id: sets-inbox-title
description: 'Must set the index (Inbox) screen title via its Stack.Screen options.'
- id: sets-details-back-title
description: "Must set headerBackTitle in the details screen's Stack.Screen options to a custom label distinct from the previous screen's title (which is the default back label) so the back button on the pushed details screen shows that custom label; setting headerBackTitle on the index screen would not affect the back button."
Comment thread
grabbou marked this conversation as resolved.
- id: no-react-navigation-header-imports
description: 'Must not import React Navigation header components or types directly.'
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { Stack } from 'expo-router'

export default function Layout() {
return (
<Stack>
<Stack.Screen name="index" options={{ title: 'Activity' }} />
</Stack>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { StyleSheet, Text, View } from 'react-native'

const ITEMS = Array.from({ length: 20 }, (_, i) => `Activity ${i + 1}`)

export default function Index() {
return (
<View style={styles.container}>
{ITEMS.map((item) => (
<Text key={item} style={styles.row}>
{item}
</Text>
))}
</View>
)
}

const styles = StyleSheet.create({
container: { padding: 16, gap: 8 },
row: { fontSize: 16 },
})
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add a large title to the Activity screen that starts large and collapses as the list scrolls.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm wondering about one thing: if we're not missing context. The requirements rely on Expo (e.g. use Expo Router Stack). But will the agent know that they are expected to use Expo?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should now by current base file which uses Expo already and package.json. I will verify it's there.

Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { Stack } from 'expo-router'

export default function Layout() {
return (
<Stack screenOptions={{ headerLargeTitleEnabled: true }}>
<Stack.Screen name="index" options={{ title: 'Activity' }} />
</Stack>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { ScrollView, StyleSheet, Text } from 'react-native'

const ITEMS = Array.from({ length: 20 }, (_, i) => `Activity ${i + 1}`)

export default function Index() {
return (
<ScrollView contentInsetAdjustmentBehavior="automatic" contentContainerStyle={styles.container}>
{ITEMS.map((item) => (
<Text key={item} style={styles.row}>
{item}
</Text>
))}
</ScrollView>
)
}

const styles = StyleSheet.create({
container: { padding: 16, gap: 8 },
row: { fontSize: 16 },
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
version: 1
inputs:
files:
- app/_layout.tsx
- app/index.tsx
requirements:
- id: large-title-option
description: 'Must enable the large title via the Stack headerLargeTitleEnabled option (not the deprecated headerLargeTitle spelling).'
- id: scroll-view-first-child
description: 'Must make the page content start with a ScrollView or compatible scrollable child.'
- id: content-inset-adjustment
description: 'Must set contentInsetAdjustmentBehavior to automatic on the scrollable so the large title collapses correctly.'
- id: stack-only
description: 'Must use Expo Router Stack, not a manually created native stack.'
Loading