Symptom

iOS app’s first-time experience showed “No walkthroughs are available on this platform yet.” in the FlowChooser empty state. Android (and JVM/desktop) worked fine — every flow surfaced as expected. WASM was unaffected because it fetches flow JSONs over HTTP rather than from the classpath.

Root cause

Flow JSON files lived at shared/src/commonMain/resources/flows/<id>.json. That path is on the JVM/Android classpath but Kotlin/Native iOS frameworks do not carry JVM resources. NSBundle.mainBundle.pathForResource(...) in shared/src/iosMain/.../FeatureLoader.ios.kt::readClasspathResource returned nil for every flow id, so FlowRegistry.loadAll() populated an empty map, and the chooser fell through to its empty-state branch.

The wider class of bug: anything that lands in shared/src/commonMain/resources/ and depends on classpath access at runtime is invisible on iOS.

Fix

Prevention

Two regression guards added in FlowRegistryTest:

  1. everyBundledFlowIdHasAComposeResourcesFile — fails the JVM test suite if a bundledFlowIds entry has no JSON file at the iOS-bundling path.
  2. noStrayFlowsLeftInSharedResources — fails if any future commit re-adds shared/src/commonMain/resources/flows/*.json. That path silently works on Android/JVM and silently breaks iOS, which is exactly the trap that produced this bug.

General rule: if a non-composeResources resource needs to ship in the mobile apps, it doesn’t ship on iOS. New runtime-loaded resources go under composeApp/src/commonMain/composeResources/, not under shared/src/commonMain/resources/. The shared resources tree is JVM-only — fine for tests, fine for the server, not fine for KMP-mobile-loaded data.