Concepts
Native builds
Fingerprinted, single-flight native builds shared by every worktree and agent.
Most branches only change JavaScript. simfleet makes sure those branches reuse an existing native build, and that when a native build is needed, it happens exactly once no matter how many agents ask.
How it works
- Fingerprint. simfleet hashes the native inputs of a worktree and excludes application JavaScript:
app.config.js/app.json, dependency declarations inpackage.json(scripts and metadata are ignored), the lockfile,react-native.config.js, theplugins,patches, andfirebasedirectories, plus anynativeFingerprintpaths you declare. The platform, build configuration, CPU architecture, and Xcode version are part of the hash. From the project config onlyprojectName,nativeAuth, andnativeShellscount, so changing ports or Metro env never invalidates a build. - Look up. The fingerprint names a cached
.appin~/Library/Caches/<cacheDirectoryName>/native. - Single-flight. On a miss, the first caller builds; every concurrent caller waits for that job and reuses the result.
simfleet native plan <worktree> debug <udid> # fingerprint, hit/miss, installed state — builds nothing
simfleet native ensure <worktree> <udid> debug # build if needed, then installplan also reports whether the installed bundle identifier is declared in nativeAuth.allowedAppIdentifiers, so an auth provider that validates app identifiers never sees an unexpected one.
With Expo
simfleet ships an Expo build cache provider, so expo run:ios also reuses fleet builds:
bun add -d simfleetmodule.exports = {
expo: {
experiments: {
buildCacheProvider: { plugin: require.resolve("simfleet/build-cache") },
},
},
};When to rebuild
Rebuild only when native inputs changed: pods, native code, config plugins, entitlements, build settings, or a dependency with native code. native plan tells you: a JS-only branch reports the same fingerprint as main and a cache hit.
Tip
Agents should always go through native plan / native ensure rather than calling expo run:ios, CocoaPods, or Xcode directly. That is what keeps ten agents from running ten identical builds.
Identity model
Bundle identifiers are stable native identities, not worktree identities. Every worktree installs the same debug app; isolation comes from each lane having its own device and Metro port. This keeps auth providers, push entitlements, and Firebase configs working without per-branch registrations.