Conversation
Bump flutter.version to c9a6c484 (Flutter 3.44.2) and engine.version to v1.0.0-flutter3.44.2. Engine artifacts rebuilt against Dart d684a576. A Flutter-version-only bump keeps simulator/debug working but breaks AOT with 'gen_snapshot: Invalid SDK hash' (old gen_snapshot cannot load kernel compiled by the new Dart SDK), so the engine rebuild is required for device/release builds.
Platform.operatingSystem / isIOS / isTvOS carry @pragma("vm:platform-const"), which gen_snapshot const-folds in AOT from the build's --target-os. Stock Flutter passes --target-os ios for TargetPlatform.ios (which tvOS rides), so release baked in operatingSystem == "ios" and isTvOS == false for all app and plugin code — correct in JIT/debug, dead on device in release. Keep debug and release on the same runtime-resolution path: - TvosKernelSnapshot.build() mirrors upstream KernelSnapshot.build() but passes targetOS: null, so the platform-const getters are not folded and resolve at runtime to "tvos" (the engine's patched kHostOperatingSystemName). - TvosArtifacts overrides flutterPatchedSdkPath / platformKernelDill for AOT only to the patched flutter_patched_sdk in the host engine artifact, so the un-folded isIOS initializer is operatingSystem == "ios" || == "tvos" and the isTvOS getter exists. Debug keeps the stock SDK (runtime resolution via the device engine makes the compile SDK irrelevant there). Verified on a physical Apple TV in release: operatingSystem == "tvos", isIOS == true, isTvOS == true, defaultTargetPlatform == iOS. Plugin isolation is unaffected — discovery is keyed on the pubspec tvos: key, not Platform.isIOS. Adds unit tests asserting the AOT frontend_server command omits --target-os and that the artifact override applies for AOT but falls through to stock for debug. The example info screen now surfaces the platform-identity values.
At app startup the native FlutterTvRemotePlugin may not have registered its button-channel handler yet when init() pushes the initial config. The one-shot invocation then throws MissingPluginException and the handshake is lost — native never sets frameworkReadyForTouches and the touchpad appears dead even though everything compiled. Retry on a short interval until the call is acknowledged or a newer config supersedes it. Guarded by a generation counter so a superseded or reset push bails out, and never runs on iOS/Android (init() is isTvos-gated).
1.3.1 was never tagged, and the platform-identity fix landed on the same branch on top of the engine bump, so the release now carries a real CLI behaviour change. Drop the "no CLI behaviour change" note, add a Fixed section for the AOT Platform.operatingSystem/isIOS/isTvOS correction, note the bundled flutter_tvos 1.1.1, and move the date to the actual ship date.
Profile builds crashed at startup with `Type '_NetworkProfiling' not found in library 'dart.io'`. The patched-SDK override pointed both profile and release at the product host_release SDK, but the non-product profile engine looks up entry-point classes (e.g. dart:io _NetworkProfiling) that the product SDK doesn't retain through AOT tree-shaking. Map profile to the non-product host_debug_unopt SDK (which also carries the platform-identity patch), matching stock Flutter's flutter_patched_sdk (profile) vs flutter_patched_sdk_product (release) split. Release is unchanged. Verified on a physical Apple TV.
The device-debug engine now maps JIT pages RWX up front so on-device --debug + hot reload work again on tvOS 26 (which denies the RW<->RX mprotect flip). Ships in the v1.0.0-flutter3.44.2 engine artifacts; debug-only, AOT keeps W^X.
Guards that debug+simulator → tvos_debug_sim_arm64, debug+device → tvos_debug_arm64, profile → tvos_profile_arm64, release → tvos_release_arm64. A wrong mapping here is how a run mode silently picks the wrong engine.
Review — multi-agent pass + adversarial verificationReviewed the AOT/kernel-snapshot logic, the artifact resolution, and the RCU change, then ran a second adversarial pass that re-verified every finding against the code at The central mechanism checks out end-to-end (traced through the SDK + patched engine, not just the description): One nit on the description: it says "gen_snapshot const-folds" — the fold is actually in frontend_server / the kernel transform, not gen_snapshot. The in-code comment is accurate; only the PR prose is loose. Worth fixing (non-blocking)I1 — RCU I2 — the new RCU retry test is wall-clock-timed, not I3 — no automated end-to-end identity assertion Optional hardening (not introduced by this PR)H1 — H2 — no coherence guard between Minor
Nice fix overall — the 🤖 Generated with Claude Code |
… review I1/I2) Addresses review feedback on the configure-handshake retry: - I1: _pushConfigWithRetry now only swallows the expected MissingPluginException / PlatformException. Any other error (e.g. a _config.toMap() serialization bug) propagates instead of being retried 50x and dropped indistinguishably from the startup race. On budget exhaustion it reports via FlutterError.reportError instead of giving up silently, consistent with the rest of the class. - I2: rewrote the retry test with fakeAsync + virtual-time ticks so it no longer leans on a real-wall-clock window a starved CI runner could blow. The unregistered-native state is modelled by a mock handler that throws MissingPluginException (a null handler would fall through to the real platform dispatcher, which fakeAsync can't pump). Added a test that the retry budget exhausting reports an error.
* chore(release): upgrade engine to Flutter 3.44.2 (1.3.1) Bump flutter.version to c9a6c484 (Flutter 3.44.2) and engine.version to v1.0.0-flutter3.44.2. Engine artifacts rebuilt against Dart d684a576. A Flutter-version-only bump keeps simulator/debug working but breaks AOT with 'gen_snapshot: Invalid SDK hash' (old gen_snapshot cannot load kernel compiled by the new Dart SDK), so the engine rebuild is required for device/release builds. * fix(platform): make tvOS platform identity correct in AOT/release builds Platform.operatingSystem / isIOS / isTvOS carry @pragma("vm:platform-const"), which gen_snapshot const-folds in AOT from the build's --target-os. Stock Flutter passes --target-os ios for TargetPlatform.ios (which tvOS rides), so release baked in operatingSystem == "ios" and isTvOS == false for all app and plugin code — correct in JIT/debug, dead on device in release. Keep debug and release on the same runtime-resolution path: - TvosKernelSnapshot.build() mirrors upstream KernelSnapshot.build() but passes targetOS: null, so the platform-const getters are not folded and resolve at runtime to "tvos" (the engine's patched kHostOperatingSystemName). - TvosArtifacts overrides flutterPatchedSdkPath / platformKernelDill for AOT only to the patched flutter_patched_sdk in the host engine artifact, so the un-folded isIOS initializer is operatingSystem == "ios" || == "tvos" and the isTvOS getter exists. Debug keeps the stock SDK (runtime resolution via the device engine makes the compile SDK irrelevant there). Verified on a physical Apple TV in release: operatingSystem == "tvos", isIOS == true, isTvOS == true, defaultTargetPlatform == iOS. Plugin isolation is unaffected — discovery is keyed on the pubspec tvos: key, not Platform.isIOS. Adds unit tests asserting the AOT frontend_server command omits --target-os and that the artifact override applies for AOT but falls through to stock for debug. The example info screen now surfaces the platform-identity values. * fix(rcu): retry the configure handshake until the native plugin is ready At app startup the native FlutterTvRemotePlugin may not have registered its button-channel handler yet when init() pushes the initial config. The one-shot invocation then throws MissingPluginException and the handshake is lost — native never sets frameworkReadyForTouches and the touchpad appears dead even though everything compiled. Retry on a short interval until the call is acknowledged or a newer config supersedes it. Guarded by a generation counter so a superseded or reset push bails out, and never runs on iOS/Android (init() is isTvos-gated). * docs(changelog): document the AOT platform-identity fix in 1.3.1 1.3.1 was never tagged, and the platform-identity fix landed on the same branch on top of the engine bump, so the release now carries a real CLI behaviour change. Drop the "no CLI behaviour change" note, add a Fixed section for the AOT Platform.operatingSystem/isIOS/isTvOS correction, note the bundled flutter_tvos 1.1.1, and move the date to the actual ship date. * fix(platform): compile profile AOT against the non-product host SDK Profile builds crashed at startup with `Type '_NetworkProfiling' not found in library 'dart.io'`. The patched-SDK override pointed both profile and release at the product host_release SDK, but the non-product profile engine looks up entry-point classes (e.g. dart:io _NetworkProfiling) that the product SDK doesn't retain through AOT tree-shaking. Map profile to the non-product host_debug_unopt SDK (which also carries the platform-identity patch), matching stock Flutter's flutter_patched_sdk (profile) vs flutter_patched_sdk_product (release) split. Release is unchanged. Verified on a physical Apple TV. * docs(changelog): note on-device debug (JIT/hot reload) fix for tvOS 26 The device-debug engine now maps JIT pages RWX up front so on-device --debug + hot reload work again on tvOS 26 (which denies the RW<->RX mprotect flip). Ships in the v1.0.0-flutter3.44.2 engine artifacts; debug-only, AOT keeps W^X. * test(artifacts): assert engine variant selection per build mode Guards that debug+simulator → tvos_debug_sim_arm64, debug+device → tvos_debug_arm64, profile → tvos_profile_arm64, release → tvos_release_arm64. A wrong mapping here is how a run mode silently picks the wrong engine. * rcu: report configure-handshake exhaustion + narrow retry catch (PR #24 review I1/I2) Addresses review feedback on the configure-handshake retry: - I1: _pushConfigWithRetry now only swallows the expected MissingPluginException / PlatformException. Any other error (e.g. a _config.toMap() serialization bug) propagates instead of being retried 50x and dropped indistinguishably from the startup race. On budget exhaustion it reports via FlutterError.reportError instead of giving up silently, consistent with the rest of the class. - I2: rewrote the retry test with fakeAsync + virtual-time ticks so it no longer leans on a real-wall-clock window a starved CI runner could blow. The unregistered-native state is modelled by a mock handler that throws MissingPluginException (a null handler would fall through to the real platform dispatcher, which fakeAsync can't pump). Added a test that the retry budget exhausting reports an error. * fix(precache): fetch only tvOS + universal artifacts Stock `flutter precache` with no platform flags downloads every enabled platform's artifacts (Android, iOS-engine, web, macOS). A tvOS embedder needs none of those. The command now drives the cache itself instead of delegating to `super.runCommand()`: it fetches the tvOS engine set plus the universal artifacts (fonts, patched SDK, host USB-deploy tools) and nothing else, while still honouring the stock per-platform flags (`--ios`, `--android`, `--all-platforms`, …) when passed explicitly. Also render the tvOS engine zips under a "tvOS Engine" header in the same nested tree style (` ├─ [1/6] …`) as the bundled Flutter SDK artifact. * fix(create): exit with usage when no output directory given `flutter-tvos create` with no output directory crashed with `Bad state: No element` because runCommand() read `rest.first` before validating. Call `validateOutputDirectoryArg()` up front so it prints the friendly usage message and exits with code 2, matching stock `flutter create`. * docs(changelog): note precache scope + create usage fixes * chore: upgrade to Flutter 3.44.3 (1.3.2) Refresh the pinned engine to Flutter 3.44.3 (e1fd963c, Dart d684a576). - bin/internal/flutter.version -> e1fd963c (Flutter 3.44.3) - bin/internal/engine.version -> v1.0.0-flutter3.44.3 - pubspec version -> 1.3.2 - Rebuilt all six engine artifact variants from the 3.44.3 source tree; all 17 tvOS patches apply cleanly and the artifact verification gate passes. Dart is unchanged, so AOT loads kernel without an SDK-hash mismatch. - Picks up the Flutter 3.44.3 flutter_tools fixes shipped in the SDK. Verified: dart analyze lib/ (0 errors), 262 tests pass, simulator-debug build succeeds, release AOT produces a valid App.framework. * docs: refresh READMEs for 3.44.3 and flutter_tvos 1.1.1 - Root README "Current version" table -> flutter-tvos 1.3.2, Flutter SDK 3.44.3 (e1fd963c), engine artifacts v1.0.0-flutter3.44.3. - flutter_tvos package README: install snippet -> ^1.1.1 and add Siri Remote support to the feature list (it shows on pub.dev). * release(flutter_tvos): 1.1.2 (docs refresh) Docs-only release so the pub.dev page picks up the README changes (Siri Remote in the feature list, updated install snippet). No functional or API changes. - packages/flutter_tvos/pubspec.yaml -> 1.1.2 - packages/flutter_tvos CHANGELOG + README (install snippet ^1.1.2) - example pubspec.lock -> 1.1.2 - CLI CHANGELOG: note bundled plugin 1.1.2 Not published — publish to pub.dev after this merges to main. * chore: gitignore per-user SwiftPM/Xcode state under plugin tvos/ Opening a plugin's tvos/Package.swift in Xcode generates .swiftpm/xcode/xcuserdata/ (per-user scheme state, e.g. xcschememanagement.plist) which is not source and must not be committed. - Add packages/flutter_tvos/tvos/.gitignore (.swiftpm/, xcuserdata/) — the bundled plugin's tvos/ had no .gitignore, so this junk was unignored. - Add the same rules to the porter's generated .gitignore (lib/plugin_porting/templates.dart), since the porter now emits a tvos/Package.swift and every ported plugin would otherwise hit this. * refactor(precache): testable artifact selection + harden flag parsing; add tests Addresses self-review findings on the precache scoping logic: - Extract the non-tvOS artifact selection into a pure, @VisibleForTesting `selectRequiredArtifacts(...)` and cover it with unit tests (test/general/tvos_precache_test.dart): default = universal+informative only, explicit platform flags add their artifacts, --all-platforms, feature-gating, and the android umbrella/child flags. - Guard `argResults.wasParsed(name)` with `argParser.options.containsKey` so a future Flutter DevelopmentArtifact without a matching precache flag cannot crash the command. - Honor an individual `--android_gen_snapshot/_maven/_internal_build` flag on its own, not only via the `--android` umbrella. - Drop the misleading `cache.isUpToDate()` fast-path: it checks every registered artifact (including platforms we intentionally skip), so it was effectively always false. `updateAll` is already idempotent per-artifact. - Add a test for `flutter-tvos create` with no output directory exiting with usage (code 2) instead of crashing (test/general/tvos_create_test.dart), plus a test/src re-export shim for createTestCommandRunner. * test: drop the create no-args test (transitively invokes Pub in CI) Running TvosCreateCommand through createTestCommandRunner pulls the full FlutterCommand pipeline, which transitively touches `Pub` — the flutter_tools test harness rejects that with "Attempted to invoke pub during test" (passes locally, fails in CI). The create no-output-dir fix is a single stock `validateOutputDirectoryArg()` call and is unchanged; the substantive coverage (precache artifact selection) is fully tested in tvos_precache_test.dart.
Summary
Ships flutter-tvos 1.3.1: pins the engine to Flutter 3.44.2 and fixes tvOS platform identity in AOT (release/profile) builds. Bundles
flutter_tvosplugin 1.1.1.The platform-identity bug
Platform.operatingSystem/isIOS/isTvOScarry@pragma("vm:platform-const"), whichgen_snapshotconst-folds in AOT from the build's--target-os. Stock Flutter passes--target-os iosforTargetPlatform.ios(which tvOS rides), so release builds baked inoperatingSystem == "ios"andisTvOS == falsefor all app and plugin code — debug/JIT was unaffected (no fold → reads the patched native VM). Symptom: tvOS-gated features (e.g. the Siri-Remote swipe handler) worked in debug but were dead in release.The fix (CLI only, no engine rebuild)
Keeps debug and release on the same runtime-resolution path:
TvosKernelSnapshot.build()mirrors upstreamKernelSnapshot.build()but passestargetOS: null, so the platform-const getters are not folded and resolve at runtime to"tvos"(the engine's patchedkHostOperatingSystemName).TvosArtifactsoverridesflutterPatchedSdkPath/platformKernelDillfor AOT only to the patchedflutter_patched_sdkin the host engine artifact, so the un-foldedisIOSinitializer isoperatingSystem == "ios" || == "tvos"and theisTvOSgetter exists. Debug keeps the stock SDK.Result in both debug and release:
operatingSystem == "tvos",isIOS == true,isTvOS == true,defaultTargetPlatform == iOS. Plugin isolation is unaffected — discovery is keyed on the pubspectvos:key, notPlatform.isIOS.Also in this release
c9a6c484, Dartd684a576); all six artifact variants rebuilt (required — a version-only bump breaks AOT withgen_snapshot: Invalid SDK hash).flutter_tvos1.1.1 — the remote-controlconfigurehandshake now retries until the native plugin registers, so the touchpad reliably starts forwarding events instead of intermittently appearing dead.Tests
tvos_kernel_snapshot_test.dartasserts the AOT frontend_server command omits--target-os;tvos_artifacts_test.dartcovers the AOT patched-SDK override + debug fall-through.dart analyzeclean.tvos/true/true, swipe works.