Skip to content

feat(KernelRace): add iOS, platform chips, and fix the broken Wasm build - #32

Merged
michalharakal merged 3 commits into
developfrom
feature/kernelrace-ios
Aug 20, 2026
Merged

michalharakal merged 3 commits into
developfrom
feature/kernelrace-ios

Conversation

@michalharakal

Copy link
Copy Markdown
Contributor

Summary

Three things, found while auditing iOS support at SKaiNET/SK-TR 0.40.1 (see
SKaiNET-transformers#315):

iOS support

shared/composeApp gain iosArm64/iosSimulatorArm64 targets, mirroring the
existing per-platform expect/actual pattern (Android/Desktop/Wasm already
had one file per concern):

  • LlamaRuntimeBuilder.ios.kt — same shape as the JVM actual, backed by
    PosixPreadRandomAccessSource instead of a JDK file API.
  • IosModelProvider.kt — same cache-or-download shape as DesktopModelProvider,
    but talks to Ktor's Darwin engine directly: skainet-data-source (used by
    the other two providers) is JVM-only per its own gradle.properties
    (skainet.targets=jvm), unreachable from iosMain.
  • Platform.ios.kt — reports the Apple native-cinterop kernel tier;
    supportsKernelRace = false (the two-process split-screen race is
    Android-only).
  • iosApp/ — hand-authored Xcode project shell. Structurally checked
    (balanced pbxproj, valid plist/JSON) but not built
    — this was authored
    without access to macOS/Xcode. First real build happens on a Mac.

Prerequisite: SKaiNET-transformers#315 fixes kllama's stale iOS/macOS
native-kernel install stubs (they were no-op'd, silently leaving packed-quant
matmul on the scalar floor on Apple targets despite the kernels existing).
Until that ships in a release, this iOS build runs correctly but without NEON
acceleration — noted in the README.

Platform-support chips

A small SamplePlatform enum + currentSamplePlatform expect/actual and a
PlatformChips composable in ChatScreen's Scaffold, showing all four
supported platforms with the currently-running one highlighted.

Wasm build fix

The web build compiles but doesn't actually work once deployed: no COOP/COEP
handling anywhere, deployed straight to GitHub Pages (which can't set custom
response headers), and Compose's Wasm/Skiko canvas needs cross-origin
isolation (SharedArrayBuffer) for its multi-threaded renderer.
wasmJsBrowserDevelopmentRun's own dev server sets these headers
automatically — which is why this wasn't caught locally — and
kernelrace-ci.yml only runs a compile check, never an actual browser.

Fix: vendor the well-established coi-serviceworker shim (MIT,
gzuidhof/coi-serviceworker
v0.1.7) into composeApp's wasmJs resources, loaded first in app.html.

