On Monday morning I opened a Rork project and the model menu had changed. Claude Opus 5.5 and GPT-6 Sol were sitting next to GPT-6 Astra, which had arrived earlier. The changelog entry is dated September 22 and says both are included in the Pro and Max plans; on the free plan, the menu shows you which plan unlocks them.
Whenever a new model shows up, there is one thing I always do first. I ask it to rebuild the three-column gallery screen I've been refining in my wallpaper app for years. Thumbnails in a grid, tap to open, pull to refresh — nothing more. It's a small screen, but it's the fastest way I know to feel how a tool behaves.
This time I built that one screen with Opus 5.5 and again with GPT-6 Sol. Below is how I set the effort level on each, where the two diverged, and which one I decided to keep using.
Decide the screen before you decide the model
If I may put the conclusion first: write the full spec of the screen before you pick a model. If the spec is still moving while you compare two models, you'll never know afterwards whether a difference came from the model or from your own wording.
Here is the single prompt I gave to both. I used it twice, word for word.
Build one gallery screen.
- Square thumbnails in 3 columns (size computed from the screen width)
- Tapping a thumbnail navigates to the detail screen /wallpaper/[id]
- Pull down to reload the list
- Use expo-image for the images
- Use a placeholder array of 12 items; loading must live in one place so I can swap in an API later
- Do not add anything not listed here: no search, no favorites, no tabsThat last line turned out to be the one that mattered most. Unless you say up front not to add unlisted features, a model will helpfully add them, and reading and removing the extras takes longer than writing the prompt did.
Opus 5.5 — why I moved effort back down to Medium
Opus 5.5 offers five effort steps: Low, Medium, High, Extra High, and Max. I picked Max without thinking. More steps meant the top one must be best — that was my assumption.
It didn't go well. The generation took a long time, and the screen came back with a favorites heart and an empty search bar, even though the prompt explicitly said not to add them. Raising effort seemed to push the model to think past the edges of the instruction — at least that is how it looked on my machine.
I restored to the earlier point and sent the same prompt at Medium. This time I got exactly the three-column grid, pull-to-refresh, and navigation to the detail screen. Loading was gathered into a single reload function, so there was no doubt where the API call would go later.
That is where I drew a line for myself. When the spec is fully written, start effort from the bottom. I only turn it up when I haven't managed to write the spec.
GPT-6 Sol — three effort steps, and where it differed
GPT-6 Sol has three effort steps: Low, Medium, and High. With fewer steps, my hand doesn't hover over the menu. To keep the comparison fair, I sent the identical prompt at Medium.
The skeleton that came back was strikingly similar. A FlatList with numColumns for the three columns, cell size computed from the window width, expo-image for thumbnails. Up to that point, the two were the same.
The differences were in two details. For column spacing, Sol put gap on columnWrapperStyle, while Opus gave each cell its own margin. For the empty state, Sol added a "No wallpapers yet" message I hadn't asked for; Opus left it blank. Neither is wrong. What showed through was each model's temperament in filling the parts I hadn't specified.
I tried Low as well. It returned a version where refreshing didn't toggle back on every reload, and I had to ask for a fix. For this screen, the middle of the three steps was the right place to sit.
The gallery screen I kept — readable even if you don't write code
After lining up both outputs, here is the version I kept. If you don't write code, you only need to follow three spots: the COLUMNS number, the SAMPLE array, and where onPress goes.
import { useCallback, useState } from "react";
import { FlatList, Pressable, StyleSheet, Text, useWindowDimensions, View } from "react-native";
import { Image } from "expo-image";
import { useRouter } from "expo-router";
type Wallpaper = { id: string; title: string; thumb: string };
const COLUMNS = 3; // change the column count here and nowhere else
const GAP = 4;
// The image host comes from an env variable (only EXPO_PUBLIC_ names reach the app)
const IMAGE_BASE = process.env.EXPO_PUBLIC_IMAGE_BASE ?? "";
// Placeholder data to be replaced by the API later
const SAMPLE: Wallpaper[] = Array.from({ length: 12 }, (_, i) => ({
id: String(i + 1),
title: `Sample ${i + 1}`,
thumb: `${IMAGE_BASE}/thumb-${i + 1}.jpg`,
}));
export default function GalleryScreen() {
const { width } = useWindowDimensions();
const size = (width - GAP * (COLUMNS + 1)) / COLUMNS; // cell size from the screen width
const router = useRouter();
const [items, setItems] = useState<Wallpaper[]>(SAMPLE);
const [refreshing, setRefreshing] = useState(false);
// Loading lives here and only here. Swap this body for the API call later
const reload = useCallback(async () => {
setRefreshing(true);
try {
await new Promise((resolve) => setTimeout(resolve, 600));
setItems((prev) => [...prev].reverse());
} finally {
setRefreshing(false);
}
}, []);
return (
<FlatList
data={items}
numColumns={COLUMNS}
keyExtractor={(w) => w.id}
contentContainerStyle={{ padding: GAP }}
columnWrapperStyle={{ gap: GAP, marginBottom: GAP }}
refreshing={refreshing}
onRefresh={reload}
renderItem={({ item }) => (
<Pressable
accessibilityLabel={item.title}
onPress={() => router.push({ pathname: "/wallpaper/[id]", params: { id: item.id } })}
style={{ width: size, height: size }}
>
<Image
source={{ uri: item.thumb }}
style={StyleSheet.absoluteFill}
contentFit="cover"
transition={150}
/>
</Pressable>
)}
ListEmptyComponent={
<View style={styles.empty}>
<Text>No wallpapers yet</Text>
</View>
}
/>
);
}
const styles = StyleSheet.create({
empty: { padding: 32, alignItems: "center" },
});A word on each choice. I took Sol's gap for the spacing — per-cell margin is a common reason the last column ends up a few points narrower than the others. I also kept Sol's empty state. The try / finally is mine: if a reload fails, refreshing must not stay stuck on.
The cell size comes from useWindowDimensions so the three columns hold on iPad and in landscape. I once had to rework a wallpaper screen laid out with fixed widths when the app moved to iPad, and since then this one line goes in from the start.
How I decided which one to keep using
Here is the side-by-side.
| Item | Claude Opus 5.5 | GPT-6 Sol |
|---|---|---|
| Effort steps | 5 (Low / Medium / High / Extra High / Max) | 3 (Low / Medium / High) |
| Step I used for this screen | Medium (Max added features) | Medium (Low left refreshing broken) |
| How it fills unspecified parts | Leaves them out | Adds an empty state |
| Plans that include it | Pro / Max | Pro / Max |
| What I use it for | Writing out a screen whose spec is settled | A first draft when the spec still has holes |
If you are on the free plan, choosing either of these shows the plan that unlocks it. Before paying, it helps to separate whether you're stuck on price or on how you're writing the prompt. On this one screen, I confirmed twice that the cause was my prompt.
Rork rewrites code on every generation, so whichever model you continue with, you'll want a habit of reading only the lines that changed. My routine is in "Reading only what Rork rewrote — the diff review I run on every re-export". For the SwiftUI side, where I tested how far Rork Max can be trusted feature by feature, see "Testing Rork Max's SwiftUI native generation across 30 features".
Pick the smallest list screen in your own app, copy just the "do not add anything not listed here" line from the prompt above, and build it once with each model at Medium. Turning effort up can wait until after that.