The crash that only happened on old installs
React Native Worklets kept crashing on my simulator, but only sometimes. Same code, same branch, same machine. The pattern: it fired when I attached current JavaScript to an obsolete 1.1.6 simulator binary. The JS bundle expects one runtime; the native side is running another. Worklets tries to bridge them and dies.
It took a while to pin down because the crash looks like a library bug rather than an environment mismatch. The app runs fine on a freshly built client, so you start suspecting your code. But the fix was simpler than hunting the Worklets stack: stop letting stale clients boot at all.
I added a runtime identity guard to Binderdex's mobile scripts. Metro startup, onboarding audits, and every Maestro E2E entry point now compare the installed app's version and Expo runtime against source. If they don't match, the script refuses to run and prints the exact rebuild command:
export function assessIosClient({
expectedVersion, installedVersion,
expectedRuntimeVersion, installedRuntimeVersion,
}) {
const reasons = [];
if (installedVersion !== expectedVersion) {
reasons.push(
`app version ${installedVersion} (expected ${expectedVersion})`
);
}
if (expectedRuntimeVersion &&
installedRuntimeVersion !== expectedRuntimeVersion) {
reasons.push(
`Expo runtime mismatch: ${installedRuntimeVersion}`
);
}
return {
status: reasons.length ? "stale" : "current",
reasons,
};
}
The guard reads the installed plist via plutil and the expected identity from Expo config. Four regression tests lock the behavior in. No more guessing whether the simulator is running today's build or last week's.
One lease provider for all member interruptions
The other thing that shipped: communication prompts, feedback sheets, and the native review nudge all used to fight over the screen independently. Two could pop at once. I replaced that with a single priority-aware lease provider. Only one interruption holds the floor at a time, and higher-priority work jumps the queue while equal-priority requests keep their order:
const PRIORITY_WEIGHT = { high: 3, normal: 2, low: 1 };
function enqueue(queue, item) {
return [...queue, item].sort((a, b) => {
const diff = PRIORITY_WEIGHT[b.priority] - PRIORITY_WEIGHT[a.priority];
return diff || a.order - b.order;
});
}
The lease survives through the native notification permission prompt (which suspends the app) and blocks feedback dismissal while a submission is in flight. Both of those were bugs I caught during the final review pass.
1.1.13 and the E2E bugs
Advancing the app from 1.1.12 to 1.1.13 for the next Store-QA candidate surfaced two test defects hiding behind the stale-client guard. Android E2E was being blocked by the package-level iOS guard, so the Android runner now starts its test variant directly. And Maestro's email sign-in fallback was tapping a button that stayed mounted while busy; the delayed tap could land on Home after a successful redirect and accidentally open a binder idea. The fallback now keys off the idle "Log in" label instead of a coordinate.
Also refreshed the grading company comparison article for SEO, but that one is a content pass.