Test plan

  • shared/composeApp compile cleanly on every non-Apple target (jvm/android/wasmJs) after each change
  • Full existing CI matrix passes: shared:jvmTest, shared:testDebugUnitTest, composeApp:assembleDebug, shared/composeApp:compileKotlinWasmJs — 0 failures, 0 errors
  • Built :composeApp:wasmJsBrowserDistribution and served the production output with a plain static server (no custom headers, reproducing GitHub Pages) — confirmed neither COOP nor COEP is set natively, confirming the shim is necessary
  • iOS/macOS targets — not verified, no Mac in this environment. Needs a real Xcode open + build before merging, or at minimum before announcing iOS support publicly.
  • Wasm fix — build-time reproduction done above; the actual browser-side pass/fail (does cross-origin isolation actually kick in after the shim's first-load reload) needs a real browser check.

michalharakal and others added 2 commits August 13, 2026 11:37
## iOS support

shared/composeApp gain iosArm64/iosSimulatorArm64 targets, mirroring the
existing per-platform expect/actual pattern:

- LlamaRuntimeBuilder.ios.kt: same shape as the JVM actual, backed by
  PosixPreadRandomAccessSource (skainet-io-core's native64Main) instead
  of a JDK file API.
- IosModelProvider.kt: same cache-or-download shape as DesktopModelProvider,
  but talks to Ktor's Darwin engine directly rather than through
  skainet-data-source's KtorRemoteDataSourceFetcher (that module is
  JVM-only per its own gradle.properties — skainet.targets=jvm — so it
  isn't reachable from iosMain at all).
- Platform.ios.kt: reports the Apple native-cinterop kernel tier;
  supportsKernelRace = false (the two-process split-screen race is an
  Android-only mechanism).
- iosApp/: hand-authored Xcode project shell (SwiftUI entry point
  embedding the KMP framework via embedAndSignAppleFrameworkForXcode).
  Structurally checked (balanced pbxproj, valid plist/JSON) but NOT
  built — this environment has no macOS/Xcode, and Kotlin/Native
  requires a macOS host to compile Apple targets at all. First real
  build happens on a Mac.

Prerequisite (separate repo): SKaiNET-transformers PR #315 fixes
kllama's stale iOS/macOS native-kernel install stubs, which were
no-op'd and silently left packed-quant matmul on the scalar floor on
Apple targets. Until that ships in a release, KernelRace's iOS build
runs correctly but without NEON acceleration — noted in the README.

## Platform-support chips

New SamplePlatform enum + currentSamplePlatform expect/actual
(Platform.kt, alongside the existing kernelTierLabel/supportsKernelRace
pattern) and a PlatformChips composable wired into ChatScreen's
Scaffold — a highlighted chip for whichever of Android/Desktop/Web/iOS
is currently running.

## Wasm build fix

The web build compiles but doesn't actually work once deployed: no
Cross-Origin-Opener-Policy/Cross-Origin-Embedder-Policy handling
anywhere, deployed straight to GitHub Pages (which can't set custom
response headers), and Compose's Wasm/Skiko canvas needs cross-origin
isolation (SharedArrayBuffer) for its multi-threaded renderer.
wasmJsBrowserDevelopmentRun's own dev server sets these headers
automatically, which is why this wasn't caught locally — and
kernelrace-ci.yml only runs a compile check, never an actual browser.

Fix: vendor the well-established coi-serviceworker shim (MIT, gzuidhof/
coi-serviceworker v0.1.7) into composeApp's wasmJs resources, loaded
first in app.html.

Verified: built :composeApp:wasmJsBrowserDistribution and served the
production output with a plain static server (no custom headers,
reproducing GitHub Pages) — confirmed neither header is set natively,
confirming the shim is necessary; couldn't verify the actual
browser-side fix (no browser in this environment).

## Verification

- shared/composeApp compile cleanly on every non-Apple target
  (jvm/android/wasmJs) after each change.
- Full existing CI matrix passes: shared:jvmTest, shared:testDebugUnitTest,
  composeApp:assembleDebug, shared/composeApp:compileKotlinWasmJs — 0
  failures, 0 errors across all suites.
- iOS/macOS targets are unverifiable here (no Mac) — noted explicitly
  rather than claimed.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Same category of bug as the Panama-wrongly-modeled-on-Android fix in the
engine repo (KernelSupportMatrixTest) — a label claiming "scalar" when a
faster kernel tier is actually active. Platform.jvm.kt's kernelTierLabel()
was a hardcoded "JVM (scalar)" string, but composeApp's :run task always
passes --add-modules jdk.incubator.vector (skainetSimdJvmArgs), so the
normal desktop run actually dispatches through PanamaVectorKernelProvider,
not scalar — the label was simply never updated to reflect that.

Fixed by probing PanamaVectorKernelProvider.isAvailable() directly, same
pattern as Android's JniKernelProvider.isAvailable() — a static capability
check, not a KernelRegistry query. That distinction matters here:
ChatViewModel evaluates kernelTierLabel() at construction time, before
DirectCpuExecutionContext.create() has ever run, and KernelRegistry only
gets populated as a side effect of that (via KernelServiceLoader.installAll()
inside DefaultCpuOpsJvm's init). A registry-based label would read an empty
registry and always report "scalar" regardless of what's about to load.

skainet-backend-cpu depends on skainet-backend-api (where KernelProvider is
declared) as implementation-scoped, so it isn't reachable transitively —
added it directly so PanamaVectorKernelProvider.isAvailable() resolves.

Verified: :shared:compileKotlinJvm, full existing CI matrix
(jvmTest/testDebugUnitTest/assembleDebug/compileKotlinWasmJs ×2/
compileDebugKotlinAndroid) all pass, 0 failures.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@michalharakal

Copy link
Copy Markdown
Contributor Author

Real-hardware validation of the iOS work in this PR (opening the Xcode project, building, running, checking the kernel tier) is tracked separately in #33, since it needs an actual Mac — not blocking this PR.

Add a concrete "how cheap adding iOS actually was" bullet — ~200 lines
across 4 files, and the one that matters (LlamaRuntimeBuilder.ios.kt) is
36 lines, nearly a line-for-line copy of the JVM actual, with zero
engine-side changes needed. That's the actual point of this sample: it's
meant to demonstrate how easy it is to build apps with SKaiNET, including
on iOS, not just to exercise the engine's targets.

Also fixed the "three kernel paths" count to four now that iOS's
native-cinterop tier exists alongside Android/Desktop/Wasm.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@michalharakal
michalharakal merged commit b61b958 into develop Aug 20, 2026
1 check passed
@michalharakal
michalharakal deleted the feature/kernelrace-ios branch August 20, 2026 16:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant