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

  1. Fingerprint. simfleet hashes the native inputs of a worktree and excludes application JavaScript: app.config.js/app.json, dependency declarations in package.json (scripts and metadata are ignored), the lockfile, react-native.config.js, the plugins, patches, and firebase directories, plus any nativeFingerprint paths you declare. The platform, build configuration, CPU architecture, and Xcode version are part of the hash. From the project config only projectName, nativeAuth, and nativeShells count, so changing ports or Metro env never invalidates a build.
  2. Look up. The fingerprint names a cached .app in ~/Library/Caches/<cacheDirectoryName>/native.
  3. 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 install

plan 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 simfleet
module.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.