` |
+| Shared preview-panel behavior | Run the affected Maestro suite against both apps |
+| Documentation-only README changes | Prettier on touched Markdown and `git diff --check` |
+
+Common local pitfalls:
+
+- Keep the Compose and Views apps in lock-step. New screens, controls, and test identifiers must
+ exist in both app shells.
+- The apps reach the host mock through `http://10.0.2.2:8000`; no manual `adb reverse` setup is
+ required for normal local runs.
+- The old UiAutomator module is dormant. Add or update Maestro flows instead.
+- After switching branches, force a bridge rebuild if the copied Android UMD asset may not match the
+ checked-out bridge source.
+- If an E2E regression appears in only one app shell, check app test-tag parity before changing SDK
+ behavior.
+
## Related
- [Android SDK](../../packages/android/README.md)
+- [Native bridge architecture](../../packages/universal/optimization-js-bridge/BRIDGE_ARCHITECTURE.md)
- [iOS SDK Reference Implementation](../ios-sdk/README.md)
- [React Native Reference Implementation](../react-native-sdk/README.md)
- [Preview Panel Scenarios](../PREVIEW_PANEL_SCENARIOS.md)
diff --git a/implementations/ios-sdk/README.md b/implementations/ios-sdk/README.md
index bc8e2ba4..3c9c32de 100644
--- a/implementations/ios-sdk/README.md
+++ b/implementations/ios-sdk/README.md
@@ -108,6 +108,57 @@ Useful environment variables:
Unlike the Android app, no port forwarding is required: the iOS Simulator shares the host network,
so the app reaches the mock server at `localhost:8000` directly.
+## Maintainer edit loop
+
+Use this app when you need a debuggable native iOS surface for changes in
+`packages/ios/ContentfulOptimization` or the shared JS bridge. The Xcode project references the SDK
+as a local Swift Package, so app builds compile the package from workspace source rather than from a
+published artifact.
+
+The app schemes include a pre-action that builds `@contentful/optimization-js-bridge` before Swift
+Package resource resolution when bridge source is newer than the copied UMD resource. The pre-action
+writes its output to `/tmp/optimization-ios-build-js-bridge.log`; check that file if Xcode appears
+to use a stale bridge bundle or cannot find `pnpm`.
+
+The normal loop is:
+
+1. Edit Swift in `packages/ios/ContentfulOptimization/Sources/...` or bridge TypeScript in
+ `packages/universal/optimization-js-bridge/src/...`.
+2. Build or run the `OptimizationAppSwiftUI` or `OptimizationAppUIKit` scheme. Xcode recompiles the
+ local Swift Package and refreshes the bridge bundle when needed.
+3. Validate with the targeted app flow or XCUITest scenario that exercises the changed behavior.
+
+From the command line, build a shell from `implementations/ios-sdk/`:
+
+```sh
+xcodebuild build \
+ -project OptimizationApp.xcodeproj \
+ -scheme OptimizationAppSwiftUI \
+ -destination 'platform=iOS Simulator,name=iPhone 16'
+```
+
+## Maintainer validation
+
+Run the smallest check that covers the changed surface:
+
+| Change area | Suggested validation |
+| ---------------------------------------- | ------------------------------------------------------------------------------------------------------------------------- |
+| Bridge TypeScript only | `pnpm --filter @contentful/optimization-js-bridge typecheck` and `pnpm --filter @contentful/optimization-js-bridge build` |
+| Swift package behavior | Targeted `xcodebuild test` for the affected app shell or XCUITest class |
+| Preview-panel or cross-platform behavior | Targeted preview-panel XCUITest plus the matching shared scenario contract review |
+| Documentation-only README changes | Prettier on touched Markdown and `git diff --check` |
+
+Common local pitfalls:
+
+- Run `xcodegen generate` after changing `project.yml` or adding, moving, or renaming iOS sources.
+- Build through a scheme, not a raw target, so the bridge pre-action runs before Swift Package
+ resource resolution.
+- If Xcode cannot find `pnpm`, launch Xcode from a shell where `pnpm --version` works or check the
+ pre-action log for the exact PATH issue.
+- After switching branches, force a bridge rebuild if the copied UMD resource has a newer timestamp
+ than the checked-out bridge source.
+- Substitute another available simulator if the local Xcode runtime does not include `iPhone 16`.
+
## Running E2E tests
Run the full suite against both app shells from `implementations/ios-sdk/`:
@@ -149,6 +200,10 @@ IDs identical across platforms so cross-platform regressions are visible in CI d
## Related
- [iOS SDK package status](../../packages/ios/README.md) - Planned native iOS SDK status marker
+- [iOS SDK code map](../../packages/ios/CODE_MAP.md) - Maintainer architecture map for the native
+ iOS package
+- [Native bridge architecture](../../packages/universal/optimization-js-bridge/BRIDGE_ARCHITECTURE.md) -
+ Shared bridge runtime and build notes
- [Mocks package](../../lib/mocks/README.md) - Shared mock API server and fixtures
- [Preview panel scenario contract](../PREVIEW_PANEL_SCENARIOS.md) - Cross-platform preview-panel
scenario source of truth
diff --git a/packages/android/README.md b/packages/android/README.md
index 80ab4c50..14275467 100644
--- a/packages/android/README.md
+++ b/packages/android/README.md
@@ -1,67 +1,268 @@
-# Optimization Android SDK
-
-Native Android (Kotlin) SDK for the Contentful Optimization SDK Suite. Uses a hybrid
-native-JavaScript architecture where Kotlin owns UI, persistence, and lifecycle while a shared
-JavaScript core (via QuickJS, embedded through `io.github.dokar3:quickjs-kt`) handles
-personalization logic, audience qualification, event batching, and preview overrides.
-
-## Current status
-
-> [!CAUTION] Pre-release. API surface is not yet stable.
-
-- Kotlin Android library module under `ContentfulOptimization/`
-- QuickJS JavaScript engine integration via `quickjs-kt`
-- Shared TypeScript bridge under `packages/universal/optimization-js-bridge/`
-- Jetpack Compose UI layer (OptimizationRoot, OptimizedEntry, scroll/view/click tracking)
-- Preview panel with audience/experience overrides, variant selection, and Contentful integration
-- View-based app support via `PreviewPanelActivity`
-- `OptimizationConfig.locale` is the app/content locale candidate used to resolve `client.locale`.
- `OptimizationApiConfig.locale` is the explicit Experience API locale override. Runtime locale
- changes use `OptimizationClient.setLocale(locale)`. Explicit invalid locale values throw, and
- invalid ambient device locale candidates are ignored.
-- `ContentfulLocales(default = "en-US")` is enough for single-locale apps. Add `supported` only when
- the app needs device-locale matching across multiple Contentful locales.
-- Use `client.locale` for app-owned CDA fetches that feed SDK entry resolution. Do not pass
- all-locale CDA responses from `withAllLocales` or `locale=*`; see
- [Entry personalization and variant resolution](../../documentation/concepts/entry-personalization-and-variant-resolution.md#single-locale-cda-entry-contract).
- For the broader locale model, see
- [Locale handling in the Optimization SDK Suite](../../documentation/concepts/locale-handling-in-the-optimization-sdk-suite.md).
-
-## Architecture
-
-The SDK mirrors the iOS SDK architecture:
-
-- **QuickJS** (via `io.github.dokar3:quickjs-kt`) replaces JavaScriptCore as the JavaScript engine
-- **`QuickJsContextManager`** manages the JS runtime on a dedicated single-thread dispatcher
-- **`NativePolyfills`** provides native Kotlin implementations for fetch, timers, crypto, console,
- and URL — the same polyfill JS scripts are shared with iOS
-- **`OptimizationClient`** exposes reactive state via `StateFlow` and async operations via `suspend`
- functions
-- **`SharedPreferencesStore`** persists SDK state across app launches
-- **`ViewTrackingController`** implements the three-phase viewport tracking state machine
-- **Compose UI layer** provides `OptimizationRoot`, `OptimizedEntry`, `OptimizationLazyColumn`,
- `ScreenTrackingEffect`, and click/view tracking modifiers
-- **Preview panel** provides a debug overlay with audience toggles, variant selectors, profile
- inspection, and override management
-
-## Key differences from iOS
-
-| Aspect | iOS | Android |
-| -------------- | ---------------------- | ---------------------------------------- |
-| JS engine | JavaScriptCore | QuickJS (via `quickjs-kt`) |
-| Threading | Main thread | Dedicated single-thread dispatcher |
-| Reactive state | `@Published` / Combine | `StateFlow` / `SharedFlow` |
-| Async | `async`/`await` | `suspend` functions |
-| Persistence | `UserDefaults` | `SharedPreferences` |
-| Lifecycle | `NotificationCenter` | `ProcessLifecycleOwner` |
-| Network | `NWPathMonitor` | `ConnectivityManager` |
-| HTTP | `URLSession` | `OkHttp` |
-| UI framework | SwiftUI | Jetpack Compose |
-| DI / context | `@EnvironmentObject` | `CompositionLocal` |
-| Preview panel | `.sheet` + UIHosting | `ModalBottomSheet` + `ComponentActivity` |
-
-## When to use this directory
-
-Use this SDK when building a native Android application that needs Contentful personalization and
-analytics. For React Native applications, use
-[@contentful/optimization-react-native](../react-native-sdk/README.md) instead.
+
+
+
+
+
+
+Contentful Personalization & Analytics
+
+Optimization Android SDK
+
+
+
+[Guides](https://contentful.github.io/optimization/documents/Documentation.Guides.html) ·
+[Reference](https://contentful.github.io/optimization) · [Contributing](../../CONTRIBUTING.md)
+
+
+
+> [!WARNING]
+>
+> The Optimization SDK Suite is pre-release (alpha). Breaking changes can be published at any time.
+
+The Optimization Android SDK is a pre-release Kotlin Android library for native Android
+applications. It is part of the [Contentful Optimization SDK Suite](../../README.md) and runs shared
+optimization behavior through a local QuickJS bridge while Kotlin code owns native app concerns such
+as persistence, networking, lifecycle handling, Jetpack Compose UI, XML Views UI, and preview-panel
+UI.
+
+
+ Table of Contents
+
+
+- [Getting started](#getting-started)
+ - [Requirements](#requirements)
+ - [Add the dependency](#add-the-dependency)
+ - [Compose quick start](#compose-quick-start)
+ - [XML Views quick start](#xml-views-quick-start)
+- [When to use this package](#when-to-use-this-package)
+- [Reference implementation](#reference-implementation)
+- [Configuration](#configuration)
+ - [Common options](#common-options)
+ - [Locale handling](#locale-handling)
+- [Runtime notes](#runtime-notes)
+- [Related](#related)
+
+
+
+
+## Getting started
+
+### Requirements
+
+- Android `minSdk` 24 or later.
+- Java 11 bytecode support in the consuming Android project.
+- A Kotlin Android application built with Jetpack Compose or XML Views.
+- Maven Central configured in the consuming build.
+
+### Add the dependency
+
+Add the SDK to your Android application module:
+
+```kotlin
+repositories {
+ mavenCentral()
+}
+
+dependencies {
+ implementation("com.contentful.java:optimization-android:")
+}
+```
+
+Use the version that matches the Optimization SDK Suite release you are adopting. The package is
+published to Maven Central as `com.contentful.java:optimization-android`.
+
+### Compose quick start
+
+Compose apps usually initialize the SDK with `OptimizationRoot`, render Contentful entries with
+`OptimizedEntry`, and emit screen events with `ScreenTrackingEffect`.
+
+```kotlin
+val optimizationConfig = OptimizationConfig(
+ clientId = "your-client-id",
+ environment = "master",
+ contentfulLocales = ContentfulLocales(default = "en-US"),
+ defaults = StorageDefaults(consent = true),
+ debug = BuildConfig.DEBUG,
+)
+
+@Composable
+fun AppRoot(heroEntry: Map) {
+ OptimizationRoot(
+ config = optimizationConfig,
+ trackViews = true,
+ trackTaps = false,
+ previewPanel = if (BuildConfig.DEBUG) PreviewPanelConfig() else null,
+ ) {
+ HomeScreen(heroEntry = heroEntry)
+ }
+}
+
+@Composable
+fun HomeScreen(heroEntry: Map) {
+ ScreenTrackingEffect(screenName = "Home")
+
+ OptimizedEntry(
+ entry = heroEntry,
+ trackTaps = true,
+ ) { resolvedEntry ->
+ HeroCard(entry = resolvedEntry)
+ }
+}
+```
+
+For the full Compose flow, see
+[Integrating the Optimization Android SDK in a Jetpack Compose app](../../documentation/guides/integrating-the-optimization-android-sdk-in-a-compose-app.md).
+
+### XML Views quick start
+
+XML Views apps usually initialize the SDK once from `Application.onCreate`, render Contentful
+entries with `OptimizedEntryView`, and emit screen events with `ScreenTracker`.
+
+```kotlin
+class MyApplication : Application() {
+ override fun onCreate() {
+ super.onCreate()
+
+ OptimizationManager.initialize(
+ context = this,
+ config = optimizationConfig,
+ trackViews = true,
+ trackTaps = false,
+ previewPanel = PreviewPanelConfig(),
+ )
+ }
+}
+
+class HomeActivity : AppCompatActivity() {
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ if (BuildConfig.DEBUG) {
+ OptimizationManager.attachPreviewPanel(this)
+ }
+ }
+
+ override fun onResume() {
+ super.onResume()
+ ScreenTracker.trackScreen("Home")
+ }
+
+ fun renderHero(entry: Map): View {
+ return OptimizedEntryView(this).apply {
+ trackTaps = true
+ setContentRenderer { resolvedEntry ->
+ HeroBinder.create(context, resolvedEntry)
+ }
+ setEntry(entry)
+ }
+ }
+}
+```
+
+For the full XML Views flow, see
+[Integrating the Optimization Android SDK in an XML Views app](../../documentation/guides/integrating-the-optimization-android-sdk-in-a-views-app.md).
+
+## When to use this package
+
+Use this package when building a native Android application that needs Contentful Personalization
+and Analytics through Kotlin APIs. The same AAR includes:
+
+- `com.contentful.optimization.core` for the stateful `OptimizationClient`, configuration, entry
+ personalization, event tracking, and preview controls.
+- `com.contentful.optimization.compose` for Jetpack Compose apps.
+- `com.contentful.optimization.views` for XML Views apps.
+
+For React Native applications, use
+[`@contentful/optimization-react-native`](../react-native-sdk/README.md) instead.
+
+## Reference implementation
+
+The [Android reference implementation](../../implementations/android-sdk/README.md) demonstrates the
+same SDK behavior in Compose and XML Views shells. It exercises entry resolution, interaction
+tracking, screen tracking, live updates, preview-panel overrides, and shared mock API flows.
+
+## Configuration
+
+### Common options
+
+| Option | Required? | Default | Description |
+| ------------------- | --------- | -------- | --------------------------------------------------------------------------------------------------- |
+| `clientId` | Yes | None | Optimization client identifier used for Experience API and Insights API calls. |
+| `environment` | No | `master` | Contentful environment name used by the Optimization APIs. |
+| `contentfulLocales` | No | `null` | Contentful locale configuration used to resolve `client.locale` for app-owned CDA requests. |
+| `locale` | No | Runtime | Initial app/content locale candidate. When omitted, the SDK can resolve from `LocaleList`. |
+| `api.locale` | No | `null` | Explicit Experience API locale override for localized profile fields. |
+| `defaults` | No | `null` | Initial persisted-state seeds such as consent or profile values, applied only when no value exists. |
+| `debug` | No | `false` | Enables SDK diagnostic logging. |
+
+`OptimizationRoot` and `OptimizationManager.initialize(...)` also accept global `trackViews`,
+`trackTaps`, and `liveUpdates` defaults. `OptimizedEntry` and `OptimizedEntryView` can override
+those defaults per entry.
+
+### Locale handling
+
+For a single-locale app, configure the Contentful locale default only:
+
+```kotlin
+val config = OptimizationConfig(
+ clientId = "your-client-id",
+ environment = "master",
+ contentfulLocales = ContentfulLocales(default = "en-US"),
+)
+```
+
+For an app that matches the user's runtime locale to multiple Contentful locales, add `supported`
+with the locale codes configured in your Contentful space:
+
+```kotlin
+val config = OptimizationConfig(
+ clientId = "your-client-id",
+ environment = "master",
+ contentfulLocales = ContentfulLocales(
+ default = "en-US",
+ supported = listOf("en-US", "de-DE", "fr-FR"),
+ ),
+)
+```
+
+Use `client.locale` when your app-owned Contentful Delivery API client fetches entries that will be
+passed to `OptimizedEntry`, `OptimizedEntryView`, or `client.personalizeEntry(...)`. The native SDK
+does not fetch Contentful entries for your app layer, so this value belongs in your CDA request
+code.
+
+`OptimizationApiConfig.locale` is an explicit Experience API override for localized profile fields.
+It does not replace the CDA locale used to fetch Contentful entries.
+
+For the full locale model, see
+[Locale handling in the Optimization SDK Suite](../../documentation/concepts/locale-handling-in-the-optimization-sdk-suite.md).
+For the single-locale CDA entry contract, see
+[Entry personalization and variant resolution](../../documentation/concepts/entry-personalization-and-variant-resolution.md#single-locale-cda-entry-contract).
+
+## Runtime notes
+
+- The SDK library manifest declares the required network permissions and the non-exported preview
+ panel Activity.
+- No JavaScript bridge setup is required in consuming applications. The generated QuickJS bundle is
+ packaged inside the AAR.
+- SDK state is persisted with `SharedPreferences`.
+- Analytics events queue while the device is offline and flush when connectivity returns or the app
+ moves toward the background.
+- For bridge architecture and maintainer build details, see
+ [Native bridge architecture](../universal/optimization-js-bridge/BRIDGE_ARCHITECTURE.md).
+
+## Related
+
+- [Compose integration guide](../../documentation/guides/integrating-the-optimization-android-sdk-in-a-compose-app.md) -
+ step-by-step setup for `OptimizationRoot`, `OptimizedEntry`, screen tracking, and preview panel
+ mounting.
+- [XML Views integration guide](../../documentation/guides/integrating-the-optimization-android-sdk-in-a-views-app.md) -
+ step-by-step setup for `OptimizationManager`, `OptimizedEntryView`, screen tracking, and preview
+ panel mounting.
+- [Android SDK runtime and interaction mechanics](../../documentation/concepts/android-sdk-runtime-and-interaction-mechanics.md) -
+ explains runtime state, consent, tracking, live updates, preview overrides, and offline delivery.
+- [Android reference implementation](../../implementations/android-sdk/README.md) - Compose and XML
+ Views apps that validate the SDK against the shared mock API.
+- [React Native SDK](../react-native-sdk/README.md) - Mobile integration for React Native
+ applications.
+- [Core SDK](../universal/core-sdk/README.md) - Shared optimization foundation used through the
+ native bridge.
diff --git a/packages/ios/CODE_MAP.md b/packages/ios/CODE_MAP.md
index 644e5d3c..d87ae88b 100644
--- a/packages/ios/CODE_MAP.md
+++ b/packages/ios/CODE_MAP.md
@@ -116,6 +116,42 @@ sequenceDiagram
JSM-->>Swift: Result
```
+### JavaScriptCore bridge runtime notes
+
+The shared bridge contract is documented in
+[`packages/universal/optimization-js-bridge/BRIDGE_ARCHITECTURE.md`](../universal/optimization-js-bridge/BRIDGE_ARCHITECTURE.md).
+The iOS package owns the JavaScriptCore-specific host side of that contract:
+
+- `JSContextManager` creates one `JSContext` for the lifetime of an `OptimizationClient`, registers
+ native bindings, evaluates the UMD bundle, validates `globalThis.__bridge`, registers push-back
+ globals, and calls `__bridge.initialize(configJSON)`.
+- `NativePolyfills` registers Swift-backed `__nativeLog`, `__nativeSetTimeout`,
+ `__nativeClearTimeout`, `__nativeRandomUUID`, and `__nativeFetch` globals before the UMD bundle
+ evaluates.
+- `BridgeCallbackManager` generates the success/error callback-name pairs that async bridge methods
+ use when JavaScript promises settle.
+- `OptimizationClient` is `@MainActor`, so public bridge calls enter JavaScriptCore from the main
+ actor. Fetch and timer completions marshal back to the main queue before re-entering JS.
+- The push-back globals (`__nativeOnStateChange`, `__nativeOnEventEmitted`, and
+ `__nativeOnOverridesChanged`) republish bridge state into Swift observables after decoding JSON.
+
+### Bundle resource and diagnostics notes
+
+`Package.swift` declares `optimization-ios-bridge.umd.js` as a copied Swift Package resource and
+links JavaScriptCore for consuming apps. `JSContextManager.loadBundleSource()` reads that resource
+from `Bundle.module` and throws `OptimizationError.resourceLoadError` if the bundle is missing.
+
+The copied UMD bundle is generated from `packages/universal/optimization-js-bridge`. Keep the flow
+one-way: edit the TypeScript bridge or polyfill source, build the bridge package, and let the bridge
+build refresh the Swift Package resource. Do not hand-edit the copied UMD resource.
+
+Diagnostics flow through two channels:
+
+- JavaScriptCore exceptions route through the context exception handler and the SDK diagnostic
+ logger.
+- Native fetch crossings are bracketed with signposts under the `com.contentful.optimization`
+ performance log so Instruments can measure bridge round-trip cost without SDK-code timing hooks.
+
### Data flow: view tracking lifecycle
```mermaid
diff --git a/packages/ios/README.md b/packages/ios/README.md
index 5b7327ae..7dcb8a55 100644
--- a/packages/ios/README.md
+++ b/packages/ios/README.md
@@ -90,6 +90,9 @@ locally before `swift build`/`swift test` with `pnpm run ios:bridge`, or use the
## Related
+- [iOS SDK code map](./CODE_MAP.md) - Maintainer architecture map for the native iOS package
+- [Native bridge architecture](../universal/optimization-js-bridge/BRIDGE_ARCHITECTURE.md) - Shared
+ bridge runtime and build notes
- [iOS reference app](../../implementations/ios-sdk/README.md) - Native app and XCUITest surface for
bridge and preview-panel validation
- [React Native SDK](../react-native-sdk/README.md) - Current stable mobile-facing JavaScript SDK
diff --git a/packages/universal/optimization-js-bridge/BRIDGE_ARCHITECTURE.md b/packages/universal/optimization-js-bridge/BRIDGE_ARCHITECTURE.md
new file mode 100644
index 00000000..98ad7faf
--- /dev/null
+++ b/packages/universal/optimization-js-bridge/BRIDGE_ARCHITECTURE.md
@@ -0,0 +1,115 @@
+# Native bridge architecture
+
+This document is for maintainers working on the internal `@contentful/optimization-js-bridge`
+package and the native iOS and Android SDKs that consume it. It is not an application integration
+guide.
+
+The bridge lets the native SDKs share one TypeScript optimization core while Swift and Kotlin own
+native runtime concerns such as persistence, networking, lifecycle handling, UI adapters, and
+preview-panel presentation.
+
+## Ownership
+
+The bridge package owns one TypeScript adapter at `src/index.ts`. That adapter wraps
+`@contentful/optimization-core`, exposes a small `globalThis.__bridge` object, and keeps the shared
+optimization state machine available to JavaScriptCore on iOS and QuickJS on Android.
+
+Native packages own the engine-specific context managers, host bindings, model decoding, and public
+Swift/Kotlin APIs:
+
+| Layer | Owns |
+| ------------------------------------------- | ---------------------------------------------------------------------------------- |
+| `packages/universal/optimization-js-bridge` | Bridge methods, callback payloads, preview override calls, polyfills, UMD outputs. |
+| `packages/ios` | JavaScriptCore context lifecycle, Swift models, SwiftUI adapters, persistence. |
+| `packages/android` | QuickJS lifecycle, Kotlin models, Compose and Views adapters, persistence. |
+
+## Build output
+
+`rslib.config.ts` builds the same bridge source into two UMD bundles:
+
+| Bundle | Native consumer | Engine |
+| ------------------------------------ | ---------------------- | ---------------------------- |
+| `optimization-ios-bridge.umd.js` | iOS Swift Package | JavaScriptCore (`JSContext`) |
+| `optimization-android-bridge.umd.js` | Android library assets | QuickJS (`quickjs-kt`) |
+
+The bundles differ only in the package name stamped into analytics `library.name`. Keep that
+platform-specific define intact so iOS and Android events remain distinguishable.
+
+The package `postbuild` step copies the emitted UMD bundles into the native SDK resource locations.
+Do not hand-edit `dist/` output or copied native bundles. Update bridge source or polyfills, then
+run:
+
+```sh
+pnpm --filter @contentful/optimization-js-bridge build
+```
+
+## Polyfills and native bindings
+
+The bridge bundle expects browser-like globals that JavaScriptCore and QuickJS do not provide:
+`console`, `setTimeout`, `fetch`, `crypto.randomUUID`, `URL`, `URLSearchParams`, `AbortController`,
+`queueMicrotask`, `Promise.withResolvers`, `TextEncoder`, and `TextDecoder`.
+
+The JS polyfills live in `src/polyfills/` and are prepended to each UMD bundle before the bridge
+IIFE. The native SDK must register the host-side `__native*` bindings before evaluating the bundle.
+
+| Binding | iOS implementation | Android implementation |
+| ---------------------- | ----------------------------- | ------------------------------ |
+| `__nativeLog` | Routes to diagnostics. | Routes through `__native.log`. |
+| `__nativeSetTimeout` | Schedules on the main queue. | Schedules a coroutine delay. |
+| `__nativeClearTimeout` | Cancels the stored work item. | Cancels the stored job. |
+| `__nativeRandomUUID` | Uses `UUID()`. | Uses `UUID.randomUUID()`. |
+| `__nativeFetch` | Uses `URLSession`. | Uses `OkHttp`. |
+
+Polyfills must stay platform-agnostic. If a future bridge feature needs platform-specific behavior,
+prefer a build-time define in the bridge entry over forking polyfill files.
+
+## Bridge method contract
+
+Native code calls only through `globalThis.__bridge`. The bridge exposes methods such as
+`initialize`, `identify`, `screen`, `page`, `trackView`, `trackClick`, `flush`, `consent`, `reset`,
+`personalizeEntry`, `getProfile`, `getState`, `getPreviewState`, and preview override mutators.
+
+Async methods receive a payload plus success and error callback names. The JS bridge invokes one of
+those global callbacks when the underlying promise settles. This keeps the JS side identical across
+JavaScriptCore and QuickJS even though the native callback transport differs by platform.
+
+Synchronous methods, including `personalizeEntry`, `consent`, `reset`, `setOnline`,
+`getMergeTagValue`, `getProfile`, and preview override mutators, return JSON-compatible values
+directly from engine evaluation.
+
+## State push-back and lifecycle
+
+During initialization, the native context manager installs three push-back globals:
+
+- `__nativeOnStateChange(json)` for profile, consent, locale, personalization, and optimization
+ state snapshots.
+- `__nativeOnEventEmitted(json)` for emitted Experience and Insights events.
+- `__nativeOnOverridesChanged(json)` for preview-panel audience and variant overrides.
+
+These callbacks fire synchronously inside bridge calls. Native handlers may republish on the main
+thread, but they should not defer the underlying state mutation past the method return when the
+public API expects an immediate state snapshot.
+
+Destroy paths should cancel timers, remove subscriptions and preview override state, evaluate
+`__bridge.destroy()`, and close or release the JavaScript engine.
+
+## Platform notes
+
+- iOS bridge details and the JavaScriptCore package-resource flow live in
+ [packages/ios/CODE_MAP.md](../../ios/CODE_MAP.md).
+- Android bridge details, QuickJS dispatcher constraints, and asset packaging notes live in
+ [packages/android/README.md](../../android/README.md).
+- Reference app bootstrap and local validation flows live in the iOS and Android reference app
+ READMEs under `implementations/`.
+
+## Validation
+
+For bridge contract, payload-shape, preview, or lifecycle changes:
+
+```sh
+pnpm --filter @contentful/optimization-js-bridge typecheck
+pnpm --filter @contentful/optimization-js-bridge build
+```
+
+Then validate the affected native SDK and reference-app flow. Rebuild the bridge before Swift,
+Kotlin, XCUITest, or Maestro results are treated as meaningful.
diff --git a/packages/universal/optimization-js-bridge/README.md b/packages/universal/optimization-js-bridge/README.md
index d23c7f46..50209781 100644
--- a/packages/universal/optimization-js-bridge/README.md
+++ b/packages/universal/optimization-js-bridge/README.md
@@ -43,6 +43,11 @@ pnpm --filter @contentful/optimization-js-bridge build
Do not hand-edit `dist/` output or the copied native bundles. Regenerate them through the build
flow.
+## Architecture notes
+
+For the bridge runtime contract, UMD bundle flow, prepended polyfills, native bindings, callback
+shape, and lifecycle constraints, see [Native bridge architecture](./BRIDGE_ARCHITECTURE.md).
+
## Commands
Run commands from the monorepo root:
@@ -57,6 +62,7 @@ Android SDKs and the reference apps that exercise the changed behavior.
## Related
+- [Native bridge architecture](./BRIDGE_ARCHITECTURE.md) - Shared bridge runtime and build notes
- [iOS SDK package](../../ios/README.md) - Native iOS SDK status and package layout
- [Android SDK package](../../android/README.md) - Native Android SDK status and package layout
- [Core preview support](../core-sdk/src/preview-support/README.md) - Shared preview override