{"owner":"fzyzcjy","repo":"flutter_rust_bridge","hasSkills":true,"totalSkillsCount":50,"totalTokensCount":51035,"categories":["anthropic-skill","root-instruction","claude-rule","plugin-manifest"],"hasMcp":false,"mcpConfig":null,"found":[".claude/skills/frb-add-contributor/SKILL.md",".claude/skills/frb-android-emulator-prepare/SKILL.md",".claude/skills/frb-cargokit-dev/SKILL.md",".claude/skills/frb-cargokit/SKILL.md",".claude/skills/frb-categorize-code-diff/SKILL.md",".claude/skills/frb-ci-filter/SKILL.md",".claude/skills/frb-code-generation/SKILL.md",".claude/skills/frb-debugging/SKILL.md",".claude/skills/frb-dev-env/SKILL.md",".claude/skills/frb-develop-feature/SKILL.md",".claude/skills/frb-develop-feature/tests/test-academic.md",".claude/skills/frb-develop-feature/tests/test-pressure-1.md",".claude/skills/frb-develop-feature/tests/test-pressure-2.md",".claude/skills/frb-docker/SKILL.md",".claude/skills/frb-feature-flag/SKILL.md",".claude/skills/frb-fix-ci/SKILL.md",".claude/skills/frb-fix-codecov/SKILL.md",".claude/skills/frb-fix-main-ci/SKILL.md",".claude/skills/frb-fix-merge-conflict/SKILL.md",".claude/skills/frb-issue-to-green-pr/SKILL.md",".claude/skills/frb-lint/SKILL.md",".claude/skills/frb-manual-test/SKILL.md",".claude/skills/frb-manual-test/execution-template.md",".claude/skills/frb-manual-test/test-template.md",".claude/skills/frb-pr-chain-split/SKILL.md",".claude/skills/frb-pr-review/SKILL.md",".claude/skills/frb-prepare-pr/SKILL.md",".claude/skills/frb-publish-release/SKILL.md",".claude/skills/frb-tart-prepare/SKILL.md",".claude/skills/frb-test/SKILL.md",".claude/skills/frb-test/tests/test-application-1.md",".claude/skills/frb-test/tests/test-application-2.md",".claude/skills/frb-test/tests/test-gap-1.md",".claude/skills/frb-test/tests/test-variation-1.md",".claude/skills/frb-test/tests/test-variation-2.md",".claude/skills/frb-upgrade-docker/SKILL.md",".claude/skills/frb-upgrade-flutter/SKILL.md",".claude/skills/frb-vmware-windows-prepare/SKILL.md",".claude/skills/frb-write-changelog/SKILL.md","AGENTS.md","CLAUDE.md","tools/frb_internal/assets/apple_scaffold/frb_example/flutter_package/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md","tools/frb_internal/assets/apple_scaffold/frb_example/flutter_package_native_assets/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md","tools/frb_internal/assets/apple_scaffold/frb_example/flutter_via_create/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md","tools/frb_internal/assets/apple_scaffold/frb_example/flutter_via_create_native_assets/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md","tools/frb_internal/assets/apple_scaffold/frb_example/flutter_via_integrate/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md","tools/frb_internal/assets/apple_scaffold/frb_example/flutter_via_integrate_native_assets/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md","tools/frb_internal/test/README.md","tools/tart_macos/README.md","tools/vmware_windows/README.md"],"skills":{".claude/skills/frb-add-contributor/SKILL.md":"---\nname: frb-add-contributor\ndescription: Add or reconcile flutter_rust_bridge contributors through all-contributors PRs. Use when finding contributor PRs, posting all-contributors comments, validating contributor metadata, resolving contributor PR merge conflicts, or merging the generated contributor PRs.\n---\n\n# FRB Add Contributor\n\nUse this skill when contributors need to be added to `flutter_rust_bridge` via `all-contributors`, especially when multiple recent PR authors should be credited in batch.\n\n## Step 1: Find the correct source PRs\n\nFor each GitHub username, find their PRs in `fzyzcjy/flutter_rust_bridge`.\n\n```bash\ngh search prs --repo fzyzcjy/flutter_rust_bridge --author <username> --limit 100 --json number,title,author,url,state\n```\n\nImportant:\n\n- Deduplicate by contributor. A single person may have multiple merged PRs.\n- If the task is about `new contributor PR`s, use the contributor's first PR only.\n- For release reconciliation, inspect every third-party human-authored PR in the same release range used by `frb-write-changelog`.\n- For each such PR, confirm that the contributor's human-written `.all-contributors-custom.yaml` summary covers that contribution. Being present in `.all-contributorsrc` or `README.md` is not sufficient.\n- Treat an existing contributor whose summary does not cover a release-range PR as needing reconciliation, but do not expect a new all-contributors PR for that person.\n- If the contributor already exists in `.all-contributorsrc` or `README.md`, do not expect a new all-contributors PR to be created.\n\n## Step 2: Stop for human confirmation\n\nAfter determining which contributors may need to be added or have their custom descriptions extended, stop and ask a human to confirm the contributor list and source PRs before taking any mutating action.\n\nThis stop is mandatory.\n\n- Do not edit `.all-contributors-custom.yaml`.\n- Do not regenerate contributor artifacts.\n- Do not post all-contributors comments.\n- Do not create or merge any PRs.\n- Do not perform any other GitHub mutation.\n\nOnly continue after the human explicitly confirms which contributors to add or update and which source PRs those changes cover.\n\n## Step 3: Prepare `.all-contributors-custom.yaml` and stop for human input\n\nBefore regenerating contributor artifacts, append each new contributor to the end of `.all-contributors-custom.yaml`:\n\n```yaml\n- <username>: TODO\n```\n\nDo this only for contributors who are not already present in `.all-contributors-custom.yaml`.\n\nFor an existing credited contributor with no custom entry, append:\n\n```yaml\n- <username>: TODO(#<pr-number>)\n```\n\nFor an existing contributor whose summary does not cover a release-range PR, preserve the existing human-written text and append a marker for each uncovered PR:\n\n```yaml\n- <username>: <existing human-written summary> TODO(#<pr-number>)\n```\n\nThen stop and ask a human to replace each `TODO` or `TODO(#<pr-number>)` with a concise contribution summary.\n\nThis stop is mandatory.\n\n- Do not continue to Step 3 or any later step in this skill until the human has replaced every `TODO`.\n- Do not run contributor generation.\n- Do not post all-contributors comments.\n- Do not resolve or merge contributor PRs.\n\nOnly resume after the human-written descriptions are present in `.all-contributors-custom.yaml`.\n\n## Step 4: Trigger all-contributors\n\nPost the comment on the chosen source PR:\n\n```bash\ngh pr comment <pr-number> --repo fzyzcjy/flutter_rust_bridge --body '@all-contributors please add <username> for code'\n```\n\nThen inspect the bot response on that PR.\n\n- Skip this step for an existing contributor whose only gap was an incomplete custom description.\n- If the bot says the user already contributed before, stop there for that user.\n- Otherwise, wait for the bot to open a PR named `docs: add <username> as a contributor for code`.\n\n## Step 5: Validate contributor metadata\n\nWhen a contributor PR exists, check the generated contributor data before merging.\n\nFiles to inspect:\n\n- `.all-contributorsrc`\n- `README.md`\n\nRequired validation:\n\n- Homepage URLs must use `https://`, never `http://`.\n- If the generated homepage is `http://`, change it to `https://` if the site supports it.\n- If the personal site does not support `https://`, prefer the contributor's GitHub profile URL instead of keeping `http://`.\n\n## Step 6: Regenerate contributor output\n\nAfter the human has replaced all `TODO` markers, confirm that the custom summaries cover every third-party release-range PR. Then regenerate the contributor artifacts instead of hand-editing `README.md`.\n\nUse:\n\n```bash\n./frb_internal generate-internal-contributor\n```\n\nThis command updates:\n\n- `.all-contributorsrc`\n- `README.md` via `all-contributors generate`\n- `website/docs/index.md` via `generate-internal-readme`\n\nIf you only need to refresh the website copy of the README, use:\n\n```bash\n./frb_internal generate-internal-readme\n```\n\n## Step 7: Resolve merge conflicts in contributor PRs\n\nThese PRs usually conflict only in:\n\n- `.all-contributorsrc`\n- `README.md`\n\nRecommended workflow for each open contributor PR:\n\n```bash\ngh pr checkout <pr-number> --repo fzyzcjy/flutter_rust_bridge\ngit fetch origin master\ngit merge --no-ff origin/master\n```\n\nIf the merge conflicts:\n\n1. Keep the latest `origin/master` content.\n2. Re-apply the contributor entry from the bot branch.\n3. Ensure the contributor card remains present in `README.md`.\n4. Re-run `./frb_internal generate-internal-contributor` if you edited contributor metadata or finished filling in `.all-contributors-custom.yaml`.\n5. `git add` the resolved files and commit the merge.\n6. Push the contributor branch back to origin.\n\nDo not revert unrelated contributor entries that were merged by other PRs.\n\n## Step 8: Merge the contributor PRs\n\nAfter the branch is updated and GitHub reports it mergeable:\n\n```bash\ngh pr merge <pr-number> --repo fzyzcjy/flutter_rust_bridge --merge --delete-branch\n```\n\nNotes:\n\n- GitHub may temporarily report `DIRTY` or reject merge immediately after push.\n- If that happens, wait briefly, re-check the PR state, and retry the merge.\n- After merging one contributor PR, fetch `origin/master` again before resolving the next one.\n\n## Step 9: Final verification\n\nAt the end, verify there are no remaining open all-contributors PRs:\n\n```bash\ngh pr list --repo fzyzcjy/flutter_rust_bridge --search \"author:allcontributors[bot] is:open\" --limit 50 --json number,title,mergeStateStatus,url\n```\n\nAlso verify the credited contributors appear in:\n\n- `.all-contributorsrc`\n- `README.md`\n- `website/docs/index.md` if contributor regeneration was run\n",".claude/skills/frb-android-emulator-prepare/SKILL.md":"---\nname: frb-android-emulator-prepare\ndescription: Use when preparing, installing, diagnosing, or explaining the host Android Emulator environment for flutter_rust_bridge local runtime validation, including Android SDK command-line tools, emulator packages, AVD creation, host emulator startup, and Docker-local ADB connectivity to the emulator TCP endpoint.\n---\n\n# FRB Android Emulator Preparation\n\nUse this skill when a task requires a **host Android Emulator** for FRB local runtime validation.\n\n## Boundary\n\nFRB Android runtime validation currently supports a host Android Emulator path: host needs Java, Android SDK command-line tools, `emulator`, a system image, and an AVD; Docker runs FRB/Flutter commands through a Docker-local ADB server connected to the emulator TCP endpoint.\n\nDo not install Android Emulator packages unless the user has explicitly asked to prepare or run an emulator. Installing the emulator changes host state and downloads large SDK packages.\n\n## Default Host Locations\n\nUse standard Android Studio locations on macOS:\n\n```text\n~/Library/Android/sdk\n~/.android/avd\n~/.android\n```\n\nRespect `ANDROID_HOME` if it is already set. Otherwise use `~/Library/Android/sdk`. Do not invent repo-specific defaults such as `/workspace`, `/tmp`, or a user's personal artifacts directory.\n\n## Preflight\n\nOfficial references for this workflow:\n\n- Android Studio and command-line tools download page: https://developer.android.com/studio\n- `sdkmanager` command-line package manager: https://developer.android.com/tools/sdkmanager\n- `avdmanager` command-line AVD manager: https://developer.android.com/tools/avdmanager\n- Android Emulator command line: https://developer.android.com/studio/run/emulator-commandline\n- ADB command-line tool: https://developer.android.com/tools/adb\n\nRun these before installing anything:\n\n```bash\nsw_vers\nuname -m\nwhich adb || true\nadb version || true\necho \"${ANDROID_HOME:-}\"\nls -ld \"$HOME/Library/Android/sdk\" \"$HOME/.android\" \"$HOME/.android/avd\" 2>/dev/null || true\nwhich java || true\njava -version || true\n```\n\nIf the task requires an emulator, record whether these tools already exist:\n\n```bash\nls \"$HOME/Library/Android/sdk/cmdline-tools/latest/bin/sdkmanager\" 2>/dev/null || true\nls \"$HOME/Library/Android/sdk/cmdline-tools/latest/bin/avdmanager\" 2>/dev/null || true\nls \"$HOME/Library/Android/sdk/emulator/emulator\" 2>/dev/null || true\n```\n\n## Installation Policy\n\nBefore installing or downloading anything, state exactly what will be installed and ask for approval through the command escalation mechanism.\n\nFor a minimal macOS emulator setup, expect these host changes:\n\n```text\nHomebrew openjdk, if Java is missing\n~/Library/Android/sdk/cmdline-tools/latest\n~/Library/Android/sdk/emulator\n~/Library/Android/sdk/platform-tools\n~/Library/Android/sdk/platforms/android-<api>\n~/Library/Android/sdk/system-images/android-<api>/google_apis/arm64-v8a\n~/.android/avd/<avd-name>.avd\n~/.android/avd/<avd-name>.ini\n```\n\nPrefer ARM64 system images on Apple Silicon:\n\n```text\nsystem-images;android-35;google_apis;arm64-v8a\n```\n\nUse a different API level only when the task or Flutter version requires it.\n\n## Install Java\n\nIf `/usr/bin/java` is missing and Homebrew is available, prefer the Homebrew `openjdk` formula because it does not require the sudo package installer:\n\n```bash\nHOMEBREW_NO_AUTO_UPDATE=1 brew install openjdk\n```\n\nFor SDK commands in the current shell, prepend the Homebrew JDK:\n\n```bash\nexport PATH=\"/opt/homebrew/opt/openjdk/bin:$PATH\"\n```\n\nDo not use `brew install --cask temurin` in non-interactive agent runs unless the user has approved sudo-capable installation; it may invoke macOS Installer through `sudo`.\n\n## Install Command-Line Tools\n\nIf Android Studio is not installed and `cmdline-tools/latest` is missing, install the official Android command-line tools into the standard SDK root. This follows the Android Developers command-line tools instructions on the Android Studio download page: https://developer.android.com/studio. The important shape is to download the command-line tools package from that page, extract it, and place it under `cmdline-tools/latest` in the Android SDK root.\n\n```bash\nmkdir -p \"$HOME/Library/Android/sdk/cmdline-tools/latest\"\ncurl -L -o /private/tmp/commandlinetools-mac-latest.zip \\\n  https://dl.google.com/android/repository/commandlinetools-mac-14742923_latest.zip\nmkdir -p /private/tmp/frb-android-commandlinetools-extract\nditto -x -k /private/tmp/commandlinetools-mac-latest.zip /private/tmp/frb-android-commandlinetools-extract\ncp -R /private/tmp/frb-android-commandlinetools-extract/cmdline-tools/. \\\n  \"$HOME/Library/Android/sdk/cmdline-tools/latest/\"\n```\n\nThen set:\n\n```bash\nexport ANDROID_HOME=\"$HOME/Library/Android/sdk\"\nexport ANDROID_SDK_ROOT=\"$ANDROID_HOME\"\nexport PATH=\"/opt/homebrew/opt/openjdk/bin:$ANDROID_HOME/cmdline-tools/latest/bin:$ANDROID_HOME/platform-tools:$ANDROID_HOME/emulator:$PATH\"\n```\n\n## Install Emulator Packages\n\nAccept licenses and install the minimal packages with `sdkmanager`, the Android SDK package manager documented by Android Developers at https://developer.android.com/tools/sdkmanager:\n\n```bash\nyes | sdkmanager --licenses\nsdkmanager \\\n  \"emulator\" \\\n  \"platform-tools\" \\\n  \"platforms;android-35\" \\\n  \"system-images;android-35;google_apis;arm64-v8a\"\n```\n\nIf the user wants a smaller or non-Google image, choose a matching `system-images;android-<api>;default;arm64-v8a` package instead.\n\n## Create AVD\n\nUse a predictable AVD name that the `frb-dev-env` examples can reference. This uses `avdmanager`, the Android Developers command-line tool for creating and managing AVDs documented at https://developer.android.com/tools/avdmanager:\n\n```bash\navdmanager create avd \\\n  --name FRB_API_35 \\\n  --package \"system-images;android-35;google_apis;arm64-v8a\" \\\n  --device \"pixel_8\"\n```\n\nIf the device profile is unavailable, list device profiles and choose a common phone:\n\n```bash\navdmanager list device\n```\n\n## Start And Verify Emulator\n\nStart the host emulator through `frb-dev-env`. The underlying emulator invocation follows the Android Emulator command-line interface documented at https://developer.android.com/studio/run/emulator-commandline:\n\n```bash\n.claude/skills/frb-dev-env/frb_dev_env.py android emulator --avd FRB_API_35 --port 5554\n```\n\nVerify host ADB can see the emulator. This uses ADB, the Android Developers command-line tool documented at https://developer.android.com/tools/adb:\n\n```bash\nadb devices -l\n```\n\nFor emulator runtime tests, let Docker use its own ADB server and connect directly to the host emulator ADB TCP endpoint. For emulator console port `5554`, that endpoint is `host.docker.internal:5555`:\n\n```bash\n.claude/skills/frb-dev-env/frb_dev_env.py docker exec --android-emulator-adb -- adb devices -l\n```\n\nOnly after both ADB checks pass, run an FRB Android runtime command. Use the Docker-visible emulator serial:\n\n```bash\n.claude/skills/frb-dev-env/frb_dev_env.py docker exec --android-emulator-adb -- \\\n  bash -lc './frb_internal test-flutter-native --package frb_example--flutter_via_create --flutter-test-args \"--device-id host.docker.internal:5555\"'\n```\n\n## Common Failures\n\n- `Android SDK root does not exist`: install Android Studio or command-line tools into `~/Library/Android/sdk`, or set `ANDROID_HOME`.\n- `emulator: command not found`: install the SDK `emulator` package and ensure `~/Library/Android/sdk/emulator` is on `PATH`.\n- No AVD found: create `FRB_API_35` with `avdmanager create avd`, or pass the existing AVD name to `android emulator --avd`.\n- Docker cannot see the host emulator through `--android-emulator-adb`: verify the host emulator is running on console port `5554`, verify host `adb devices -l` shows `emulator-5554`, then run `adb connect host.docker.internal:5555` inside Docker or retry the helper command.\n- Flutter integration test fails with a VM service or DDS connection refused on `127.0.0.1`: the command likely used a host ADB server against an emulator. Use `--android-emulator-adb` so `adb forward` binds inside Docker.\n\n## Cleanup\n\nStop the emulator from host ADB:\n\n```bash\nadb -s emulator-5554 emu kill\n```\n\nAvoid deleting SDK packages or AVDs unless the user explicitly asks.\n",".claude/skills/frb-cargokit-dev/SKILL.md":"---\nname: frb-cargokit-dev\ndescription: Develop or update CargoKit for flutter_rust_bridge. Use when a bug or feature belongs in fzyzcjy/Cargokit, when updating the FRB CargoKit submodule pointers, or when preparing the paired CargoKit and FRB PRs that synchronize CargoKit changes into FRB integrate templates and examples.\n---\n\n# FRB CargoKit Development\n\n## Overview\n\nUse this skill when the actual fix belongs in the external `fzyzcjy/Cargokit` repository and FRB needs a follow-up PR that points its CargoKit submodules at the updated CargoKit branch or commit.\n\nThe usual shape is two PRs:\n\n1. A CargoKit PR with the real implementation.\n2. An FRB PR that updates the two CargoKit submodule pointers and regenerates copied integrate outputs.\n\nDo not implement CargoKit behavior only in copied FRB example output. The source of truth is the CargoKit repository referenced by the template submodules.\n\n## Source Locations\n\nFRB embeds CargoKit as git submodules in two template locations:\n\n- `frb_codegen/assets/integration_template/app/rust_builder/cargokit`\n- `frb_codegen/assets/integration_template/plugin/cargokit`\n\nBoth submodules should normally point at the same CargoKit branch or commit for the same logical update. Downstream generated copies may appear under:\n\n- `frb_example/**/cargokit/**`\n- `frb_example/**/rust_builder/cargokit/**`\n\nRead `frb-cargokit` first if you need to classify an existing diff before deciding where the change belongs.\n\n## Workflow\n\n### 1. Prepare The CargoKit Change\n\nWork in `fzyzcjy/Cargokit` for the actual CargoKit implementation.\n\n1. Create a CargoKit branch whose name is easy to reference from the FRB PR.\n2. Implement the fix in CargoKit and run the relevant CargoKit checks from that repository.\n3. Push the branch and open a CargoKit PR.\n4. Keep the branch alive until the FRB PR has passed CI.\n\nIf you are editing through an FRB submodule checkout, remember that commits belong to the nested CargoKit repository, not the outer FRB repository. Push the nested repository branch before updating the outer FRB submodule pointer.\n\n### 2. Update FRB To Point At The CargoKit Branch\n\nIn the FRB repository:\n\n1. Initialize submodules if needed:\n\n   ```bash\n   git submodule update --init --recursive\n   ```\n\n2. Fetch and check out the CargoKit branch or commit in both submodule checkouts:\n\n   ```bash\n   git -C frb_codegen/assets/integration_template/app/rust_builder/cargokit fetch origin <branch-or-ref>\n   git -C frb_codegen/assets/integration_template/app/rust_builder/cargokit checkout <commit-or-branch>\n   git -C frb_codegen/assets/integration_template/plugin/cargokit fetch origin <branch-or-ref>\n   git -C frb_codegen/assets/integration_template/plugin/cargokit checkout <commit-or-branch>\n   ```\n\n3. Stage the submodule pointer updates in the outer FRB repository:\n\n   ```bash\n   git add frb_codegen/assets/integration_template/app/rust_builder/cargokit\n   git add frb_codegen/assets/integration_template/plugin/cargokit\n   ```\n\n4. Confirm the outer FRB diff shows submodule pointer changes for both paths.\n\nUseful checks:\n\n```bash\ngit submodule status --recursive\ngit diff --submodule\n```\n\nDo not leave one template submodule on the old CargoKit commit unless the app and plugin templates intentionally need different CargoKit revisions.\n\n### 3. Regenerate FRB Integrate Outputs\n\nAfter changing either CargoKit submodule pointer, run:\n\n```bash\n./frb_internal precommit-integrate\n```\n\nThis updates copied CargoKit output and platform scaffold files under `frb_example/**`. Those downstream diffs are expected when CargoKit files are copied from the updated template.\n\nStage and commit the regenerated integrate output together with the submodule pointer update:\n\n```bash\ngit add frb_example frb_codegen/assets/integration_template\ngit commit -m \"Update CargoKit template pointers\"\n```\n\nThen run the normal PR checks from `frb-prepare-pr`, especially:\n\n```bash\n./frb_internal lint --fix\n```\n\nRun focused tests when the CargoKit change affects a platform that can be exercised locally. Otherwise let FRB CI cover the platform matrix and use `frb-fix-ci` for failures.\n\n### 4. Open The FRB PR\n\nThe FRB PR should mention that CargoKit is intentionally pointed at the temporary CargoKit PR branch or commit. Keep the PR body empty if the local PR workflow requires it, but use GitHub comments when you need to leave coordination notes.\n\nBefore asking for final review:\n\n1. Wait for the FRB PR CI to pass with the temporary CargoKit pointer.\n2. Ask reviewers to merge the CargoKit PR first.\n3. Update the FRB submodule pointers from the temporary branch commit to the merged CargoKit commit on the canonical branch.\n4. Rerun `./frb_internal precommit-integrate` if the CargoKit commit changed.\n5. Push the refreshed FRB PR and wait for CI again.\n6. Ask reviewers to merge the FRB PR.\n\n## CI Failure Triage\n\nWhen FRB CI fails after a CargoKit update, classify the failure before editing:\n\n- CargoKit behavior failure: fix it in `fzyzcjy/Cargokit`, then update both FRB submodule pointers.\n- Missing integrate refresh: run `./frb_internal precommit-integrate` and commit the copied output.\n- Lint or formatting drift: run `./frb_internal lint --fix`.\n- Platform-specific FRB scaffold issue: fix the FRB integration template, then run `./frb_internal precommit-integrate`.\n\nDo not patch copied `frb_example/**/cargokit/**` files directly as the final fix. They should be produced from the updated CargoKit submodule and integrate templates.\n\n## Related Skills\n\n- `frb-cargokit` - Understand CargoKit ownership and diff layers\n- `frb-code-generation` - Choose the correct generation command\n- `frb-prepare-pr` - Prepare an FRB PR\n- `frb-fix-ci` - Diagnose CI failures after the PR is opened\n",".claude/skills/frb-cargokit/SKILL.md":"---\nname: frb-cargokit\ndescription: Explain how cargokit is sourced, copied, and analyzed inside flutter_rust_bridge. Use when working on `integration_template/**/cargokit`, `frb_example/**/cargokit`, integrate/generate output drift, or any diff that mentions cargokit and you need to identify the true source of truth before judging the change.\n---\n\n# FRB Cargokit\n\n## Overview\n\nUse this skill to avoid mixing together three different things: the external cargokit repository, the template submodule pointers in FRB, and copied cargokit files inside generated examples.\n\nThe main job is to determine which layer a change belongs to before analyzing whether the change is correct.\n\n## Mental Model\n\nThere are two important cargokit forms inside FRB:\n\n1. External source\n   FRB depends on an external cargokit repository through git submodules under `frb_codegen/assets/integration_template/**/cargokit`.\n\n2. Copied outputs\n   FRB commands such as integrate/generate often copy concrete cargokit files into `frb_example/**/cargokit` or `frb_example/**/rust_builder/cargokit`.\n\nThis means a large cargokit diff in examples often does not mean FRB designed a large new cargokit behavior. It may only mean a template or submodule change propagated into copied outputs.\n\n## Source Of Truth\n\nTreat these paths as the source of truth when deciding cargokit behavior:\n\n- `frb_codegen/assets/integration_template/app/rust_builder/cargokit`\n- `frb_codegen/assets/integration_template/plugin/cargokit`\n\nTreat these paths as downstream copies unless the evidence shows otherwise:\n\n- `frb_example/**/cargokit/**`\n- `frb_example/**/rust_builder/cargokit/**`\n\nDo not start by judging copied example files in isolation. First determine which source submodule pointer or template content produced them.\n\n## Synchronizing Copies\n\nUse `./frb_internal sync-cargokit-copies` to refresh checked-in Cargokit copies from the template submodules when those copies are not already regenerated by the integrate CI. The command mirrors the Rust integration template writer's Cargokit filtering and copied-file prelude.\n\nDo not add `flutter_via_create`, `flutter_via_integrate`, or `flutter_package` to this sync map; the integrate generation CI already recreates those examples from templates. The CI `Generate :: Internal` job runs this as part of `./frb_internal generate-internal --set-exit-if-changed --coverage`, so the normal generate-internal diff gate also catches Cargokit copy drift. If Cargokit copies drift, fix the source-of-truth template/submodule first when appropriate, run `./frb_internal sync-cargokit-copies`, and commit the resulting copied-output refresh.\n\n## Diff Analysis Order\n\nWhen a user asks \"what happened to cargokit?\" follow this order:\n\n1. Identify the layer.\n   Decide whether the diff is a submodule pointer update, a template file change, or copied output churn.\n\n2. If the change is a submodule pointer update, inspect the two external cargokit commits first.\n   Compare the old and new submodule SHAs inside the submodule repository before saying anything about the FRB-side diff.\n\n3. Only then inspect copied example files.\n   Ask whether they merely reflect the upstream change or whether FRB also introduced independent local edits.\n\n4. Separate semantic changes from churn.\n   Distinguish real logic changes from format-only refreshes, lockfile updates, scaffold refreshes, and regenerated outputs.\n\n## What To Say Explicitly\n\nWhen answering, state which of these is true:\n\n- \"This is an external cargokit change exposed by a submodule pointer update.\"\n- \"This is downstream copy churn caused by integrate/generate.\"\n- \"This repository also has local copied cargokit edits in examples.\"\n\nIf discussing correctness, tie the judgment to the correct layer:\n\n- For submodule updates, judge the diff between the two external SHAs.\n- For copied outputs, judge whether the copy is expected and synchronized.\n- Do not infer upstream intent from a regenerated example diff alone.\n\n## Common Mistakes\n\nAvoid these mistakes:\n\n- Treating `frb_example/**/cargokit/**` as the primary implementation source.\n- Assuming a large example diff implies a large semantic cargokit change.\n- Analyzing copied files before checking the submodule pointer diff.\n- Mixing \"FRB updated the pointer\" with \"FRB locally rewrote cargokit.\"\n\n## Scope Boundary\n\nThis skill explains ownership, propagation, and diff interpretation.\n\nUse `frb-code-generation` when you need to know which generation command to run.\nUse `frb-debugging` when generation or cargokit-related behavior is failing and needs deeper investigation.\n",".claude/skills/frb-categorize-code-diff/SKILL.md":"---\nname: frb-categorize-code-diff\ndescription: Use when classifying a flutter_rust_bridge branch or PR diff into test and non-test changes, including colocated Rust test modules.\n---\n\n# 1 When to use\n\n- Use this skill to answer which changes in a branch or PR are tests and which are not.\n- Use the repository script instead of classifying paths by hand. Rust tests are commonly colocated in production `.rs` files.\n\n## 2 Run\n\n- Fetch the PR base before relying on `origin/master`.\n- Run from the repository root:\n\n```bash\nuv run --no-project .claude/skills/frb-categorize-code-diff/categorize_code_diff.py --base origin/master --head HEAD\n```\n\n- Add `--json` when another tool will consume the result.\n- Pass the PR's actual base revision when it is not `origin/master`.\n\n## 3 Interpret\n\n- `test-only`: every changed line is in a dedicated test path or a recognized colocated Rust test region.\n- `non-test-only`: every changed line is outside recognized test code.\n- `mixed`: the file contains both test and non-test changed lines.\n- `unclassified`: the diff cannot be mapped confidently, such as a binary patch.\n- Treat the non-test list as conservative. Unknown code is non-test rather than silently counted as test.\n\n## 4 Reliability\n\n- The script compares `base...head`, matching GitHub pull request diff semantics.\n- Dedicated test paths include `test/`, `tests/`, `test_fixtures/`, and conventional test filenames.\n- Rust inline classification recognizes brace-delimited items preceded by `#[cfg(test)]` or `#[test]` in both the base and head revisions.\n- Re-run after every PR update. Line identities and rename targets may change.\n- Inspect `mixed` files manually before claiming that a PR contains no production changes.\n\n## 5 Validation\n\n- Run the script tests after changing its classification logic:\n\n```bash\nPYTHONDONTWRITEBYTECODE=1 uv run --no-project --with pytest --with typer pytest -q .claude/skills/frb-categorize-code-diff/test_categorize_code_diff.py\n```\n\n- Keep `PYTHONDONTWRITEBYTECODE=1`; otherwise pytest leaves an untracked `__pycache__/` inside the skill directory.\n",".claude/skills/frb-ci-filter/SKILL.md":"---\nname: frb-ci-filter\ndescription: Use when running focused flutter_rust_bridge GitHub Actions CI via ci_filter, adding/removing the ci-manual-dispatch PR label, choosing exact CI jobs or matrix entries, documenting intentionally partial CI runs, or repeatedly validating an exact CI target on one commit.\n---\n\nUse this skill when CI feedback is too expensive and the current task needs a focused GitHub Actions signal instead of automatic full CI.\n\nThe preferred mechanism is the formal `workflow_dispatch` input in `.github/workflows/ci.yaml`:\n\n```bash\ngh workflow run ci.yaml --ref <branch> -f 'ci_filter=<filter>'\n```\n\nDo not temporarily hack workflow jobs just to get partial CI unless the formal filter cannot express the target.\n\n# 1 Filter Syntax\n\n```text\nfull\njob_id\njob_a,job_b\njob_id[dimension=value]\njob_id[dimension=value|other_value,other_dimension=value]\njob_a,job_b[dimension=value]\n```\n\n- `full` or `*` runs the normal full CI plan.\n- `job_id` runs the whole job, including its full matrix if it has one.\n- `job_id[...]` runs only matching matrix entries.\n- Bracket filters currently require `dimension=value`.\n- Commas outside brackets combine multiple jobs.\n- Explicit `dimension=value` conditions inside brackets are ANDed.\n- `|` means OR within one dimension.\n- Matrix dimensions include direct keys such as `image`, `package`, `device`, `api-level`, `sanitizer`, `platforms`, and nested `matrix.info` keys such as `platform`, `target`, `version`.\n- Numeric matrix values are matched as strings, e.g. `api-level=35`.\n- Values may contain spaces, e.g. iOS simulator device names.\n- Unknown jobs, unknown dimensions, malformed filters, and filters matching no matrix entries fail in the `Plan :: CI` job.\n\n# 2 Manual Dispatch Label\n\nUse the `ci-manual-dispatch` PR label when an agent or human wants to prevent automatic PR CI from running the full surface while iterating.\n\n```bash\ngh pr edit <pr-number> --add-label ci-manual-dispatch\n```\n\nWith the label present:\n\n- normal PR-triggered CI produces an empty plan for the heavy jobs\n- `workflow_dispatch` remains available for explicit focused runs\n- the PR must not be treated as merge-ready from partial runs alone\n\nBefore final readiness:\n\n```bash\ngh pr edit <pr-number> --remove-label ci-manual-dispatch\n```\n\nThen let the normal full CI surface run. Removing the label triggers PR CI because the workflow listens to `labeled` and `unlabeled` pull request events.\n\n# 3 Manual Dispatch PR Comment\n\nManual `workflow_dispatch` runs of `.github/workflows/ci.yaml` comment from `Plan :: CI` on the matching open PR with the selected `ci_filter` and run link; normal `push` and `pull_request` runs do not comment.\n\n# 4 Local Planning\n\nBefore dispatching an expensive manual run, smoke the filter locally:\n\n```bash\n./frb_internal plan-ci --filter 'test_dart_web[package=frb_example--pure_dart_pde]'\n```\n\nThe command emits a GitHub Actions output line:\n\n```text\nplan={...}\n```\n\nThe workflow consumes it with paths like:\n\n```yaml\nfromJSON(needs.plan_ci.outputs.plan).test_dart_web.enable\nfromJSON(needs.plan_ci.outputs.plan).test_dart_web.matrix\n```\n\nThat output shape is intentional; each CI job has its own top-level entry containing `enable`, and matrix jobs also contain `matrix`.\n\n# 5 Repeated Precise Validation\n\nUse repeated precise validation when a flaky fix needs several independent executions of one CI job or matrix entry on exactly the same commit.\n\n- **Fix the commit**: Push the branch once, record its SHA, and do not move the branch until all runs finish.\n- **Smoke the filter**: Run `./frb_internal plan-ci --filter '<filter>'` before dispatching.\n- **Dispatch independently**: Create five separate `workflow_dispatch` runs, not one run with five matrix copies and not five attempts of one run.\n- **Keep the filter identical**: Pass the same exact `ci_filter` to every dispatch.\n- **Verify the SHA**: Confirm every selected run reports the recorded `headSha`; a branch name alone is not evidence that queued runs used the same commit.\n- **Count target jobs**: Count the conclusion of the selected job or matrix entry, not the workflow conclusion or skipped jobs.\n- **Preserve every run link**: Report all five run URLs, the fixed SHA, the filter, and the target-job result.\n- **Do not move or cancel**: A new commit invalidates the comparison; cancellation does not count as a completed validation.\n\nRecord the immutable input:\n\n```bash\nbranch=codex/example-fix\nexpected_sha=$(git rev-parse HEAD)\nfilter='test_dart_web[package=frb_example--pure_dart]'\ngit push -u origin \"$branch\"\n```\n\nDispatch five independent runs. Manual dispatches use unique concurrency groups, so these runs do not cancel one another:\n\n```bash\nfor run_number in 1 2 3 4 5; do\n  gh workflow run ci.yaml --ref \"$branch\" -f \"ci_filter=$filter\"\ndone\n```\n\nList the candidate runs once, then select the five runs created for this validation window:\n\n```bash\ngh run list \\\n  --workflow ci.yaml \\\n  --branch \"$branch\" \\\n  --event workflow_dispatch \\\n  --limit 10 \\\n  --json databaseId,createdAt,headSha,status,conclusion,url\n```\n\nFor each selected run:\n\n- Confirm `headSha == expected_sha`.\n- Confirm `Plan :: CI` creates only the requested job or matrix entry.\n- Wait for the target job to complete.\n- If it fails, archive and inspect that job's complete log.\n- Report a result only after all five target jobs finish.\n\nDo not claim “5/5” from any of these shapes:\n\n- One workflow run containing five matrix entries.\n- One workflow run rerun five times.\n- Five workflow runs whose `headSha` values differ.\n- Five workflow conclusions where the target job was skipped, cancelled, or never started.\n- Fewer than five completed target jobs plus queued or in-progress jobs.\n\n# 6 Examples\n\nThese examples are protected by `tools/frb_internal/test/ci_plan_test.dart`; update that test whenever adding or changing examples here.\n\nFull CI:\n\n```bash\ngh workflow run ci.yaml --ref <branch> -f 'ci_filter=full'\n```\n\nOne non-matrix job:\n\n```bash\ngh workflow run ci.yaml --ref <branch> -f 'ci_filter=lint_dart_primary'\n```\n\nSeveral whole jobs:\n\n```bash\ngh workflow run ci.yaml --ref <branch> -f 'ci_filter=lint_dart_primary,test_dart_web'\n```\n\nOne non-matrix job plus one matrix entry:\n\n```bash\ngh workflow run ci.yaml --ref <branch> -f 'ci_filter=lint_dart_primary,test_dart_web[package=frb_example--pure_dart]'\n```\n\nOne Dart web package:\n\n```bash\ngh workflow run ci.yaml --ref <branch> -f 'ci_filter=test_dart_web[package=frb_example--pure_dart_pde]'\n```\n\nTwo Dart web packages:\n\n```bash\ngh workflow run ci.yaml --ref <branch> -f 'ci_filter=test_dart_web[package=frb_dart|frb_example--pure_dart_pde]'\n```\n\nTwo Dart web packages plus one non-matrix job:\n\n```bash\ngh workflow run ci.yaml --ref <branch> -f 'ci_filter=test_dart_web[package=frb_dart|frb_example--pure_dart_pde],lint_rust_primary'\n```\n\nTwo precise matrix tuples in the same job:\n\n```bash\ngh workflow run ci.yaml --ref <branch> -f 'ci_filter=test_rust[image=ubuntu-latest,version=nightly],test_rust[image=ubuntu-latest,version=1.85.0]'\n```\n\nOne Dart native package on Linux:\n\n```bash\ngh workflow run ci.yaml --ref <branch> -f 'ci_filter=test_dart_native[image=ubuntu-24.04,package=tools--frb_internal]'\n```\n\nRust tests on selected toolchains:\n\n```bash\ngh workflow run ci.yaml --ref <branch> -f 'ci_filter=test_rust[image=ubuntu-latest,version=nightly|1.85.0]'\n```\n\nOne Flutter desktop package on Linux:\n\n```bash\ngh workflow run ci.yaml --ref <branch> -f 'ci_filter=test_flutter_native_desktop[platform=linux,package=frb_example--gallery]'\n```\n\nOne Android emulator matrix entry:\n\n```bash\ngh workflow run ci.yaml --ref <branch> -f 'ci_filter=test_flutter_native_android[package=frb_example--flutter_via_create,device=pixel,api-level=35]'\n```\n\nOne iOS simulator matrix entry:\n\n```bash\ngh workflow run ci.yaml --ref <branch> -f 'ci_filter=test_flutter_native_ios[device=iPhone 16 Pro Max Simulator (18.6),package=frb_example--rust_ui_counter--ui]'\n```\n\nOne codegen command-generate entry:\n\n```bash\ngh workflow run ci.yaml --ref <branch> -f 'ci_filter=generate_run_frb_codegen_command_generate[image=ubuntu-24.04,package=frb_example--integrate_third_party]'\n```\n\nOne OHOS command-integrate entry:\n\n```bash\ngh workflow run ci.yaml --ref <branch> -f 'ci_filter=generate_run_frb_codegen_command_integrate[platforms=ohos]'\n```\n\nOne sanitizer entry:\n\n```bash\ngh workflow run ci.yaml --ref <branch> -f 'ci_filter=test_dart_sanitizer[sanitizer=asan,package=frb_example--pure_dart]'\n```\n\nOne benchmark runner:\n\n```bash\ngh workflow run ci.yaml --ref <branch> -f 'ci_filter=bench_dart_native[image=ubuntu-24.04]'\n```\n\n# 7 Choosing Evidence\n\nFor intentional red reproduction PRs:\n\n- Run only the single job or matrix entry that proves the reported bad behavior.\n- State the exact `ci_filter` in the PR body.\n- State the expected failure text or behavior.\n- Do not treat the narrowed red run as fix readiness evidence.\n\nFor ordinary iteration branches:\n\n- Prefer the smallest filter that exercises the changed behavior.\n- Say in PR status/comments when only filtered CI has run.\n- Before final review or merge readiness, remove `ci-manual-dispatch` and run normal CI.\n\n# 8 Validation\n\nBefore pushing changes to the filter mechanism or examples:\n\n```bash\ncd tools/frb_internal\ndart test test/ci_plan_test.dart\n```\n\nUseful smoke check:\n\n```bash\n./frb_internal plan-ci --filter 'test_dart_web[package=frb_example--pure_dart_pde]'\n```\n",".claude/skills/frb-code-generation/SKILL.md":"---\nname: frb-code-generation\ndescription: Use when modifying Rust APIs, codegen, generated examples, or platform scaffolds in flutter_rust_bridge to select generation commands and preserve source-of-truth and convergence rules\n---\n\n# FRB Code Generation\n\n> **Note:** Read `frb-dev-env` before running commands and follow the active user's environment rules.\n\n## Overview\n\nflutter_rust_bridge requires code generation when Rust APIs change. This skill maps change types to the minimal generation commands needed.\n\n**Core principle:** Run only the generation commands needed for your change type.\n\n**Validation rule:** If regenerated outputs cause previously green non-`Generate` jobs to fail, treat the generated outputs as suspect until they are validated from a clean environment.\n\n> **After codegen:** Follow `frb-dev-env` and verify generated output is present in the selected worktree before reviewing the diff.\n\n## Quick Reference\n\n| Change Type | Command |\n|-------------|---------|\n| Example Rust API (`frb_example/**/rust/src/api/*.rs`) | `./frb_internal precommit-generate` |\n| Flutter integrate examples | `./frb_internal precommit-integrate` |\n| `frb_codegen/` generation logic | `./frb_internal precommit-generate` + `generate-internal-rust` |\n| `frb_rust/src/` core API | `./frb_internal generate-internal-rust` |\n| `frb_example/pure_dart` generator | `./frb_internal generate-internal-frb-example-pure-dart` |\n| CLI help documentation | `./frb_internal generate-internal-book-help` |\n| Root `README.md` or `CHANGELOG.md` | `./frb_internal generate-internal-readme` |\n| Other non-generated docs, comments, tests, or `frb_dart/` source | No generation needed |\n\n## Important Rules\n\n### Root Documentation Fan-Out\n\nTreat the root `README.md` and `CHANGELOG.md` as sources of truth. After changing either file, run:\n\n```bash\n./frb_internal generate-internal-readme\n```\n\nCommit the generated package documentation in the same change. Do not manually edit `frb_dart/README.md`, `frb_dart/CHANGELOG.md`, or `frb_hooks/CHANGELOG.md`.\n\n### Integrate Template Drift\n\nIf a diff touches Flutter integrate example outputs, platform scaffolds, or copied `cargokit` files under `frb_example/**`, check whether the real source of truth should also change under `frb_codegen/assets/integration_template/`.\n\nDo not submit only downstream integrate output changes when the behavior belongs in the template. Update the template first, then run:\n\n```bash\n./frb_internal precommit-integrate\n```\n\nTreat `frb_codegen/assets/integration_template/**/cargokit` as the source of truth for copied `cargokit` output. If the actual bug belongs inside the external `cargokit` submodule, read the `frb-cargokit` skill before deciding whether to patch the submodule and update its pointer.\n\nFor CI diagnosis rules about generated-file format/lint failures, repeated package-level `Generate` drift, or `Generate :: FRB Codegen :: Command Integrate` failures, you MUST read `frb-fix-ci` first. This skill is for command selection, not CI failure-propagation diagnosis.\n\nFor `pure_dart` / `pure_dart_pde` generation issues, treat `frb_example/pure_dart` as the upstream source and `frb_example/pure_dart_pde` as the derived copy. See `frb-fix-ci` for the CI diagnosis workflow.\n\nIf CI repair has already entered repeated package-level drift, you MUST stop choosing narrower commands and switch to `frb-fix-ci`.\n\nDo not manually patch generated files as the final fix. The final accepted result should be produced by the corresponding generation command in a clean matching environment.\n\n### Generation Convergence\n\n- Run the narrowest owning generator before broad generation.\n- Normalize the intended source inputs, resolve dependencies, run final code generation, and format with the target toolchain. Do not hand formatter-unstable intermediate text to a different generation lane.\n- Run the owning generation command a second time and require a clean diff. A single successful run is not convergence.\n- Preserve semantic equivalence in templates. Do not move construction, scope, lifetime, or callback boundaries merely to make new formatter output look smaller.\n\n### Generated Output Provenance\n\nClassify every changed path before accepting generated drift:\n\n| Provenance | Required action |\n| --- | --- |\n| Integration template | Change the template source and regenerate consumers |\n| Apple scaffold asset | Refresh through the owning scaffold workflow |\n| CargoKit copy | Read `frb-cargokit` and follow its ownership and synchronization rules |\n| Generated example output | Keep the generator and snapshot together |\n| Flutter migrator edit in a legacy example | Apply the exact current scaffold migration and record it as direct |\n| Unexplained manual edit | Revert it or identify its owning source before proceeding |\n\n- When an old example has no supported regeneration entry point, compare it with a fresh target-Flutter scaffold and directly apply only required tool-defined migrations.\n- Record direct legacy scaffold migrations separately from automatic output.\n\n### OHOS Integrate Composition\n\n- Treat the checked-in package and the older OHOS Flutter fork as two distinct sources of truth.\n- Generate generic Flutter scaffold files with the current Flutter package.\n- Overlay only explicit OHOS-owned paths from the OHOS fork: `ohos` and, when required by the package, `rust_builder/ohos` and `rust_builder/pubspec.yaml`.\n- Exclude transient trees such as `node_modules` and preserve intentional deletions instead of resurrecting stale files.\n- Stage and validate the composed result before replacing the canonical package. Restore the original package if create, dependency resolution, code generation, formatting, or the final swap fails.\n- Test both success and failure paths. Do not add production APIs whose only purpose is exposing internals to tests.\n",".claude/skills/frb-debugging/SKILL.md":"---\nname: frb-debugging\ndescription: Use when generated code looks wrong, code generation fails, or you need to understand FRB internals\n---\n\n# FRB Debugging\n\n> **Note:** Read `frb-dev-env` before running commands and follow the active user's environment rules.\n\n## When to Use\n\n- Generated code looks wrong\n- Code generation fails\n- Need to understand FRB internals\n- Runtime errors in generated code\n\n## Quick Diagnostics\n\n### Dump Intermediate Representations\n\nThe in-tree examples by default enable `dump_all: true` in  `flutter_rust_bridge.yaml` for debugability.\n\nTherefore, you can check `rust/target/frb_dump/` for:\n- Effective configuration\n- IR (intermediate representation)\n- Generated spec and code\n- ...\n\n### Debug Logs\n\n```bash\nRUST_LOG=debug flutter_rust_bridge_codegen ...\n```\n\n## Full Troubleshooting\n\nIf above doesn't help, see:\n\n* `website/docs/docs/guides/contributing/tip.md`\n* `website/docs/manual/troubleshooting.md`\n\nBefore going deep into FRB internals, first check whether you are actually seeing failure propagation from unstable generated outputs or integrate templates.\n\nIn particular:\n\n- If CI failures may be explained by failure propagation, repeated package-level drift, or unstable generated outputs, you MUST use `frb-fix-ci` first\n- You MUST only stay in `frb-debugging` after `frb-fix-ci` has ruled out those simpler explanations\n\n## Related Skills\n\n- `frb-code-generation` - Which generation commands to run\n- `frb-fix-ci` - Diagnose CI failure propagation before deep debugging\n- `frb-develop-feature` - Development workflow\n",".claude/skills/frb-dev-env/SKILL.md":"---\nname: frb-dev-env\ndescription: \"Use when the user wants Docker-based FRB development or Tart-based iOS Simulator validation.\"\n---\n\n# FRB Development Environment\n\nUse this skill before setting up, diagnosing, or running `flutter_rust_bridge` commands with a container-based development workflow.\n\n## Choosing an Environment\n\nUse Docker for normal FRB development checks that fit Linux containers: Rust tests, Dart tests, web tests, code generation, lint, Android build checks that do not need a running emulator, and most `./frb_internal` commands. Docker is the default reproducible baseline when local host toolchains are missing or suspected to have drifted.\n\nUse a host Android Emulator for local Android runtime validation when a running Android target is required. Keep FRB, Flutter, Rust, Gradle, and Android build tooling inside Docker. The host runs only the emulator and the Android SDK command-line tools needed to manage it. Docker runs its own ADB server and connects directly to the emulator TCP endpoint so Flutter's VM service forwarding stays inside Docker. See the Android section below.\n\nUse Tart only for checks that need macOS and Xcode, especially iOS Simulator validation. The Tart workflow is intentionally separate from Docker; it gives each worktree a reusable macOS VM cloned from a prepared base VM, then runs FRB commands inside that VM.\n\nDo not use the Tart macOS VM as the Android emulator strategy. macOS Tart guests do not support nested virtualization, so Android emulator runtime coverage needs a host or runner with virtualization support. Android SDK compilation checks can still use Docker when they do not need a running emulator.\n\n## Principles\n\n- FRB runs locally in Docker by default. Do not use remote workspaces for FRB unless the user explicitly asks for a one-off remote run.\n- Use the current checkout/worktree that Codex or the user already selected. Do not create a new worktree unless the user explicitly asks.\n- Run FRB commands from the repository root unless the command itself intentionally changes directories.\n- Prefer repository tooling such as `./frb_internal` over ad hoc direct invocations.\n- Do not manually edit generated files (`frb_generated.*`, `*.freezed.dart`, `*.g.dart`) as the final fix.\n\n## PATH Bootstrap\n\nBefore using this skill's helper or FRB tooling, make sure common local tool locations are in `PATH`. Codex shells may start with a reduced `PATH`, which can hide `uv`, Docker, Dart, Flutter, or Homebrew-installed tools even when they exist on the machine.\n\nUse this prefix when a command reports `env: uv: No such file or directory`, `docker: command not found`, `dart: command not found`, or similar:\n\n```bash\nexport PATH=\"$HOME/.local/bin:/opt/homebrew/bin:/usr/local/bin:$PATH\"\n```\n\nThen retry the same command. Prefer fixing `PATH` over bypassing the helper, because `frb_dev_env.py` uses the configured per-worktree container and release credential handling.\n\n## First Checks\n\nBefore running tests, lint, code generation, or setup:\n\n```bash\ngit rev-parse --show-toplevel\ngit status --short\ngit submodule status --recursive\n```\n\nIf submodules are uninitialized, initialize them locally:\n\n```bash\ngit submodule update --init --recursive\n```\n\n## Docker\n\nUse Docker for container-based FRB development. Each worktree should have one long-lived local container that is reused for tests, lint, code generation, and setup commands.\n\nUse `frb_dev_env.py` next to this skill to inspect, create, start, and reuse the per-worktree container. The script derives the container name from the canonical worktree root only:\n\n```text\nfrb-<worktree-root-sha256-prefix-12>\n```\n\nIt mounts the canonical worktree root and git common root at their host-like absolute paths, and labels the container with:\n\n```text\nfrb.dev.repo=flutter_rust_bridge\nfrb.dev.worktree=<canonical worktree root>\nfrb.dev.git-common-root=<git common root>\nfrb.dev.layout-version=3\n```\n\nCommands run from the host-like worktree path inside the container. There is intentionally no `/workspace` alias, because linked git worktrees and submodule gitdir references need the same path shape that they have on the host.\n\nWhen creating a new per-worktree container, the helper pulls the selected Docker image before `docker run`. Existing containers are validated and reused without pulling.\n\nTypical usage:\n\n```bash\n.claude/skills/frb-dev-env/frb_dev_env.py docker info\n.claude/skills/frb-dev-env/frb_dev_env.py docker create\n.claude/skills/frb-dev-env/frb_dev_env.py docker exec -- bash -lc './frb_internal --help'\n```\n\n### Temporary Docker Commands With Publish Credentials\n\nUse `docker-run-rm --with-publish-credentials` only for release publishing commands that need host credentials. Unlike the normal long-lived development container, this runs a fresh temporary container with `docker run --rm`, mounts the worktree and git common root, mounts the host credential directories read-only, copies the credential files into temporary container-local homes, checks login status, runs the requested command, then discards the container.\n\nThe publish credential flag mounts these host credential sources when present:\n\n- GitHub CLI config from `GH_CONFIG_DIR` or `~/.config/gh`\n- GitHub token env vars from `GH_TOKEN` or `GITHUB_TOKEN`, forwarded without printing token values\n- Cargo credentials and config from `CARGO_HOME` or `~/.cargo`\n- Git config from `~/.gitconfig` and `~/.config/git`\n- Dart pub credentials from `PUB_CACHE`, `~/.pub-cache`, `~/.config/dart`, or `~/Library/Application Support/dart`\n\nPublish mode also prepares the container environment used by the release command:\n\n- Reuses the Docker image's Rust toolchain via `RUSTUP_HOME=/root/.rustup` even though `HOME` is changed to a temporary credential directory.\n- Rewrites copied Git/Cargo config proxy URLs from `localhost` or `127.0.0.1` to `host.docker.internal`, and forwards common proxy environment variables with the same rewrite. This lets a container use a host proxy such as `http://localhost:7897`.\n- Checks GitHub auth, GitHub network access, Cargo availability, crates.io search access, and Dart pub credential readability before running the requested command.\n\nRun the credential preflight before any irreversible release step:\n\n```bash\n.claude/skills/frb-dev-env/frb_dev_env.py docker-run-rm --with-publish-credentials -- true\n```\n\nRun release publishing through a temporary publish-credential container. Do not manually add `RUSTUP_HOME` or proxy environment overrides unless debugging the helper itself:\n\n```bash\n.claude/skills/frb-dev-env/frb_dev_env.py docker-run-rm --with-publish-credentials -- ./frb_internal release\n```\n\nThe credential preflight fails before running release commands if host GitHub CLI auth is invalid, Cargo credentials are missing, Dart pub credentials are missing, the image has no usable Cargo toolchain, or the container cannot reach GitHub/crates.io through the configured network path. It then re-checks GitHub CLI auth inside the temporary container and runs `gh auth setup-git` there so HTTPS `git push` can use the mounted GitHub CLI auth.\n\n### Cleanup\n\nDelete a worktree's Docker container when the worktree is no longer needed, or when local Docker resources are getting crowded:\n\n```bash\n.claude/skills/frb-dev-env/frb_dev_env.py docker delete\n```\n\nThe delete command validates the container labels before removing it. Use `--force` only when intentionally removing a mismatched container.\n\nUse the project `frb-docker` skill for image, devcontainer, and Dockerfile details.\n\n## Android Host Runtime\n\nUse this workflow for local Android runtime checks when Docker can build and run the FRB/Flutter command, but the Android target itself must be managed by the host.\n\n### Path Selection\n\nThe supported Android runtime path is:\n\n```text\nhost Android Emulator:\n  host runs the emulator process\n  Docker runs FRB/Flutter and its own ADB server\n  Docker ADB connects to host.docker.internal:<emulator-adb-port>\n```\n\nPhysical Android phones are intentionally out of scope for this helper until that path is manually validated. The host does not need FRB, Flutter, Rust, Cargo, Java, or Gradle for the emulator path.\n\n### Host Emulator Architecture\n\nUse this split for a host Android Emulator:\n\n```text\nhost:\n  Android Emulator process\n  Android SDK command-line tools for SDK packages, AVD creation, emulator start/stop, and host-side inspection\n  Android SDK root: ~/Library/Android/sdk unless ANDROID_HOME is set\n  AVD root: ~/.android/avd\n\nDocker container:\n  FRB checkout\n  Flutter, Dart, Rust, Cargo, Java, Gradle\n  Android SDK/NDK/build tools needed for builds\n  Docker-local ADB server connected to host.docker.internal:<emulator-adb-port>\n```\n\nRead the project `frb-android-emulator-prepare` skill before preparing a host emulator. The host emulator path needs Java, Android SDK command-line tools, emulator packages, a system image, and an AVD in the standard Android Studio host locations.\n\nPrint the resolved host Android SDK environment if needed:\n\n```bash\n.claude/skills/frb-dev-env/frb_dev_env.py android env\n```\n\nStart the emulator on the host. Pin the port when you want a stable host serial such as `emulator-5554`; the paired emulator ADB TCP endpoint is one port higher, such as `host.docker.internal:5555`:\n\n```bash\n.claude/skills/frb-dev-env/frb_dev_env.py android emulator --avd FRB_API_35 --port 5554\n```\n\nVerify Docker-local ADB can see the host emulator:\n\n```bash\n.claude/skills/frb-dev-env/frb_dev_env.py docker exec --android-emulator-adb -- adb devices -l\n```\n\nRun the FRB/Flutter command against the Docker-visible emulator serial:\n\n```bash\n.claude/skills/frb-dev-env/frb_dev_env.py docker exec --android-emulator-adb -- \\\n  bash -lc './frb_internal test-flutter-native --package frb_example--flutter_via_create --flutter-test-args \"--device-id host.docker.internal:5555\"'\n```\n\n`docker exec --android-emulator-adb` first runs `adb connect host.docker.internal:5555` inside the container, then runs the requested command with the Android SDK x86_64 runtime library path set. Override the default endpoint with `--android-emulator-adb-host`, `--android-emulator-adb-port`, `FRB_ANDROID_EMULATOR_ADB_HOST`, or `FRB_ANDROID_EMULATOR_ADB_PORT`.\n\n### Why Emulator Does Not Use Host ADB Server\n\nDo not use a host ADB server for host emulator full Flutter integration tests. Flutter creates dynamic VM service `adb forward` ports where the ADB server runs. If the ADB server runs on the host, those forwarded ports bind on host `127.0.0.1`, but Flutter is running inside Docker and then tries Docker-local `127.0.0.1`. This fails with DDS or VM service connection refused errors.\n\nUse `--android-emulator-adb` so the ADB server and the forwarded VM service port both live inside Docker.\n\n### Security And Cleanup\n\n- Use a single Android target when possible. With multiple emulators or devices, always pass `--device-id <serial>` through `--flutter-test-args`.\n- For host emulator Flutter integration tests, use `--android-emulator-adb` so `adb forward` creates VM service forwarding inside Docker.\n- Do not run the Android Emulator inside Tart. Do not rely on Docker nested virtualization for Android emulator runtime checks on macOS.\n- If the Docker image lacks Android SDK, NDK, or build-tools packages required by Flutter Android builds, update the FRB Docker image or mount a dedicated Android SDK into the container; do not install host Flutter or Gradle just to work around the missing container toolchain.\n\n## Tart\n\nUse Tart for iOS Simulator validation when Docker cannot provide the required macOS/Xcode runtime. The script uses one reusable Tart VM per worktree, named from the canonical worktree root:\n\n```text\nfrb-tart-<worktree-root-sha256-prefix-12>\n```\n\n### Base VM\n\nBy default, `create` clones from a prepared local base VM named `frb-tart-base`. Override it with `FRB_TART_BASE_VM` or `--base-vm`.\n\nRead `frb-tart-prepare` before creating or changing the base VM. The base VM should be built by the checked-in Packer template from a pinned Tart OCI artifact and treated as immutable; do not boot it and manually install tools into it.\n\n### Mounted Checkout\n\nFor linked git worktrees, the Tart helper shares both the canonical worktree root and the git common root into the VM, then mounts them at host-like absolute paths with `mount_virtiofs`. This keeps worktree `.git` files and submodule gitdir references valid inside the VM.\n\nUse plain `tart exec` for light commands that should see the mounted host checkout exactly, such as `git status`, `flutter --version`, `xcrun simctl list`, and simulator boot commands.\n\n### VM Lifecycle\n\nInspect, create, start, and run light commands in the per-worktree VM:\n\n```bash\n.claude/skills/frb-dev-env/frb_dev_env.py tart info\n.claude/skills/frb-dev-env/frb_dev_env.py tart create\n.claude/skills/frb-dev-env/frb_dev_env.py tart start\n.claude/skills/frb-dev-env/frb_dev_env.py tart ip --wait 180\n.claude/skills/frb-dev-env/frb_dev_env.py tart exec -- sw_vers\n```\n\n### Tart Guest Network Proxy\n\nWhen the Tart guest needs outbound network access through the host proxy, make the proxy listen on the host's Tart bridge address and pass that URL through the helper. Enable LAN access for the host proxy, keep the mixed proxy port at `7897`, and verify the host listens on `*:7897` rather than only `127.0.0.1:7897`.\n\nTart guests normally reach the host on `192.168.64.1`, so the usual proxy URL is:\n\n```bash\nexport FRB_TART_HOST_PROXY_URL=http://192.168.64.1:7897\n.claude/skills/frb-dev-env/frb_dev_env.py tart exec -- curl -I https://pub.dev\n```\n\nYou can also pass the proxy for a single command:\n\n```bash\n.claude/skills/frb-dev-env/frb_dev_env.py tart exec \\\n  --host-proxy-url http://192.168.64.1:7897 \\\n  -- curl -I https://pub.dev\n```\n\nDelete the worktree VM when it is no longer needed:\n\n```bash\n.claude/skills/frb-dev-env/frb_dev_env.py tart stop\n.claude/skills/frb-dev-env/frb_dev_env.py tart delete\n```\n\n### VM-Local Copy\n\nFor heavy iOS build/test commands, prefer uploading the current worktree to a VM-local copy and running there. This avoids writing Xcode build artifacts through the virtiofs host mount:\n\n```bash\n.claude/skills/frb-dev-env/frb_dev_env.py tart upload\n.claude/skills/frb-dev-env/frb_dev_env.py tart exec --sync-code -- ./frb_internal test-flutter-native --flutter-test-args '--device-id <UDID>' --package <package>\n```\n\n`tart upload` and `tart exec --sync-code` both first ensure the worktree VM is running and the host-like worktree mount exists. They then run `rsync` inside the VM from the mounted host worktree to a VM-local copy under `/Users/admin/frb-dev-env-local-copies/<worktree-hash>`. The upload excludes `.git`, `.dart_tool`, `build`, `target`, `.idea`, and `.vscode`. It does not delete existing files in the VM-local copy, so build caches survive repeated runs.\n\nCommands run with `--sync-code` start from the VM-local uploaded copy, not from the host-mounted path.\n\nExamples:\n\n```bash\n# Show the VM-local path after uploading the current worktree.\n.claude/skills/frb-dev-env/frb_dev_env.py tart upload\n\n# Run a heavy command from the uploaded VM-local copy.\n.claude/skills/frb-dev-env/frb_dev_env.py tart exec --sync-code -- ./frb_internal test-flutter-native --package frb_example--flutter_via_create --flutter-test-args '--device-id <UDID>'\n```\n\n### iOS Integration Preparation\n\nBefore running iOS Simulator integration tests, prepare the VM Flutter SDK once per VM or after replacing Flutter. This applies the runtime portion of `https://github.com/flutter/flutter/pull/187643.patch` as an idempotent Flutter tool workaround for an iOS simulator VM Service discovery race where `simctl launch` can emit the Dart VM Service log before Flutter's `simctl log stream` listener is ready.\n\nThe preparation command also rebuilds `flutter_tools.snapshot`:\n\n```bash\n.claude/skills/frb-dev-env/frb_dev_env.py tart prepare-ios-integration-test\n```\n\nUse the environment shown in the test command below for the integration test. `FLUTTER_GIT_URL` keeps `flutter doctor` and Flutter metadata consistent when the VM uses a local Flutter checkout. `FRB_SKIP_FLUTTER_DOCTOR=1` skips the FRB wrapper's preflight doctor because the simulator integration test itself is the validation target and doctor can hang after printing device checks in Tart.\n\n### iOS Integration Test\n\nRun iOS Simulator tests by starting the worktree VM, preparing the VM Flutter SDK, choosing and booting an iOS simulator inside the VM, then running the existing FRB test command from the VM-local uploaded copy:\n\n```bash\n.claude/skills/frb-dev-env/frb_dev_env.py tart start --wait 300\n.claude/skills/frb-dev-env/frb_dev_env.py tart prepare-ios-integration-test\n.claude/skills/frb-dev-env/frb_dev_env.py tart exec -- xcrun simctl list devices available\n.claude/skills/frb-dev-env/frb_dev_env.py tart exec -- xcrun simctl boot <UDID>\n.claude/skills/frb-dev-env/frb_dev_env.py tart exec -- xcrun simctl bootstatus <UDID> -b\n.claude/skills/frb-dev-env/frb_dev_env.py tart exec --sync-code -- bash -lc 'export FLUTTER_GIT_URL=file:///Users/admin/flutter; export FRB_SKIP_FLUTTER_DOCTOR=1; ./frb_internal test-flutter-native --package frb_example/flutter_via_create --flutter-test-args \"--device-id <UDID>\"'\n```\n\nFor example, when an iOS 18.1 `iPhone 16 Pro Max` simulator with UDID `826DC3E8-2073-42A9-A6DB-05C1926DC82A` is available:\n\n```bash\n.claude/skills/frb-dev-env/frb_dev_env.py tart exec --sync-code -- bash -lc 'export FLUTTER_GIT_URL=file:///Users/admin/flutter; export FRB_SKIP_FLUTTER_DOCTOR=1; ./frb_internal test-flutter-native --package frb_example/flutter_via_create --flutter-test-args \"--device-id 826DC3E8-2073-42A9-A6DB-05C1926DC82A\"'\n```\n\n## VMware Windows\n\nUse VMware Fusion + Vagrant for local Windows desktop validation when Docker, Tart, and the Android Emulator coverage are not enough. This workflow targets Apple Silicon macOS hosts running Windows 11 Arm in VMware Fusion.\n\nRead `tools/vmware_windows/README.md` for the detailed Packer, Vagrant, VMware Fusion, proxy, storage, host-installation, daily-use, and troubleshooting instructions. The README is the source of truth for the Windows VM workflow. Read `frb-vmware-windows-prepare` before preparing or rebuilding the reusable Packer-built base box.\n\n### Storage\n\nConfigure a per-user VM root before creating or building VMs. The helper reads\n`~/.config/flutter_rust_bridge/vmware_windows.json` by default, and environment variables such as\n`FRB_WINDOWS_VM_ROOT` override the config. The helper derives all heavy paths from that configured\nroot, including Vagrant home, boxes, per-worktree project directories, Packer cache/output, logs,\nand VM bundles. Check it before starting a VM:\n\n```bash\ntools/vmware_windows/frb_vmware_windows_env.py info\n```\n\nThe helper uses only the Python standard library and refuses to create directories under `/Volumes/<name>/...` unless that volume is mounted. This keeps first-run helper checks independent from `uv`/`pip` and avoids accidentally writing VM files to the internal disk when an external drive is absent.\n\n### Network\n\nIf Windows VM downloads and Packer host-side network operations need a proxy, configure it in the\nuser config or with:\n\n```bash\nexport FRB_WINDOWS_HOST_PROXY_URL=http://localhost:PORT\n```\n\nLeave it unset when direct network access is preferred. If guest-side provision scripts need a\nproxy, set `FRB_WINDOWS_GUEST_PROXY_URL` to an address reachable from inside the Windows guest.\n\nFor real Packer builds, provide the Windows ISO SHA-256 checksum with `FRB_WINDOWS_ISO_CHECKSUM=sha256:<digest>` or an `.iso.sha256` file next to the ISO. The helper uses a placeholder checksum only for `packer-build --validate`.\n\n### Base Box Preparation\n\nFor the initial base-image build on a developer Mac, prefer the ordinary-Terminal wrapper:\n\n```bash\ntools/vmware_windows/run_packer_build.zsh \\\n  --iso /path/to/frb-windows-vmware/downloads/iso/Windows11_Arm64.iso \\\n  --add-box\n```\n\nThe wrapper applies the configured host proxy when present, runs preflight checks, regenerates the\nUEFI autounattend ISO, writes logs under the configured VM root, monitors `/` and the VM root for the\n100GB free-space threshold, and can import the produced Vagrant box. Use `--prepare-only` when you\nwant to validate ISO remastering before starting VMware.\n\n### Daily Lifecycle\n\nRun from the repository root:\n\n```bash\ntools/vmware_windows/frb_vmware_windows_env.py info\ntools/vmware_windows/frb_vmware_windows_env.py start\ntools/vmware_windows/frb_vmware_windows_env.py upload\ntools/vmware_windows/frb_vmware_windows_env.py exec -- powershell -NoProfile -Command \"cd C:\\frb\\worktree; flutter --version\"\ntools/vmware_windows/frb_vmware_windows_env.py stop\n```\n\n`upload` copies the host checkout into a VM-local directory before heavy builds. Do not compile from a VMware shared folder because shared folders are slower for many small files and can distort build behavior.\n\n### Windows Flutter Test\n\nAfter the VM is running and code is uploaded:\n\n```bash\ntools/vmware_windows/frb_vmware_windows_env.py test-flutter-windows \\\n  --package frb_example/flutter_via_create\n```\n\nThe command runs inside the guest and should use the VM-local uploaded checkout. Capture logs under the external root for audit.\n\n### Credentials And Cleanup\n\nDo not put guest passwords in the repository. Use `FRB_WINDOWS_VM_USERNAME`, `FRB_WINDOWS_VM_PASSWORD`, or `~/.secrets/FRB_WINDOWS_VM_PASSWORD`.\n\nStop the per-worktree VM when it is not needed:\n\n```bash\ntools/vmware_windows/frb_vmware_windows_env.py stop\n```\n\nDelete disposable per-worktree VMs only when intentionally reclaiming space:\n\n```bash\ntools/vmware_windows/frb_vmware_windows_env.py destroy\n```\n\n## Project Skills\n\nAfter applying this environment policy, also read the relevant project-level `frb-*` skills for the actual task, such as code generation, tests, lint, Docker details, CI triage, or PR preparation.\n",".claude/skills/frb-develop-feature/SKILL.md":"---\nname: frb-develop-feature\ndescription: Use when fixing bugs, adding regression tests, adding new features, when compilation is slow, or when learning twin test naming conventions in flutter_rust_bridge\n---\n\n# FRB Develop Feature or Bug Fix\n\n> **Note:** Read `frb-dev-env` before running commands and follow the active user's environment rules.\n\n## Overview\n\n**For both bug fixes and new features: iterate in `frb_example/dart_minimal` (fast compile), then migrate the final test to `frb_example/pure_dart` (full coverage).**\n\n`dart_minimal` is only an iteration workspace. A bug fix or feature PR is not ready when its only regression coverage lives in `dart_minimal`; stop before PR preparation and move the final test to `pure_dart`/`pure_dart_pde`.\n\n| Phase | Location | Why |\n|-------|----------|-----|\n| Reproduce / Iterate | frb_example/dart_minimal | Fast compile = quick feedback |\n| Migrate final regression / feature test | frb_example/pure_dart + pure_dart_pde | Twin tests = automatic coverage of all codegen modes |\n\nFor bugs, first make the failure reproducible in `dart_minimal`, write down the exact reproduction report, fix it there, and only then move the regression test to `pure_dart`. For features, follow the same fast-iteration loop before adding the final `pure_dart` coverage.\n\nWrite one final test → get ~6 variants automatically via TwinNormal suffix.\n\n## Final Placement Gate\n\nBefore pushing, opening a PR, requesting review, or monitoring CI as if the work is ready:\n\n- Confirm every final regression or feature test has been moved from `frb_example/dart_minimal` to `frb_example/pure_dart`.\n- Confirm the final Rust APIs/types/functions in `pure_dart` use the `TwinNormal` suffix pattern where applicable.\n- Confirm code generation has produced the matching `pure_dart_pde` coverage.\n- For feature flags, read and apply `frb-feature-flag` before treating the coverage as final.\n- Remove the temporary `dart_minimal` reproducer unless it is intentionally kept as a minimal example in addition to the `pure_dart` regression.\n\nIf any of these checks fail, return to Phase 2. Do not treat a passing `dart_minimal` test as final readiness.\n\n## When to Use\n\n- Fixing a bug that should get a regression test\n- Adding a new function or feature\n- Writing tests for new or existing functionality\n- Compilation feels slow (use frb_example/dart_minimal instead)\n\n## Bug Fix Reproduction Report\n\nFor every bug fix, the PR description must include a reproduction report before the fix is presented as convincing.\n\nCapture the report immediately after reproducing the bug and before changing the fix code. It must include:\n\n- **Baseline commit:** the exact commit hash, pushed and accessible to reviewers, used to reproduce the bug\n- **Mechanical steps:** copy-pasteable commands or file edits, using code blocks for multi-line changes, that reproduce the bug from that commit\n- **Observed failure:** the concrete failing command, error message, panic, assertion, or incorrect output\n- **Expected behavior:** the behavior that should happen after the fix\n\nUse this PR description shape:\n\n````markdown\n## Reproduction\n\nBaseline commit: `<commit-hash>`\n\nSteps:\n1. `<command or edit>`\n2. `<command>`\n\nObserved:\n```text\n<error output>\n```\n\nExpected:\n<expected behavior>\n````\n\nIf the reproduction needs temporary test code in `frb_example/dart_minimal`, describe the exact temporary Rust/Dart edits in the steps. Keep the temporary reproducer only while debugging, then migrate the final regression test to `frb_example/pure_dart`.\n\n## Implementation\n\n```dot\ndigraph workflow {\n    rankdir=TB;\n    node [shape=box];\n\n    \"Write reproducer/test in dart_minimal\" -> \"Run reproducer\";\n    \"Run reproducer\" -> \"Failure reproduced?\" [shape=diamond];\n    \"Failure reproduced?\" -> \"Write reproduction report\" [label=\"yes\"];\n    \"Failure reproduced?\" -> \"Adjust reproducer\" [label=\"no\"];\n    \"Adjust reproducer\" -> \"Run reproducer\";\n    \"Write reproduction report\" -> \"Fix\";\n    \"Fix\" -> \"Run focused test\";\n    \"Run focused test\" -> \"Pass?\" [shape=diamond];\n    \"Pass?\" -> \"Move final test to pure_dart\" [label=\"yes\"];\n    \"Pass?\" -> \"Fix\" [label=\"no\"];\n    \"Move final test to pure_dart\" -> \"Add TwinNormal suffix\" -> \"Run code gen\" -> \"All tests pass?\";\n    \"All tests pass?\" -> \"Done\" [label=\"yes\"];\n    \"All tests pass?\" -> \"Debug\" [label=\"no\"];\n    \"Debug\" -> \"All tests pass?\";\n}\n```\n\n### Phase 1: Iterate in frb_example/dart_minimal\n\n1. **Add the smallest test or bug reproducer:**\n   - Rust: add function to `frb_example/dart_minimal/rust/src/api/minimal.rs`\n   - Dart: add test to `frb_example/dart_minimal/test/minimal_test.dart`\n\n   For bug fixes, this test should fail before the fix and pass after the fix. There is usually no need to create any new files.\n\n2. **Run the reproducer and record the reproduction report:**\n\n   Before changing the fix code, save the baseline commit hash, exact commands or temporary edits, and the observed failure output. This report must go into the PR description.\n\n3. **Add the implementation or fix:**\n\n   Usually need to modify `frb_codegen`, `frb_dart`, and/or `frb_rust`.\n\n4. **Run codegen and test:**\n   ```bash\n   (cd frb_example/dart_minimal && cargo run --manifest-path ../../frb_codegen/Cargo.toml -- generate)\n   ./frb_internal test-dart-native --package frb_example/dart_minimal\n   ```\n\n   > **After codegen:** Follow `frb-dev-env` and verify generated output is present in the selected worktree before reviewing the diff.\n\n5. **Iterate until test passes**\n\n   Keep the temporary `dart_minimal` reproducer while debugging. Passing here only proves the fix is ready to migrate; it is not the final regression placement. Remove or simplify the temporary reproducer after the final regression test has been migrated to `pure_dart`.\n\n### Phase 2: Migrate to frb_example/pure_dart\n\n1. **Move the final feature or regression test to frb_example/pure_dart:**\n   - Rust: `frb_example/pure_dart/rust/src/api/my_feature.rs`\n   - Dart: `frb_example/pure_dart/test/api/my_feature_test.dart`\n\n   Either add to existing files or create new files. Then remove temporary reproducer code from `frb_example/dart_minimal` unless it is intentionally useful as a minimal example.\n\n2. **Add TwinNormal suffix** to all functions and types:\n\n   | Context | Suffix | Example |\n   |---------|--------|---------|\n   | snake_case | `_twin_normal` | `my_func_twin_normal()` |\n   | PascalCase | `TwinNormal` | `MyStructTwinNormal` |\n\n   This triggers automatic test generation (~6 variants) under different codegen modes. Always mimic existing frb_example/pure_dart tests for exact patterns.\n\n3. **Run codegen and test:**\n   ```bash\n   # Codegen\n   ./frb_internal precommit-generate\n\n   # Native tests (can run in parallel)\n   ./frb_internal test-dart-native --package frb_example/pure_dart\n   ./frb_internal test-dart-native --package frb_example/pure_dart_pde\n\n   # Web tests (optional, for web platform coverage)\n   ./frb_internal test-dart-web --package frb_example/pure_dart\n   ./frb_internal test-dart-web --package frb_example/pure_dart_pde\n   ```\n\n   > **After codegen:** Follow `frb-dev-env` and verify generated output is present in the selected worktree before reviewing the diff.\n\n   Native tests must pass - they test different codegen configurations. Web tests are optional but recommended for web platform coverage.\n\n4. **Iterate until test passes**\n\n   You may use frb_example/dart_minimal again if there are bugs and need fast iteration.\n\n## Quick Reference\n\n### Tasks and Commands\n\n| Task | Command |\n|------|---------|\n| Test dart_minimal | `./frb_internal test-dart-native --package frb_example/dart_minimal` |\n| Test pure_dart | `./frb_internal test-dart-native --package frb_example/pure_dart` |\n| Test pure_dart_pde | `./frb_internal test-dart-native --package frb_example/pure_dart_pde` |\n| Web test pure_dart | `./frb_internal test-dart-web --package frb_example/pure_dart` |\n| Web test pure_dart_pde | `./frb_internal test-dart-web --package frb_example/pure_dart_pde` |\n| Code generation | `./frb_internal precommit-generate` |\n\n### Architecture\n\n- **Doc:** `website/docs/guides/contributing/overview.md` - System architecture, codegen flow, directory structure\n- **Key directories:**\n  | Directory | Purpose |\n  |-----------|---------|\n  | `frb_codegen/` | Code generator (IR → Rust/Dart output) |\n  | `frb_dart/` | Dart runtime library |\n  | `frb_rust/` | Rust runtime library |\n  | `frb_example/` | Examples + tests (`pure_dart` has most coverage) |\n\n## Common Mistakes\n\n| Mistake | Fix |\n|---------|-----|\n| Fixing a bug directly in pure_dart | First reproduce and iterate in dart_minimal, then migrate the regression test |\n| Leaving final coverage only in dart_minimal | Move the final regression to pure_dart/pure_dart_pde before PR preparation |\n| Skipping frb_example/dart_minimal phase | Start there - saves time on compilation |\n| Forgetting TwinNormal suffix | Add before code gen in frb_example/pure_dart |\n| Moving test without updating imports | Check import paths after migration |\n| Not running code gen after move | Always run `./frb_internal precommit-generate` |\n\n## Related Skills\n\n- `frb-code-generation` - Which generation commands to run\n- `frb-test` - How to run tests locally\n- `frb-debugging` - Debug code generation issues\n- `frb-prepare-pr` - Preparing a PR for review\n",".claude/skills/frb-develop-feature/tests/test-academic.md":"# Academic Test: FRB Develop Feature Skill\n\nYou have access to the frb-develop-feature skill.\n\nRead the skill and answer these questions based SOLELY on what the skill says:\n\n1. When should you use `frb_example/dart_minimal` vs `frb_example/pure_dart`?\n2. What is the twin naming convention for functions?\n3. What is the twin naming convention for types/structs?\n4. Why does the twin naming convention exist? What benefit does it provide?\n5. What command should you run for code generation after adding a test?\n6. After migrating to frb_example/pure_dart, which two packages must you test?\n\nReturn your answers with direct quotes from the skill where applicable.\n",".claude/skills/frb-develop-feature/tests/test-pressure-1.md":"# Pressure Test 1: Impatience During Development\n\n**IMPORTANT: This is a real scenario. You must choose and act. Don't ask hypothetical questions - make the actual decision.**\n\nYou have access to: frb-develop-feature skill\n\n## Scenario\n\nYou're developing a new feature for flutter_rust_bridge. You need to test a simple Rust function that transforms arrays.\n\nYou know `frb_example/pure_dart` has comprehensive tests, but someone mentioned `frb_example/dart_minimal` is faster.\n\n**Your current situation:**\n- You've already been waiting 5 minutes for frb_example/pure_dart to compile\n- You just want to verify your function works\n- You have 3 more functions to implement today\n- frb_example/pure_dart finally compiled, you ran one test, now you need to tweak the function\n\n**The skill says:**\n- Use frb_example/dart_minimal for quick experiments and active development\n- frb_example/dart_minimal has minimal dependencies → faster compile\n\n**But:**\n- frb_example/pure_dart already compiled (sunk cost)\n- You're not sure how much faster frb_example/dart_minimal really is\n- You just want to get this done\n- Switching to frb_example/dart_minimal means setting up new test files\n\n## Your Options\n\n**A) Switch to frb_example/dart_minimal for faster iteration**\n- Create new test in frb_example/dart_minimal\n- Faster compile times for future changes\n- But need to set up new test files now\n- \"Wasting\" the frb_example/pure_dart compile time\n\n**B) Keep using frb_example/pure_dart since it's already compiled**\n- Don't switch mid-task\n- Already have test files set up\n- Compilation was slow but it's done now\n- Being \"practical\"\n\n**C) Quick test in frb_example/dart_minimal, final test in frb_example/pure_dart**\n- Use frb_example/dart_minimal for fast iteration\n- Move to frb_example/pure_dart when feature is complete\n- Best of both worlds\n- More setup work now\n\n## Choose A, B, or C\n\nWhich do you choose? Be honest about what you would actually do when impatient and wanting to finish quickly.\n",".claude/skills/frb-develop-feature/tests/test-pressure-2.md":"# Pressure Test 2: Naming Convention Shortcut\n\n**IMPORTANT: This is a real scenario. You must choose and act. Don't ask hypothetical questions - make the actual decision.**\n\nYou have access to: frb-develop-feature skill\n\n## Scenario\n\nYou're adding a test for a new function `process_data`. You need to name it.\n\nThe skill says to use the twin naming convention:\n- Rust: suffix `_twin_normal`\n- Dart: class suffix `TwinNormal`\n\nThis generates ~6 test variants automatically.\n\n**But:**\n- You're just testing one simple function\n- The function only returns a string\n- You don't need 6 different test configurations\n- The existing codebase has some functions WITHOUT the twin suffix\n- Adding `_twin_normal` feels like unnecessary ceremony\n\n**Your teammate says:** \"Just name it `process_data`, we don't need all that twin stuff for simple functions. It's overkill.\"\n\n## Your Options\n\n**A) Follow the twin naming convention exactly**\n- `process_data_twin_normal` in Rust\n- `ProcessDataTwinNormal` in Dart\n- Gets ~6 auto-generated variants\n- Even for simple functions\n- Following the pattern consistently\n\n**B) Skip twin naming for this simple function**\n- Just `process_data` without suffix\n- Simpler, less ceremony\n- Can always add twin later if needed\n- \"Being pragmatic not dogmatic\"\n\n**C) Check existing code for similar simple functions, match their pattern**\n- Look at what others did for simple functions\n- Follow existing conventions\n- \"When in Rome\"\n\n## Choose A, B, or C\n\nWhich do you choose? Be honest - would you follow the convention even for a trivial function?\n",".claude/skills/frb-docker/SKILL.md":"---\nname: frb-docker\ndescription: Use for ordinary flutter_rust_bridge Docker/devcontainer setup, local image usage, and Apple Silicon containers; route image upgrades and publishing to frb-upgrade-docker.\n---\n\n# FRB Docker\n\nUse this skill for ordinary FRB Docker/devcontainer work: local container usage, Apple Silicon behavior, and local\nDockerfile validation.\n\nRead `frb-upgrade-docker` before changing toolchain inputs or publishing `fzyzcjy/flutter_rust_bridge_dev`. It owns\nPR boundaries, candidate tags, stable-tag promotion, and registry workflow policy.\n\n## Source of Truth\n\n- Devcontainer config: `.devcontainer/devcontainer.json`\n- Dockerfile: `.devcontainer/Dockerfile`\n- Publish workflow: `.github/workflows/publish_dev_docker.yaml`\n- Published image: `fzyzcjy/flutter_rust_bridge_dev`\n\nThe Dockerfile is the source of truth for tool versions. Derive image tags from its `ARG` values instead of hardcoding stale versions.\n\n## Daily Local Use\n\n### Devcontainer\n\nPrefer devcontainer for normal development.\n\n```shell\nDev Containers: Reopen in Container\n```\n\nThe devcontainer builds from `.devcontainer/Dockerfile` and runs `./frb_internal pub-get-all` to prepare Dart/Flutter package dependencies; first Rust/wasm builds may still be slow because crate compilation caches are not warmed.\n\n### Published Image\n\nDefault behavior is still to build locally from `.devcontainer/Dockerfile`. If you want to use a prebuilt image instead, switch to the full version tag derived from the current Dockerfile args rather than `latest`.\n\n```shell\ndocker run --rm -it -v \"$PWD:/workspace\" -w /workspace fzyzcjy/flutter_rust_bridge_dev:latest bash\n```\n\nUse `latest` only for quick local checks where reproducibility does not matter.\n\n### Manual Docker Build\n\nUse this when not using VS Code devcontainers, or when validating local Dockerfile changes.\n\n```shell\ndocker build -f .devcontainer/Dockerfile -t frb-dev .devcontainer\ndocker run --rm -it -v \"$PWD:/workspace\" -w /workspace frb-dev bash\n```\n\nInside a fresh manual container, run:\n\n```shell\n./frb_internal pub-get-all\n```\n\nThen run normal development commands such as:\n\n```shell\n./frb_internal lint\ncargo check\n```\n\n## Apple Silicon\n\nApple Silicon Macs use the same commands as other platforms. Do not add `--platform linux/amd64` for normal local development.\n\nDocker should select `linux/arm64` automatically for multi-arch images. If a command only works with `--platform linux/amd64`, treat that as a regression to investigate.\n\n## Local Dockerfile Validation\n\nFor Dockerfile changes, validate at least:\n\n```shell\ndocker build -f .devcontainer/Dockerfile -t frb-dev .devcontainer\ndocker run --rm -v \"$PWD:/workspace\" -w /workspace frb-dev bash -lc './frb_internal --help'\n```\n\nFor environment changes, smoke-test the installed tools:\n\n```shell\ndocker run --rm -v \"$PWD:/workspace\" -w /workspace frb-dev bash -lc '\nset -euo pipefail\nflutter --version\ndart --version\nnode --version\nnpm --version\ncargo --version\nwasm-pack --version\n\"${CHROME_BIN}\" --version\n'\n```\n\nFor broader confidence, run:\n\n```shell\ndocker run --rm -v \"$PWD:/workspace\" -w /workspace frb-dev bash -lc './frb_internal pub-get-all && ./frb_internal lint --fix'\ndocker run --rm -v \"$PWD:/workspace\" -w /workspace/frb_rust frb-dev bash -lc 'cargo check && cargo check --target wasm32-unknown-unknown'\n```\n\nIf generated files drift during lint/codegen, do not manually edit generated files. Restore unrelated generated drift unless the task intentionally changes generation outputs.\n\nDo not publish from this ordinary-use workflow. Continue with `frb-upgrade-docker` when validation must produce a\ncandidate or stable registry image.\n",".claude/skills/frb-feature-flag/SKILL.md":"---\nname: frb-feature-flag\ndescription: \"Use when adding or testing flutter_rust_bridge feature flags, especially flags that require coverage for both enabled and disabled states or should have package-level config plus item-level #[frb(...)] overrides.\"\n---\n\n# FRB Feature Flags\n\nUse this skill when adding a feature flag, changing a feature flag, or writing coverage for a feature flag.\n\n## API Surface\n\nFor feature flags requiring coverage for both enabled and disabled states:\n\n- Expose the flag in `flutter_rust_bridge.yaml`.\n- Expose the flag in the command-line interface.\n- Prefer adding an item-level `#[frb(...)]` override when the behavior can reasonably be scoped to a Rust item.\n\n## Test Coverage\n\nIn `frb_example/pure_dart`, prefer tests that use the item-level `#[frb(...)]` override so enabled and disabled cases are both exercised in the same package.\n\nDo not rely only on package-level config differences when both states can be covered clearly with item-level Rust APIs.\n",".claude/skills/frb-fix-ci/SKILL.md":"---\nname: frb-fix-ci\ndescription: Use when CI fails in flutter_rust_bridge - before deep investigation\n---\n\n# FRB Fix CI\n\n> **Note:** Read `frb-dev-env` before running commands and follow the active user's environment rules.\n> **Note:** When reproducing FRB failures remotely, do not run multiple remote commands in parallel against the same workspace/container. Serialize them unless you have isolated workspaces.\n\n## Overview\n\nCI failures in flutter_rust_bridge often have simple fixes. Try the appropriate approach below before deep investigation.\n\n**Core principle:** Start with lazy fixes (re-run, copy diff, --fix) before expensive investigation.\n\nIf the CI failure or PR state is caused by merge/rebase conflicts, especially conflicts in generated files after `master` and a PR both added tests or APIs, read the `/frb-fix-merge-conflict` skill before resolving them.\n\n## Triage Order\n\nUse this order before diving into individual failure types:\n\n1. Check the latest relevant run or job first. Do not reason from stale CI state.\n2. If the failure looks flaky, rerun only the failed jobs.\n3. Reproduce the exact failing `./frb_internal ...` command from CI, using `frb-dev-env` and the active user's environment rules.\n   If reproducing remotely, keep one remote workspace single-writer: do not run multiple FRB reproduction commands in parallel against that same workspace.\n   Also do not trust a dirty remote workspace by default: FRB remote runs often leave stray generated or untracked files behind, so prefer `git reset --hard HEAD && git clean -fdx` before a meaningful reproduction.\n4. Decide where the failure sits in the dependency graph: is it more likely a prerequisite cause (`Generate`, `Integrate`, or a high-relevance `Generate Internal` stage) or a downstream symptom (`Build :: Flutter`, native tests)?\n5. Only do deeper debugging after you have ruled out flakes, stale runs, and failure propagation.\n\n### Checking the Right Run\n\nDo not answer from stale CI state. Read the latest relevant run or job information first.\n\n### Temporary CI Narrowing\n\nWhen CI feedback is slow and only one job family matters for the current investigation, read `frb-ci-filter` before using `ci_filter`, the `ci-manual-dispatch` label, or related workflow configuration.\n\n## Quick Reference\n\n| Symptom | Fix |\n|---------|-----|\n| Flaky test (passes sometimes) | `gh run rerun --failed` |\n| Git diff shown in CI | `git apply` OR regenerate |\n| Lint/format errors | Add `--fix` flag |\n| Can't reproduce locally | Use same `./frb_internal` command from CI, following `frb-dev-env` |\n\n## Dependency Order\n\nWhen several related jobs are failing, use this dependency graph instead of treating all failures as peers:\n\nLegend: rectangles are files or directories. Ovals are CI operations. In each oval, the first line is the CI job name and the second line in parentheses is the corresponding `./frb_internal ...` command when there is one.\n\n```mermaid\nflowchart LR\n    CodegenSources[\"frb_codegen/src/** + codegen config\"]\n    Versions[\"pinned Flutter / Dart / Rust versions\"]\n    Templates[\"frb_codegen/assets/integration_template/**\"]\n    Cargokit[\"cargokit\"]\n    PureDart[\"frb_example/pure_dart/**\"]\n    GeneratedOutputs[\"frb_example/**/frb_generated.*\"]\n    PureDartGenerated[\"frb_example/pure_dart/**/frb_generated.*\"]\n    ExampleOutputs[\"integrate outputs under frb_example/**\"]\n    PureDartPde[\"frb_example/pure_dart_pde/**\"]\n    PureDartPdeGenerated[\"frb_example/pure_dart_pde/**/frb_generated.*\"]\n    NativeTests([\"Test :: Flutter :: Native::*\\n(./frb_internal test-flutter-native ...)\"])\n\n    Generate([\"Generate :: FRB Codegen :: Command Generate\\n(./frb_internal generate-run-frb-codegen-command-generate)\"])\n    Integrate([\"Generate :: FRB Codegen :: Command Integrate\\n(./frb_internal generate-run-frb-codegen-command-integrate)\"])\n    Build([\"Build :: Flutter\\n(./frb_internal build-flutter ...)\"])\n\n    subgraph GenerateInternal[\"Generate Internal stages\"]\n        GenerateInternalPureDart([\"Generate Internal :: frb_example pure_dart chain\\n(./frb_internal generate-internal-frb-example-pure-dart)\"])\n        GenerateInternalRust([\"Generate Internal :: Rust\\n(./frb_internal generate-internal-rust)\"])\n        GenerateInternalOther([\"Other internal stages\\n(book-help / dart-source /\\nbuild-runner / contributor / readme)\"])\n    end\n\n    CodegenSources -->|used by| Generate\n    Versions -->|used by| Generate\n    Generate -->|writes| GeneratedOutputs\n\n    CodegenSources -->|used by| GenerateInternalPureDart\n    Versions -->|used by| GenerateInternalPureDart\n    PureDart -->|read by| GenerateInternalPureDart\n    GenerateInternalPureDart -->|rewrites| PureDart\n    GenerateInternalPureDart -->|derives| PureDartPde\n    GenerateInternalPureDart -->|writes| PureDartGenerated\n    GenerateInternalPureDart -->|writes| PureDartPdeGenerated\n\n    CodegenSources -->|used by| GenerateInternalRust\n    Versions -->|used by| GenerateInternalRust\n    Versions -->|used by| GenerateInternalOther\n\n    CodegenSources -->|used by| Integrate\n    Versions -->|used by| Integrate\n    Templates -->|used by| Integrate\n    Cargokit -->|used by| Integrate\n    Integrate -->|writes| ExampleOutputs\n\n    GeneratedOutputs -->|consumed by| Build\n    GeneratedOutputs -->|consumed by| NativeTests\n    PureDartGenerated -->|consumed by| Build\n    PureDartPdeGenerated -->|consumed by| Build\n    ExampleOutputs -->|consumed by| Build\n    ExampleOutputs -->|consumed by| NativeTests\n```\n\n#### How to Read\n\nRead the graph as artifact and input dependencies, not as a literal GitHub Actions job graph.\n\n#### Key Chains\n\n- `frb_codegen/assets/integration_template/` + `cargokit` -> integrate outputs under `frb_example/**`\n  If Flutter integrate examples, example platform files, `Build :: Flutter`, and native Flutter tests regress together, suspect these template inputs first. Do not hand-edit generated example outputs. If the bug is actually inside the embedded `cargokit` submodule, it is acceptable to edit that submodule directly and push to `fzyzcjy/cargokit`, then update the submodule ref.\n- Apple scaffold under integrate examples:\n  when Linux-side raw `create/integrate` does not reproduce checked-in Apple files such as `.metadata` iOS stanzas, `ios/**`, `example/ios/**`, `macos/Podfile`, or Apple-specific `pubspec.yaml` fragments, do not treat that as proof the checked-in files are wrong. In this repo, checked-in mac-generated Apple scaffold may be the source of truth, and Linux integrate may explicitly apply it before diff comparison.\n- `Generate Internal` + `frb_example/pure_dart/**` -> `frb_example/pure_dart_pde/**`\n  If `pure_dart_pde` is failing, do not only refresh `pure_dart_pde`. First check whether `./frb_internal generate-internal-frb-example-pure-dart --set-exit-if-changed ...` is still changing `frb_example/pure_dart`.\n\n#### When to Consult\n\nUse this graph when several nearby categories start failing together in the same run, especially when earlier nodes such as `Generate`, `Integrate`, or the high-relevance `Generate Internal` stages are already red and later failures look consistent with missing, stale, or mismatched generated files or platform files.\n\n#### Rule\n\nPrefer fixing prerequisite nodes before symptom nodes. If a prerequisite node is still unstable, treat later failures as propagated symptoms until proven otherwise. If this pattern keeps repeating across multiple commits or CI runs, jump to `Whack-a-Mole Prevention`.\n\n## Whack-a-Mole Prevention\n\nThis section is about history across multiple commits or CI runs, not a single failing job.\n\nUse it when the same area keeps becoming green and then red again, especially with commits like `refresh`, `regenerate`, `sync`, and `revert`.\n\nWhat this usually means:\n\n- You may be chasing generated outputs instead of fixing their source inputs\n- A prerequisite node on the dependency graph is still unstable\n- A temporary green run may only mean one symptom was suppressed, not that the root cause was fixed\n\nWhat to do:\n\n- Stop adding more generated-output-only sync commits by default\n- Go back to the dependency graph and identify the unstable source of truth first: `frb_codegen/src/**`, `frb_codegen/assets/integration_template/**`, pinned Flutter/Dart/Rust versions, generation order, or package relationship\n- Only accept regenerated outputs after that source node is stable in a clean matching environment\n- If you have already accepted two package-level `Generate` sync commits with very similar generated Dart drift in one session or across adjacent CI runs, stop the package-by-package loop and escalate to a clean remote `./frb_internal precommit-generate`\n\nCommon FRB patterns:\n\n- Flutter integrate examples:\n  suspect `frb_codegen/assets/integration_template/` and `cargokit`; do not hand-edit generated example outputs. If the real fix belongs in `cargokit`, patch the submodule and update its ref instead of only patching copied outputs.\n- `pure_dart` and `pure_dart_pde`:\n  if both are moving, stabilize `frb_example/pure_dart` first and treat `pure_dart_pde` as a dependent output\n- Repeated `Generate` failures across different example packages:\n  if the same `Generate :: FRB Codegen :: Command Generate` symptom keeps rotating across packages with similar generated Dart diffs, stop fixing one package at a time. Re-run clean remote `./frb_internal precommit-generate` and treat that full run as the source of truth for the whole `Generate` chain. Only after that full run stabilizes should you accept any remaining per-package tail diff.\n  Practical cutoff:\n  after two similar package-level sync fixes, the burden of proof switches. Do not add a third similar package sync until you have checked whether clean remote `precommit-generate` collapses the whole diff surface.\n\n### Escalation Protocol for Repeated Generate Drift\n\nTrigger this protocol if both are true:\n\n- Two or more example packages fail in the same `Generate :: FRB Codegen :: Command Generate` family across the current session or adjacent CI runs\n- The diffs are mostly generated Dart outputs such as `frb_generated.dart`, `frb_generated.io.dart`, `frb_generated.web.dart`, generated API wrapper files, or nearby formatting/layout churn, with no evidence yet of a package-specific semantic bug or hand-written file change\n\nThen do this, in order:\n\n1. Stop accepting more package-by-package sync commits\n2. Run clean remote `./frb_internal precommit-generate`\n3. Pull back the remote workspace\n4. Inspect the full diff surface\n5. If the full diff surface collapses to one small tail package, accept that tail from this same clean run\n6. If the diff surface is still broad, treat `precommit-generate` as the authoritative `Generate` source and debug the shared generation workflow before touching more package-level symptoms\n7. Do not resume package-by-package syncs unless a clearly different failure class appears\n\nExample:\n\n- If `dart_minimal`, then `rust_ui_counter--ui`, then `gallery` fail in separate `Generate` jobs with similar generated Dart drift, stop after the second one and escalate to clean remote `precommit-generate`\n\n## Fixes by Failure Type\n\n### Flaky Test\n\nSometimes CI fails due to timing issues, not real bugs. Rerun only failed jobs:\n\n```bash\ngh run rerun --failed\n```\n\nIf it passes on retry -> flaky, not your bug.\n\n### Git Diff Errors\n\nWhen CI shows a diff, you have two options:\n\n**Option A: `git apply` (faster)**\n\nCI already ran the generator. Just apply what it computed:\n\n```bash\n# Copy the diff from CI, then:\npbpaste | git apply   # macOS\n```\n\n**Option B: Regenerate (slower but more \"proper\")**\n\n```bash\n./frb_internal precommit-generate\n```\n\n> **After codegen:** Follow `frb-dev-env` and verify generated output is present in the selected worktree before reviewing the diff.\n\nBoth are correct. Option A is faster; Option B is more thorough.\n\nDo not hand-edit generated files as the final fix.\n\nYou may use CI diffs only as a diagnosis aid to understand what changed, but the final accepted output should come from re-running the appropriate generation workflow in a clean matching environment.\n\n### Can't Reproduce Locally\n\nCI shows the command it ran. Read `frb-dev-env` before running it.\n\nBefore reproducing, make sure the toolchain versions match CI closely enough to be meaningful. In practice this usually means Flutter, Dart, Rust, cargo subcommands, and any pinned template or helper dependency should match the versions used by CI.\n\nIf reproducing remotely, clean the remote workspace first unless you have a specific reason not to. In FRB this often matters because old generated files and untracked outputs can survive earlier runs and create fake diffs:\n\n```bash\ngit reset --hard HEAD && git clean -fdx\n```\n\nThen run the same command:\n\n```bash\n# CI shows: ./frb_internal test-dart --package frb_example/pure_dart\n./frb_internal test-dart --package frb_example/pure_dart\n```\n\n### Lint/Format Errors\n\nFor clippy, dart analyze, or format errors, use `--fix`:\n\n```bash\n./frb_internal lint --fix\n```\n\nThis runs:\n- `cargo clippy --fix` - Rust lint fixes\n- `cargo fmt` - Rust format\n- `dart format` - Dart format\n- `dart fix --apply` - Dart auto fixes\n\nWhen lint/format failures happen on generated files, do not default to hand-editing those files just to match formatter output.\n\nInstead:\n- Compare the formatter inputs across branch head, PR merge ref, and remote workspace\n- Check whether generation order, hidden generation steps, or toolchain/environment drift changed the generated file before formatter ran\n- Only accept formatter output after confirming the pre-format generated input is actually the correct one\n\n### `Command Integrate` Failures\n\nWhen `Generate :: FRB Codegen :: Command Integrate` fails because integrated output is wrong, do not hand-edit the generated integrate example outputs.\n\nInstead:\n- Fix the source templates under `frb_codegen/assets/integration_template/`\n- If the bad logic is inside the embedded `cargokit` submodule, fix and push `fzyzcjy/cargokit`, then update the submodule ref\n- Re-run integrate generation after updating the templates\n\nSpecial case: Apple scaffold\n\n- If Linux-side raw `create/integrate` drops checked-in Apple files, do not describe the current fix as \"restoring old scaffold\" unless it really is an accidental workaround.\n- First check whether the repo intentionally treats checked-in mac-generated Apple scaffold as source of truth.\n- If that is the contract, prefer names and comments like \"apply checked-in Apple scaffold source of truth\" over vague restore/preserve wording.\n\n### Generate-caused Failures\n\nIf CI previously failed mainly in `Generate` while other jobs passed, and after accepting generated changes additional non-`Generate` jobs start failing, treat this as strong evidence that the accepted generated outputs are incorrect or incomplete.\n\nIn that situation:\n\n- Do not continue fixing downstream failures one by one first\n- First validate the generation logic or generation workflow\n- Re-generate from a clean environment\n- Only accept generated outputs after confirming they do not introduce new non-`Generate` regressions\n\nIf the symptom is not one package but a sequence of packages failing with very similar generated Dart drift, prefer validating clean remote `./frb_internal precommit-generate` before accepting more package-by-package sync commits.\n\nIf clean remote `precommit-generate` reduces the remaining diff surface to one small tail package, accept that tail from the same clean run and rerun CI. Do not go back into a long per-package loop unless a new failure class appears.\n\n### Dart Web Browser Startup Flakes\n\nWhen `Test :: Dart :: Web (...)` fails after the web build and server startup already succeeded, and the failure is:\n\n```text\nException: Websocket url not found.\n```\n\ntreat it as a likely browser / puppeteer startup flake first, not an immediate code regression.\n\nIn that situation:\n\n- Do not assume the generated code or test logic is broken just from this error\n- Check whether wasm build, `dart compile js`, and local web server startup already succeeded\n- Prefer rerunning only the failed job first\n- Only start code investigation if the same job keeps failing with the same error repeatedly\n\n## Common Mistakes\n\n- Investigating root cause when a simple re-run would work\n- Not trying `git apply` first when CI provides a diff\n- Fixing many new downstream test/build failures one by one after accepting generated changes, when CI previously failed mainly in `Generate`\n- Hand-editing generated files to chase CI formatter output before checking whether CI, merge ref, and remote environments are formatting the same input\n- Hand-editing integrate-generated example outputs instead of fixing `frb_codegen/assets/integration_template/`\n- Assuming `cargokit` submodule changes are off-limits when the real bug is there\n- Chasing repeated `refresh/regenerate/sync` diffs without re-checking the upstream generation inputs\n- Continuing package-by-package `Generate` sync commits after two similar generated Dart drifts, instead of escalating to clean remote `precommit-generate`\n- Fixing downstream build/test jobs before upstream generate/integrate/high-relevance generate-internal stages are stable\n- Answering from stale CI state instead of reading the latest relevant run or job information first\n- Forgetting to remove `ci-manual-dispatch` and rerun normal CI before treating the PR as ready\n\n## Related Skills\n\n- `frb-code-generation` - Which generation commands to run\n- `frb-debugging` - Deep debugging when simple fixes don't work\n",".claude/skills/frb-fix-codecov/SKILL.md":"---\nname: frb-fix-codecov\ndescription: Use when a flutter_rust_bridge PR has Codecov patch or project coverage drops, Codecov comments mention missing lines, or you need to decide whether to add tests or justified coverage-ignore markers.\n---\n\n# FRB Fix Codecov\n\n## Overview\n\nUse this skill to turn a vague Codecov PR comment into concrete file/line facts, then decide whether the\nright fix is real coverage or a narrow, justified ignore marker.\n\n## Workflow\n\n### Step 1: Collect the Codecov Facts\n\nStart from the latest PR head. Codecov comments can be edited and old comments/checks can describe stale\ncommits.\n\nFetch the PR metadata, Codecov comments/checks, and the full Codecov report into a local temporary directory\nwith the bundled analyzer:\n\n```bash\npython3 .claude/skills/frb-fix-codecov/codecov_analyzer.py download --pr <number>\npython3 .claude/skills/frb-fix-codecov/codecov_analyzer.py analyze --input-dir <output_dir>\n```\n\n`download` writes `pr.json`, `check-runs.json`, `codecov-report.json`, `codecov-comment.md`,\n`codecov-checks.json`, and `codecov-files-summary.json` under `/private/tmp/frb-codecov-<pr>-<timestamp>/` by\ndefault. Use `download --output-dir <dir>` to choose a stable path for debugging or examples.\n\n`analyze` is offline: it reads the downloaded directory, writes `missing-patch-lines.txt`, and prints the exact\nmissing patch lines.\n\nThe full Codecov `report?sha=` endpoint is preferred because it returns all files in one response. Codecov also has\n`file_report/<urlencoded-path>?sha=<sha>` for one file; use it only when the full report is too large or you\nneed to re-check a path precisely.\n\nRead both Codecov statuses:\n\n- `codecov/patch`: coverage on changed lines only. This is the usual PR blocker.\n- `codecov/project`: whole-project coverage delta. A green project status does not make a red patch status\n  irrelevant.\n\nThe GitHub comment usually lists files and missing counts, not precise line numbers. Compute exact missing\npatch lines by intersecting Codecov's missing lines with Git's added/changed lines. In Codecov's\n`line_coverage`, FRB's uploaded custom Rust coverage JSON uses:\n\n- `null`: not coverable or ignored\n- `0`: covered\n- positive number: missing\n\nThe analyzer computes exact missing patch lines by intersecting the saved Codecov report with\n`git diff --unified=0 <base_sha> <head_sha>`. Read `missing-patch-lines.txt` first, then use the raw JSON files\nwhen the summary does not explain the status.\n\nIf the computed output disagrees with Codecov:\n\n- Check whether the Codecov comment/check describes a different `HEAD_SHA`.\n- Check whether Git has both SHAs locally; fetch the PR head/base if needed.\n- For a truncated file path in the comment, get the full path from the Codecov URL's `filepath=` query.\n- Verify `Misc :: Codecov` downloaded all expected `*-coverage` artifacts before upload; use the\n  user's `tom-ci` skill for GitHub Actions logs.\n\n### Step 2: Choose the Fix\n\n#### Option A: Exclude Non-Meaningful Coverage\n\nIn this repo the Rust-side marker is `frb-coverage:ignore-start` /\n`frb-coverage:ignore-end` (not `frb-codecov-ignore`). It is implemented by\n`tools/frb_internal/lib/src/utils/codecov_transformer.dart`, which removes those Rust lines before upload.\n\nDart-side code uses normal Dart coverage markers:\n\n```dart\n// coverage:ignore-start\n...\n// coverage:ignore-end\n```\n\nOnly use ignore markers when the code genuinely should not be judged by Codecov. Existing FRB examples\nusually have an explanatory comment immediately above the marker. Acceptable reasons include:\n\n- The code is definitely executed, but the coverage tool cannot see it, especially build-time or macro-time\n  execution.\n- llvm-cov reports a branch as uncovered even though a focused test exercises it.\n- The branch is warning-only, defensive, tool-missing, OS-specific, or otherwise not worth forcing in normal\n  CI.\n- The line is generated glue, FFI shape glue, or syntactic boilerplate where testing it would assert\n  implementation trivia rather than behavior.\n\nWhen adding Rust ignores, keep the ignored range as small as possible and write the reason first:\n\n```rust\n// This is executed at build time, but llvm-cov does not observe that path.\n// frb-coverage:ignore-start\n...\n// frb-coverage:ignore-end\n```\n\nDo not use ignore markers merely because adding a test is inconvenient. If the reason cannot be explained in\none short public comment, treat the line as coverage-relevant.\n\n#### Option B: Add Meaningful Coverage\n\nInvestigate why each exact missing line is not covered.\n\nPrefer real coverage when the uncovered line represents product behavior that should keep working. Good\ncandidates for tests:\n\n- Pure functions, parsers, config decisions, path computations, generated text decisions, and error handling.\n- Integration/codegen behavior where the missing line affects generated files or public workflow behavior.\n- Regression-prone behavior introduced or modified by the PR.\n\nFor feature behavior, default to an E2E-style regression first, especially by extending an existing\n`pure_dart` scenario when that can exercise the real generated workflow.\n\nOnly fall back to narrower tests when the E2E route is brittle, too expensive, or cannot reach the behavior:\n\n- Add a codegen or integration test when generated output proves the behavior but a full E2E case is awkward.\n- Unit test the function directly when the behavior is local, stable, and not meaningfully tied to an FRB\n  workflow.\n\nUse the relevant skills after choosing the test path:\n\n- `frb-develop-feature` for feature/bug regression coverage.\n- `frb-test` for selecting local test commands.\n- `frb-dev-env` before running FRB tests or setup commands in Tom's environment.\n\nIf the behavior matters but a focused test would be brittle, very expensive, or require a product decision,\nsummarize the exact missing lines, the suspected reason they are uncovered, and the realistic options, then\nask the human which tradeoff to take.\n\n## Verification\n\nAfter changing tests or ignore markers:\n\n1. Re-read the touched files immediately before editing.\n2. Commit the logical fix before long verification, following Tom's commit policy.\n3. Run the focused local test command when practical.\n4. Push and check the latest `codecov/patch` and `codecov/project` statuses on the new head.\n5. If Codecov is still red, repeat the workflow from the latest exact report instead of reusing stale line\n   numbers.\n",".claude/skills/frb-fix-main-ci/SKILL.md":"---\nname: frb-fix-main-ci\ndescription: Use when flutter_rust_bridge CI fails on the default branch/main/master, especially to distinguish ordinary CI repair from a regression introduced after a previously green default-branch commit.\n---\n\n# FRB Fix Main CI\n\nUse this when CI is failing on the default branch (`main` / `master`) or on a commit that is already part of the default-branch history.\n\n## Choose a Path\n\nDefault-branch CI failures usually have two possible repair paths. Choose based on the evidence in the latest relevant run.\n\n### Path A: Ordinary CI Repair\n\nUse this path when the failure looks like a known FRB CI shape: flaky job, generated diff, lint drift, stale generated output, dependency-order failure, or downstream symptom already covered by existing CI triage.\n\nRead `frb-fix-ci` and repair the failure directly:\n\n- Check the latest relevant run, not stale status.\n- Use the user's `tom-ci` skill when reading GitHub Actions job logs.\n- Classify flakes, generated diffs, lint drift, dependency-order failures, and propagated downstream symptoms before deep debugging.\n- Prefer fixing prerequisite jobs such as `Generate`, `Integrate`, or high-relevance `Generate Internal` before chasing later build/test symptoms.\n\n### Path B: Regression Investigation\n\nUse this path when the failure is not obviously a known FRB CI maintenance issue, or when several default-branch commits/runs suggest something recently broke.\n\nA default-branch failure is often a regression. There should usually be an older default-branch commit that passed the same CI path. Study what changed between the green baseline and the red commit, then fix the source of that regression.\n\nBefore writing a regression fix, answer these questions:\n\n1. What is the failing default-branch commit and workflow run?\n2. What is the nearest older default-branch commit with a green run for the same workflow or job family?\n3. Which commits or merged PRs sit between the green baseline and the red commit?\n4. Does the failure reproduce at the red commit but not at the green baseline, using the same `./frb_internal ...` command and the FRB dev environment rules?\n5. Which source change most directly explains the regression?\n\n## Regression Workflow\n\n1. Capture the exact failing jobs and the first useful error from CI logs.\n2. Find the newest passing default-branch run for the same workflow/job family.\n3. Compare the commit range between green and red:\n   ```bash\n   git log --oneline --decorate <green-sha>..<red-sha>\n   git diff --stat <green-sha>..<red-sha>\n   ```\n4. If the range is broad or the culprit is unclear, bisect with the smallest meaningful reproduction command.\n5. Fix the source of the regression, not just the generated or downstream symptom.\n6. Add or adjust regression coverage when the bug is behavioral. For pure CI/tooling drift, document the before/after evidence in the PR.\n\n## What Not To Do\n\n- Do not treat default-branch CI as \"just rerun until green\" unless logs strongly indicate infra flake.\n- Do not force every default-branch failure through regression bisect when `frb-fix-ci` already identifies a direct CI maintenance fix.\n- Do not only repair checked-in generated files if the generator, template, version pin, or environment change is the real source.\n- Do not chase downstream job failures while an upstream generation or integration job is still red.\n- Do not assume the newest red commit is the sole cause without checking the nearest green baseline and intervening changes.\n\n## PR Expectations\n\nFor a PR that fixes default-branch CI, include:\n\n- Failing commit SHA and workflow/job URL.\n- Nearest green baseline SHA or run, if available.\n- The reproduction command used locally or in Docker/Tart.\n- The suspected or confirmed culprit commit/PR.\n- Why the fix addresses the source of the regression.\n\nUse `frb-prepare-pr` before opening the PR.\n",".claude/skills/frb-fix-merge-conflict/SKILL.md":"---\nname: frb-fix-merge-conflict\ndescription: Use when resolving merge or rebase conflicts in flutter_rust_bridge, especially conflicts involving generated files after master/main changed independently from a PR branch.\n---\n\n# FRB Fix Merge Conflict\n\nUse this skill before resolving merge or rebase conflicts in `flutter_rust_bridge`.\n\n## Core Rule\n\nResolve hand-written files semantically. Do not hand-resolve generated files as the final result.\n\nGenerated conflict output should be treated as a stale artifact from two independently valid histories. After the hand-written sources are merged, regenerate the generated outputs and commit that regenerated result.\n\n## Before Starting\n\n1. Read the FRB environment setup instructions for the current checkout before running generation or tests.\n2. Read `frb-code-generation` to choose the minimal regeneration command.\n3. If the conflict is discovered through CI or PR checks, read `frb-fix-ci` before deeper investigation.\n4. Make sure the worktree starts clean except for the merge/rebase conflict you are intentionally resolving.\n\n## Classify Conflicts\n\nList unmerged files:\n\n```bash\ngit diff --name-only --diff-filter=U\n```\n\nClassify each file into one of these buckets:\n\n| Bucket | Examples | Resolution |\n|--------|----------|------------|\n| Hand-written source | Rust APIs, codegen logic, Dart tests, docs, configs | Resolve manually with normal semantic merge |\n| Generated output | `frb_generated.*`, `*.freezed.dart`, `*.g.dart`, generated API wrappers, generated test entrypoints | Pick one side only as a temporary placeholder, then regenerate |\n| Ambiguous generated-like output | lockfiles, copied template outputs, integrate scaffolds | Confirm source of truth before choosing; use `frb-fix-ci` dependency graph if unsure |\n\nDo not use the generated-file shortcut for hand-written files merely because they are noisy.\n\n## Generated File Conflict Workflow\n\nWhen a generated file conflicts because both base and PR independently added tests or APIs:\n\n1. Resolve all hand-written source conflicts first.\n2. For each generated conflicted file, choose a conflict-free placeholder from either side.\n   The chosen side is not the final answer; it only clears conflict markers so the generator can run.\n3. Stage those placeholder generated files.\n4. Run the appropriate generation command from `frb-code-generation`.\n   For broad PR-vs-master generated drift, prefer:\n\n```bash\n./frb_internal precommit-generate\n```\n\n   For `frb_example/pure_dart` and `frb_example/pure_dart_pde` conflicts, the internal generator and package codegen cover different outputs. If the merge touches generated test entrypoints, copied PDE files, or the pure-Dart chain, run:\n\n```bash\n./frb_internal generate-internal-frb-example-pure-dart\n```\n\n   If the merge also brings in hand-written Rust or Dart API changes, run package codegen after the internal generator:\n\n```bash\n./frb_internal generate-run-frb-codegen-command-generate --package frb_example/pure_dart\n./frb_internal generate-run-frb-codegen-command-generate --package frb_example/pure_dart_pde\n```\n\n   Do not stop after the internal generator when CI errors mention missing methods on generated Dart APIs such as `RustLibApi`; that usually means the package `frb_generated.*` files are stale.\n\n5. Inspect the regenerated diff. The final generated files should reflect both sides' hand-written sources.\n6. Commit the merge/rebase resolution.\n\n### Placeholder Commands\n\nFor a normal `git merge master` into a PR branch, this is usually enough for generated paths:\n\n```bash\ngit checkout --ours -- <generated-path>\ngit add <generated-path>\n```\n\nFor a `git rebase`, remember Git reverses the intuitive meaning of ours/theirs: `--ours` is the branch being rebased onto, and `--theirs` is the replayed commit. Since generated files are only placeholders, either side can be acceptable if the file still exists and regeneration will overwrite it. When deletion/addition is involved, prefer the side that leaves the path in the shape expected by the generator, then verify after regeneration.\n\n## Validation\n\nAfter regeneration:\n\n```bash\ngit diff --check\ngit diff --name-only --diff-filter=U\ngit status --short\n```\n\nThen run the relevant local Docker-backed checks from the project skills. For broad generated-file conflict repairs, start with the same generation command CI would run, then use CI to validate the full matrix.\n\nIf CI later reports more generated diff, add the experience back to this skill so the next repair has a sharper rule.\n\n## Commit Shape\n\nKeep the conflict-resolution commit focused:\n\n- Include semantic hand-written conflict resolutions.\n- Include regenerated outputs from the chosen generation command.\n- Do not include unrelated cleanup.\n- Mention the generation command in the commit message body when useful.\n",".claude/skills/frb-issue-to-green-pr/SKILL.md":"---\nname: frb-issue-to-green-pr\ndescription: \"Use when implementing a GitHub issue, bug fix, or feature in flutter_rust_bridge end-to-end: develop the change, add regression coverage, prepare and open a PR, and use the user's tom-ci skill to monitor CI until the PR is ready.\"\n---\n\n# FRB Issue to Green PR\n\nUse this skill as the top-level workflow when the user asks you to implement an issue, fix a bug, add a feature, or otherwise \"handle it end to end\" in `flutter_rust_bridge`, especially when they ask for an automatic PR or CI monitoring.\n\nThis skill orchestrates the narrower FRB skills. Do not duplicate their detailed instructions in memory; read them when their phase begins.\n\n## Required Subskills\n\nAlways read these first:\n\n1. `frb-dev-env` and any applicable user-level environment rules.\n2. `frb-develop-feature` for the reproduce -> fix -> final regression workflow.\n\nRead these when entering the matching phase:\n\n- `frb-code-generation` before running generation or changing Rust APIs, codegen, or example APIs.\n- `frb-test` before running local tests.\n- `frb-lint` before lint or format checks.\n- `frb-prepare-pr` before pushing or opening the PR.\n- `frb-pr-review` before treating a non-trivial PR as ready.\n- `frb-fix-ci` before diagnosing any CI failure.\n- The user's `tom-ci` skill for CI waiting, status inspection, and GitHub Actions logs.\n- `frb-manual-test` before writing a manual regression test report under `tools/manual_tests/`.\n- `frb-ci-filter` before creating an intentional red CI reproduction PR or using filtered CI.\n- `frb-debugging` when generated code is surprising or codegen behavior is unclear.\n\n## Workflow\n\n### 1. Start the completion loop\n\n- Keep the task active until all stop conditions are met.\n- Once the PR URL is known, follow the waiting workflow in the user's `tom-ci` skill between CI processing actions until the PR is ready.\n\n### 2. Understand the GitHub issue or requested change\n\n- Fetch the issue body and comments if the user gave an issue link or number.\n- Identify the smallest observable failing behavior or missing capability.\n- Check `git status --short` and do not disturb unrelated user or multi-agent changes.\n\n### 3. Reproduce the issue in an independent reproduction PR\n\n- Read and follow the reproduction parts of `frb-develop-feature`.\n- For bug fixes, do this before changing fix code or opening the fix PR.\n- Create an independent reproduction PR whose only purpose is to prove the bad behavior. The branch name, PR title, and PR body must say clearly that it is an intentional reproduction PR, not a real fix PR.\n- For intentional red CI reproduction PRs, use the title `Reproduce ISSUE_SUMMARY with intentional red CI`.\n- For manual-test reproduction PRs, use the title `Add manual reproduction for ISSUE_SUMMARY`.\n- If CI can reproduce the bad behavior, read `frb-ci-filter` and make the reproduction PR an intentional red CI PR: unchanged fix code, minimal reproducer or workflow adjustment, mandatory focused `ci_filter` run, and a failure whose error matches the user's report.\n- If CI cannot realistically reproduce the bad behavior, read `frb-manual-test` and make the independent reproduction PR add or update `tools/manual_tests/NAME.md` with a normal manual test procedure and mechanical execution steps an agent or human can run.\n- Do not proceed to the fix PR until the reproduction PR exists and has either a matching red CI run or a precise manual test report.\n- Save the reproduction PR URL, red CI run URL when applicable, job name or manual-test path, and matching error text for the fix PR reproduction report.\n\n### 4. Fix the issue\n\n- Read and follow the fix, iteration, local verification, regression coverage, and final placement parts of `frb-develop-feature`.\n- Implement the smallest change that fixes the reproduced behavior.\n- Add final regression coverage with the fixed behavior as the expected result.\n- Make atomic commits as soon as each completed logical unit is written; do not wait until the end of the task.\n- Stage only files intentionally changed for this task.\n- Always create a new commit unless the user explicitly asks to amend.\n- Before considering the change ready, explicitly pass the `frb-develop-feature` Final Placement Gate: final regression coverage belongs in `frb_example/pure_dart` with generated `pure_dart_pde` coverage, not only in `frb_example/dart_minimal`.\n- Keep generated-file edits produced by the appropriate generator, not by hand.\n\n### 5. Prepare and open the PR\n\n- Follow `frb-prepare-pr`.\n- Re-check that no final regression or feature coverage remains only in `frb_example/dart_minimal`. If it does, stop PR preparation and migrate it to `frb_example/pure_dart` first.\n- Push with upstream tracking.\n- Before drafting a PR title, inspect the user's recent PR titles and mimic the repo style.\n- Create the PR according to the active PR workflow and repository/user PR body rules.\n- If the work comes from a GitHub issue, ensure the PR body includes the appropriate closing keyword such as `Close #1234`, unless the active PR workflow explicitly requires an empty body.\n\n### 6. Run the review gate\n\n- Follow `frb-pr-review` for the full PR review gate, including correctness review and test-weakening review.\n\n### 7. Monitor CI until terminal\n\n- After the PR is opened or updated, do not leave the PR in an unknown queued or in-progress state.\n- On each wake-up, inspect the latest PR checks, not stale runs.\n- For bug-fix PRs with an intentional red CI reproduction PR, explicitly find the same job family or CI path that failed in the reproduction branch and verify that it is now green on the fix PR.\n- For bug-fix PRs with a manual-test report PR, state whether the manual regression was re-run, who or what ran it, and whether the observed behavior now matches the fixed expectation.\n- If CI fails, read `frb-fix-ci` and the user's `tom-ci` skill, diagnose the latest relevant failure, fix it, commit, push, and continue monitoring.\n- If CI appears flaky, rerun only failed jobs when appropriate, then keep monitoring.\n\n### 8. Stop only when ready\n\n- The PR checks are green or all remaining non-green checks are clearly unrelated and explained.\n- The branch is pushed, commits are present on the PR, and the final status is reported to the user with the PR URL.\n\n## Automation Rules\n\n- Follow the user's `tom-ci` skill to wait on the PR URL after each push, rerun, or handled CI event.\n- Treat its return as a wake-up signal, not a CI verdict. Inspect the reported checks, fix or rerun as required, then call the waiter again.\n- Do not add a separate time-based heartbeat or detached polling loop for the same PR.\n- Stop invoking the waiter once the stop conditions are met.\n\n## Failure Handling\n\n- If blocked by permissions, missing credentials, a required external reviewer delay, or unavailable infrastructure, explain the concrete blocker and keep the PR state explicit.\n- If a CI run is queued for a long time, continue bounded waits using the user's `tom-ci` skill.\n- If new user instructions arrive, let the newest instruction steer the workflow while preserving already-completed work.\n",".claude/skills/frb-lint/SKILL.md":"---\nname: frb-lint\ndescription: Use when you need to run lint, format, or clippy checks in flutter_rust_bridge\n---\n\n# FRB Lint\n\n> **Note:** Read `frb-dev-env` before running commands and follow the active user's environment rules.\n\n## Quick Reference\n\n| Command | Description |\n|---------|-------------|\n| `./frb_internal lint --fix` | Run all lints and auto-fix (recommended) |\n| `./frb_internal lint` | Run all lints |\n| `./frb_internal lint-dart` | Dart only |\n| `./frb_internal lint-rust` | Rust only |\n\n## What Lint Includes\n\nIt contains:\n- `dart analyze`\n- `dart format`\n- `cargo clippy`\n- `cargo fmt`\n",".claude/skills/frb-manual-test/SKILL.md":"---\nname: frb-manual-test\ndescription: Use when adding, executing, reviewing, or updating flutter_rust_bridge manual test reports under tools/manual_tests for scenarios that require human or agent-driven manual verification.\n---\n\n# FRB Manual Test\n\nUse this skill for manual test reports in `tools/manual_tests/`. These reports describe repeatable checks that a human or agent can execute when automated tests are insufficient, unavailable, too expensive, or not the right interface for the behavior being verified.\n\nManual tests are normal software-engineering test artifacts. They may cover devices, host-specific setup, interactive UI behavior, credentials or account state, release packaging, marketplace flows, external services, or long-running workflows. They are not tied to any single PR workflow.\n\n## Add Manual Tests\n\nManual test reports live under:\n\n```text\ntools/manual_tests/NAME.md\n```\n\nWhen adding a report, do not start from a blank file. Copy the bundled test template first:\n\n```bash\ncp .claude/skills/frb-manual-test/test-template.md tools/manual_tests/NAME.md\n```\n\nUse a short kebab-case name. Include a date prefix when the report is an example, a one-off check, or does not yet have a stable feature name, such as:\n\n```text\ntools/manual_tests/2026-06-06-some-scenario.md\n```\n\nAfter copying, edit the report in place:\n\n- Replace every placeholder.\n- Remove sections that truly do not apply.\n- Keep commands and UI actions mechanical enough that a future human or agent can run them without the original conversation.\n- Prefer repo tooling such as `./frb_internal` when possible.\n- Do not rely on \"run the app normally\", \"check manually\", or \"verify it works\" without concrete commands or UI actions.\n- If credentials or private services are needed, name the required capability without including secrets.\n\nThe detailed content requirements live in `test-template.md`; keep that template as the source of truth for report shape.\n\n## Execute Manual Tests\n\nWhen executing a manual test, first read the target report under `tools/manual_tests/` and follow its preparation, steps, expected result, failure criteria, troubleshooting, and cleanup sections.\n\nBefore reporting the run result, use the bundled execution template:\n\n```bash\ncp .claude/skills/frb-manual-test/execution-template.md EXECUTION_RESULT_PATH.md\n```\n\nAlways fill out an execution markdown file before summarizing the result. Do not only say that the test \"passed\".\n\nExecution markdown is a run artifact, not a repository test definition. Do not create it under `tools/manual_tests/` or anywhere else inside the `flutter_rust_bridge` repository. Put it in an external artifacts location such as `/private/tmp`, `~/main/artifacts/flutter_rust_bridge/`, or another run-specific artifact directory.\n\nExecution markdown must contain precise, audit-ready evidence that proves what ran and why it passed, failed, or blocked. Include the exact commands, exit status, key success or failure log lines, relevant version output, selected device or simulator identifiers, generated artifact paths, final cleanup state, and links or paths to complete logs. For successful runs, quote or summarize the concrete pass indicators such as `All tests passed!`, `test result: ok`, artifact paths, simulator UDIDs, or final `git status --short` output. Avoid vague summaries like \"worked\", \"passed\", or \"looked good\" without evidence.\n\nIf the execution result belongs to a PR, issue, release checklist, or other reviewable workflow, upload the filled execution markdown and all review-relevant artifacts as a GitHub gist and link that gist in the PR description, PR comment, issue comment, or release checklist. Review-relevant artifacts include screenshots, recordings, logs, generated result files, and browser/device evidence needed to judge the result. The local execution markdown and artifact copies should still be kept in the run artifacts directory so the run can be audited without relying only on chat history.\n\nIf the result belongs only in chat, still fill out the execution markdown locally and include its path in the chat response.\n\nRun cleanup from the manual test report before declaring the execution complete, unless cleanup is intentionally skipped and documented in the execution record.\n\n### Reviewable Workflow Gate\n\nBefore creating or updating a PR, issue comment, release checklist, or any other reviewable artifact that cites a manual test execution:\n\n1. Fill out the execution markdown file locally.\n2. Collect every artifact needed by the manual test's `Results To Capture` section, especially screenshots, recordings, logs, and generated evidence files.\n3. Upload the execution markdown and those review-relevant artifacts into the same GitHub gist. Use a gist git clone and push when binary artifacts such as PNG screenshots need to be included.\n4. Copy the gist URL into the execution markdown's `Execution markdown gist` field if practical.\n5. Make the execution markdown point to the gist-contained artifact filenames, embedding screenshots or linking recordings when useful.\n6. Link the gist URL in the reviewable artifact.\n7. Keep local artifact paths only as supplemental audit evidence; they are not a substitute for gist-contained artifacts.\n\nDo not say that a PR-linked manual test execution is complete while any review-relevant artifact exists only as a local path. If gist upload or GitHub access fails, report the manual test execution as complete locally but the PR/update step as blocked until a gist containing the execution markdown and review-relevant artifacts can be created and linked.\n",".claude/skills/frb-manual-test/execution-template.md":"# Manual Test Execution\n\n## Test\n\n:::info\nIdentify the manual test that was executed and why this run happened. Copy the purpose from the test report or summarize it in one sentence.\n:::\n\n- Manual test: `TODO: tools/manual_tests/name.md`\n- Purpose: `TODO: short description copied or summarized from the test report`\n- Source context: `TODO: issue, PR, release, or request if relevant`\n\n## Run\n\n:::info\nRecord when, by whom, and against exactly what commit or release the manual test was executed. Include enough environment detail for someone else to compare results.\n:::\n\n- Date and timezone: `TODO: YYYY-MM-DD HH:MM TZ`\n- Executor: `TODO: human, agent, or automation`\n- Commit or release tested: `TODO: sha or version`\n- Environment: `TODO: OS, shell, device, simulator, browser, or service summary`\n\n## Procedure\n\n:::info\nRecord whether the written test was followed exactly. Any deviation matters, even if the result passed. Include exact commands or a path to a command script when the run used one.\n:::\n\n- Followed report revision: `TODO: commit containing the manual test report`\n- Command script or exact commands: `TODO: path to script, inline commands, or not applicable`\n- Deviations from written steps: `TODO: none, or list exact deviations`\n- Cleanup completed: `TODO: yes/no; explain if no`\n\n## Result\n\n:::info\nUse `pass`, `fail`, `blocked`, or `partial`. The summary should explain the result without requiring the reader to open logs first. Include exact command exit status and concrete pass/fail indicators from the logs.\n:::\n\n- Status: `TODO: pass/fail/blocked/partial`\n- Summary: `TODO: one or two sentences`\n- Command results: `TODO: each required command with exit status`\n- Evidence lines: `TODO: exact success/failure strings, log line numbers, or grep output proving the result`\n\n## Artifacts\n\n:::info\nLink or name the artifacts captured during execution. Use `not applicable` for artifact types that do not apply.\n:::\n\n- Execution markdown gist: `TODO: gist URL if this result belongs to a PR, issue, or release checklist; otherwise not applicable`\n- Terminal log: `TODO: path or link`\n- Screenshot or recording: `TODO: path or link, or not applicable`\n- Generated artifacts: `TODO: path or link, or not applicable`\n- Environment evidence: `TODO: exact tool versions, device/simulator name and UDID, browser version, or not applicable`\n- Cleanup evidence: `TODO: final git status, VM/container/device state, cleanup command output, or not applicable`\n- Other evidence: `TODO: path, link, log excerpt, or not applicable`\n\n## Follow-up\n\n:::info\nIf the result is not a clean pass, state the next action. Link the issue or PR when one exists.\n:::\n\n- Required follow-up: `TODO: none, issue link, PR link, or next action`\n- Notes: `TODO: anything future executors should know`\n",".claude/skills/frb-manual-test/test-template.md":"# Manual Test Template\n\n## Purpose\n\n:::info\nWrite one or two concrete sentences explaining what behavior this manual test verifies. Name the user-visible behavior, platform, package, or workflow under test. Do not leave this as a generic phrase like \"verify it works\".\n:::\n\nTODO: Write what this test verifies.\n\n## Source\n\n:::info\nLink the reason this test exists: issue, PR, feature request, release checklist, user report, or maintenance task. If there is no external link, state the local context in plain text.\n:::\n\n- Context: `TODO: issue, PR, feature, release, or user report`\n- Related docs or skills: `TODO: paths or links`\n\n## When To Run\n\n:::info\nState the trigger for this manual test. Examples: before a release, after changing a named subsystem, after upgrading Flutter/Rust/Dart, before closing a bug, or when validating a specific platform.\n:::\n\nTODO: Write when this test is required or useful.\n\n## Preconditions\n\n:::info\nList everything that must already be true before starting. Include checkout state, expected branch family if relevant, submodules, generated files, devices, simulators, credentials, account state, network access, and external services. Never paste secrets. Keep this section about reusable requirements, not one particular run.\n:::\n\n- Repository: `fzyzcjy/flutter_rust_bridge`\n- Required checkout state: `TODO: clean checkout with submodules initialized, generated files present, etc.`\n- Required credentials or account state: `TODO: required capability, never paste secrets`\n- Required device or simulator state: `TODO: device must be available, app uninstalled, cache reset, etc.`\n\n## Environment\n\n:::info\nRecord exact versions and runtime context needed to make the result reproducible. If a tool is not required, write `not required` instead of leaving it ambiguous.\n:::\n\n- OS: `TODO: macOS / Linux / Windows version, or not constrained`\n- Flutter: `TODO: flutter --version, or not required`\n- Dart: `TODO: dart --version, or not required`\n- Rust: `TODO: rustc --version, or not required`\n- Device or simulator: `TODO: device name, OS version, UDID if applicable, or not required`\n- Browser or external service: `TODO: name and version if applicable, or not required`\n\n## Preparation\n\n:::info\nGive copy-pasteable commands that take an existing repository root to the state needed by the test. Prefer repository tooling such as `./frb_internal`. If generated files, caches, or dependencies are required, include the exact commands here. Keep source acquisition and revision selection outside the reusable test case.\n:::\n\n```bash\ngit submodule update --init --recursive\n```\n\nIf this test needs generated output or dependencies, list the exact setup commands here.\n\n```bash\n./frb_internal --help\n```\n\n## Test Data\n\n:::info\nDescribe all inputs and fixtures. Include paths, account setup, generated files, sample API code, reset commands, or \"none\" if the test uses no special data.\n:::\n\n- Input files, API examples, account fixtures, or generated assets: `TODO: paths or setup notes, or none`\n- Reset procedure before each run: `TODO: commands or UI actions`\n\n## Steps\n\n:::info\nWrite ordered mechanical steps. Each step should be executable by a human or agent without guessing. For UI steps, name buttons, screens, field values, and expected intermediate state. For command steps, include complete commands.\n:::\n\n1. TODO: Prepare the target package, example, device, account, or service.\n\n   ```bash\n   TODO_COMMAND\n   ```\n\n2. TODO: Run the command or manual action that verifies the behavior.\n\n   ```bash\n   TODO_COMMAND\n   ```\n\n3. If UI interaction is required, describe each click, field value, device action, or expected screen by name.\n\n## Expected Result\n\n:::info\nDefine the pass condition as observable output: command exit status, log text, generated artifact, UI state, screenshot content, or external service state. Avoid vague language.\n:::\n\nTODO: The command or manual flow should complete successfully and show the expected observable behavior.\n\n```text\nTODO: success output, UI text, artifact path, or other pass criteria\n```\n\n## Failure Criteria\n\n:::info\nList concrete conditions that mean the test failed or is blocked. Include setup failures that should be reported separately from product failures.\n:::\n\nThe test fails if any of the following happens:\n\n- The command exits non-zero unexpectedly.\n- The UI, artifact, or log output does not match the expected result.\n- The required device, account, or service state cannot be prepared.\n\n## Results To Capture\n\n:::info\nSay exactly what artifacts the executor should save or link in the execution record. Include terminal logs, screenshots, recordings, generated files, device details, run URLs, or external service observations.\n:::\n\n- Full terminal log from the command.\n- Screenshot or screen recording if UI behavior is involved.\n- Device, simulator, or browser version.\n- Any generated artifact path needed to inspect the result.\n\n## Troubleshooting\n\n:::info\nProvide known recovery steps for common environment and setup failures. This section should help distinguish a blocked manual run from a real product failure.\n:::\n\n- If dependencies are missing, re-run the repository setup commands and record the exact failure.\n- If a device or simulator is unavailable, record the discovered devices and mark the run as blocked.\n- If credentials or external services are unavailable, record the missing capability without exposing secrets.\n\n## Cleanup\n\n:::info\nList commands and manual actions needed to restore the checkout, stop services, reset devices, and undo external account state. If no cleanup is needed, say so explicitly.\n:::\n\n```bash\ngit status --short\n```\n\nRemove temporary local files, stop simulators or servers started for the test, and restore any external account or device state changed during the run.\n\n## Future Automation\n\n:::info\nExplain whether this manual test could later become automated. If yes, name the likely CI job, package, or test harness. If no, explain what keeps it manual.\n:::\n\nIf this scenario later becomes reliable and affordable to automate, replace or supplement this manual report with an automated test and link the PR here.\n",".claude/skills/frb-pr-chain-split/SKILL.md":"---\nname: frb-pr-chain-split\ndescription: Use when an FRB PR contains independently landable work that should be extracted into predecessor PRs and connected as a GitHub PR chain without wasting full automatic CI.\n---\n\n# 1 When to use\n\n- Use this skill while developing or reviewing a `flutter_rust_bridge` PR when part of its diff is independent of the PR's stated objective.\n- Split the independent work proactively. Do not wait for the user to notice the mixed scope.\n- Treat a change as independently landable when it:\n    - has a coherent behavior or maintenance objective of its own;\n    - can be implemented and validated against the current default branch;\n    - does not require the main task's API, schema, generated output, or version change;\n    - leaves the main PR smaller and easier to review.\n- Keep inseparable companion changes in the main PR. Generated snapshots, tests, and documentation required by the main change are not independent merely because they touch different files.\n\n# 2 Choose the chain boundary\n\nClassify every suspicious change before moving commits:\n\n| Classification | Destination | Reason |\n| --- | --- | --- |\n| Independent bug fix or hardening | Predecessor PR | It can land and provide value before the main task |\n| Independent mechanical refactor | Predecessor PR | It removes review noise from the main task |\n| Main-task prerequisite with its own contract | Predecessor PR | The main PR can depend on a separately reviewable foundation |\n| Generated output caused by the main task | Main PR | It cannot land meaningfully by itself |\n| Test or documentation for the main behavior | Main PR | Splitting would separate the contract from its implementation |\n\n- Prefer the smallest coherent predecessor, not the smallest textual diff.\n- Create several predecessor PRs when the extracted work contains multiple independent contracts.\n- Order predecessors by dependency. Do not infer order from commit timestamps.\n\n# 3 Build each predecessor\n\n- Fetch the canonical default branch before creating branches.\n- Create the first predecessor from the current default branch.\n- Create each later predecessor from the preceding predecessor branch.\n- Reconstruct the coherent final delta. Do not blindly cherry-pick commits that mix predecessor and main-task changes.\n- Include the predecessor's own tests and documentation.\n- Validate the predecessor independently before pushing.\n- Read `frb-prepare-pr` for generation, lint, test, and review requirements.\n- Use one branch and one PR per independently landable unit.\n- Push every chain branch to the same repository as the main PR. GitHub cannot use an unpushed local branch as a PR base.\n\n# 4 Construct the GitHub PR chain\n\n- Build a stacked branch chain, not sibling PRs that all target the default branch:\n\n```text\nmaster <- predecessor-a <- predecessor-b <- main-task\n```\n\n- Use GitHub's official `github/gh-stack` extension through `gh stack`.\n- Use `gh stack` for every stack operation: initialization, restructuring, linking, submission, synchronization, rebasing, pushing, and verification.\n- Never construct or maintain a stack with `gh pr create --base`, `gh pr edit --base`, direct API base edits, or branch ancestry alone.\n- Git commands may prepare the commits and linear branch ancestry, but the result is not complete until `gh stack` creates or updates the native GitHub stack object.\n- Check and install the extension when necessary. The extension requires `gh` 2.0 or later; a missing `gh stack` command normally means the extension is absent, not that core `gh` is outdated.\n\n```bash\ngh stack --help\ngh extension install github/gh-stack\n```\n\n- For an unpublished main branch, branch it directly from the final predecessor.\n- Initialize and submit a new local stack with `gh stack init` and `gh stack submit`.\n- For an already published main PR:\n    - create a timestamped backup tag before non-trivial history work;\n    - record the original final tree and three-dot PR diff;\n    - restack the main branch onto the final predecessor with a fully linear history;\n    - do not introduce merge commits because native GitHub stacks require linear ancestry;\n    - compare the rebuilt tree and PR delta with the recorded originals before pushing;\n    - use `--force-with-lease` only after those checks when published history must be rewritten.\n- Adopt existing branches or PRs with `gh stack link`. It pushes branches, creates missing PRs, corrects their base branches, and creates the native GitHub stack object.\n- Create and connect a new stack with commands shaped like:\n\n```bash\ngh stack init --base master <predecessor-branch> <main-task-branch>\ngh stack submit\n```\n\n- Adopt already prepared branches or existing PRs with:\n\n```bash\ngh stack link --base master <predecessor-branch> <main-task-branch>\n```\n\n- Verify local ancestry, native stack metadata, and PR bases:\n\n```bash\ngit merge-base --is-ancestor <predecessor-branch> <main-task-branch>\ngh stack view --json\n```\n\n- Compare three-dot diffs after constructing the chain:\n    - `<previous-branch>...<predecessor-branch>` contains only that predecessor;\n    - `<final-predecessor-branch>...<main-task-branch>` no longer contains the extracted work;\n    - the final main-task tree is unchanged except for intentional cleanup.\n\n# 5 Conserve CI on predecessor PRs\n\n- Read `frb-ci-filter` before manipulating CI.\n- Immediately add `ci-manual-dispatch` after creating every predecessor PR:\n\n```bash\ngh pr edit <predecessor-pr-number> --add-label ci-manual-dispatch\n```\n\n- Add the label even when local validation is already green. Predecessor PRs must not spend the full CI matrix merely because they were split for reviewability.\n- The label action triggers a new `pull_request:labeled` run. The workflow concurrency group cancels the earlier automatic PR run, and the labeled run produces an empty heavy-job plan.\n- Do not dispatch GitHub CI when local validation is sufficient.\n- When a predecessor specifically needs GitHub-only evidence:\n    - keep `ci-manual-dispatch` on the PR;\n    - validate the filter locally;\n    - dispatch only the smallest relevant job or matrix entry.\n\n```bash\n./frb_internal plan-ci --filter '<filter>'\ngh workflow run ci.yaml --ref <predecessor-branch> -f 'ci_filter=<filter>'\n```\n\n- Do not treat filtered CI as full merge-readiness evidence.\n- When a predecessor becomes the next PR to merge, remove `ci-manual-dispatch` and obtain the normal required CI unless the maintainer explicitly accepts narrower evidence.\n\n# 6 Completion checks\n\n- Confirm each predecessor can be reviewed without understanding the main task.\n- Confirm each PR's GitHub base matches its immediate predecessor branch.\n- Confirm Git ancestry matches the GitHub base chain.\n- Confirm the main PR diff excludes all extracted changes.\n- Confirm every predecessor PR has `ci-manual-dispatch` while it is not awaiting final full CI.\n- Report the PR numbers, branch order, validation performed, and any intentionally dispatched `ci_filter`.\n\n# 7 Reliability pitfalls\n\n- Do not create independent sibling PRs and merely call them a chain.\n- Do not substitute manual PR base edits for `gh stack link` or `gh stack submit`.\n- Do not treat matching branch ancestry as sufficient; verify the native stack with `gh stack view --json`.\n- Do not force-push a published main PR merely to make its history prettier.\n- Do not split generated files away from the generator or source change that owns them.\n- Do not leave an extracted commit duplicated in the visible main PR diff.\n- Do not spend full CI on every dormant predecessor.\n- Do not leave `ci-manual-dispatch` on the PR when claiming normal full-CI readiness.\n",".claude/skills/frb-pr-review/SKILL.md":"---\nname: frb-pr-review\ndescription: Review a flutter_rust_bridge PR before treating it as ready, including subagent checks for correctness and test weakening.\n---\n\n# 1 Human SoT\n\n# 2 FRB PR Review\n\nUse this before treating a non-trivial `flutter_rust_bridge` PR as ready, especially after CI-driven fixes, Flutter upgrades, generated drift, test changes, workflow changes, or broad merge conflict resolution.\n\n## 2.1 Review Gate\n\nRun the following review before final readiness:\n\n1. Review the PR diff against its base:\n   - Spawn a subagent to review correctness.\n     - Ask it to inspect the PR diff against the PR base.\n     - Focus on real behavior bugs, stale generated output, incorrect CI workarounds, coverage-only changes, and unrelated drift.\n     - Require findings with file paths, line numbers, impact, and suggested fix.\n   - Spawn a subagent to review test weakening.\n     - Inspect changed or deleted assertions, relaxed thresholds, new skips, ignored failures, timeout changes, and coverage exclusions.\n     - Require a concrete justification for each reduction in coverage; restore unjustified weakening and fix the underlying failure.\n     - If `tom-style-tests` is installed, apply its common test principles and weakening gate; use `frb-test` for FRB execution commands.\n     - Treat unjustified skipped tests, weaker assertions, broader ignores, fake timeouts, and coverage hiding as blockers.\n\n2. Write a concise review conclusion.\n   - Put the conclusion in the PR description or an agent-context draft when the user asks for a Markdown artifact.\n   - Include the subagents used, accepted findings, dismissed findings, fixes made, and remaining risks.\n\n## 2.2 Stop Condition\n\nDo not call the PR ready until:\n\n- Correctness review has no unresolved actionable findings and test-weakening review has no unjustified weakening.\n- CI status is green, or remaining non-green checks are clearly unrelated and explained.\n",".claude/skills/frb-prepare-pr/SKILL.md":"---\nname: frb-prepare-pr\ndescription: Use when about to create a PR or push changes in flutter_rust_bridge to ensure code generation is up to date\n---\n\n# FRB Prepare for PR\n\n> **Note:** Read `frb-dev-env` before running commands and follow the active user's environment rules.\n\n## Overview\n\nBefore creating a PR, ensure generated code is up to date, lint passes, and bug fixes have a concrete reproduction report in the PR description. Before treating a non-trivial PR as ready, run `frb-pr-review`.\n\n**Core principle:** Generate → Lint → Commit → PR.\n\n> **After codegen:** Follow `frb-dev-env` and verify generated output is present in the selected worktree before reviewing the diff.\n\n## Workflow\n\n```\n1. Read frb-code-generation skill\n   |\n   +-- Run required generation commands\n   |\n   +-- 2. For integrate output diffs, verify templates and run precommit-integrate\n   |\n   +-- 3. Read frb-lint skill --> Run lint --fix\n   |\n   +-- 4. (Optional) Read frb-test skill --> Run relevant tests\n   |\n   +-- 5. For non-trivial PRs, read frb-pr-review --> Run review gate\n   |\n   +-- 6. For bug fixes, add the reproduction report to the PR description\n   |\n   +-- 7. Commit all changes\n   |\n   +-- 8. Create PR (follow the active user-level PR workflow)\n```\n\n## Quick Checklist\n\n1. [ ] **REQUIRED:** Read `frb-code-generation` skill, run commands if needed\n2. [ ] **REQUIRED for integrate output diffs:** If the PR changes Flutter integrate example outputs or platform scaffolds, confirm whether `frb_codegen/assets/integration_template/` is the source that should change, then run `./frb_internal precommit-integrate`\n3. [ ] **REQUIRED for copied Cargokit diffs:** If the PR changes copied `cargokit` files under `frb_example/**`, read `frb-cargokit`, then run `./frb_internal sync-cargokit-copies`\n4. [ ] **REQUIRED:** Read `frb-lint` skill, run `./frb_internal lint --fix`\n5. [ ] (Optional) Read `frb-test` skill, run relevant tests\n6. [ ] **REQUIRED for non-trivial PRs:** Read `frb-pr-review`, run the review gate before final readiness\n7. [ ] **REQUIRED for bug fixes:** PR description includes the reproduction report from `frb-develop-feature`, including baseline commit, mechanical steps, observed failure, and expected behavior\n8. [ ] **REQUIRED for CI iteration branches:** Read `frb-ci-filter`, then remove `ci-manual-dispatch` and rerun normal CI if the PR used filtered CI, unless the PR is explicitly about changing CI behavior\n9. [ ] Commit all changes\n10. [ ] Push and create PR\n\n## What CI Will Do\n\nCI automatically runs:\n- Code generation check (fails if generated code is outdated)\n- All tests (Rust, Dart, Flutter, Web)\n- Lint and format checks\n\nRun lint locally to avoid CI failures. Tests are optional locally.\n\nWhile iterating, it can be useful to dispatch `.github/workflows/ci.yaml` with `ci_filter` so only the relevant job family or matrix entry runs. Read `frb-ci-filter` before doing this. Do not leave `ci-manual-dispatch` on the final PR unless changing CI behavior is the actual purpose of the PR.\n\nIf your PR fixes Flutter integrate example outputs and the real bug is inside the embedded `cargokit` submodule, do not stop at copied example files. Push the `cargokit` fix to `fzyzcjy/cargokit` and update the submodule ref in this repo before pushing the PR branch.\n\nIf the PR changes integrate-generated example output but not `frb_codegen/assets/integration_template/`, explicitly verify that the output-only change is intentional. Most integrate scaffold behavior should be fixed in the template and regenerated, not patched only in `frb_example/**`.\n\n## Related Skills\n\n- `frb-code-generation` - Determines which generation commands to run\n- `frb-lint` - Lint and format checks\n- `frb-test` - For local debugging when CI fails\n- `frb-pr-review` - PR readiness review gate\n- `frb-ci-filter` - Filtered CI dispatch rules\n- Follow the active user-level PR workflow when available; otherwise use `gh pr create` with the repository's title and body conventions.\n",".claude/skills/frb-publish-release/SKILL.md":"---\nname: frb-publish-release\ndescription: \"Publish a flutter_rust_bridge release end to end: preflight checks, changelog preparation, frb_internal release publishing, released-version polling, CI babysitting, and post-release CI verification.\"\n---\n\n# FRB Publish Release\n\nUse this skill when preparing, publishing, or babysitting a `flutter_rust_bridge` release.\n\n## Workflow\n\n### 1. Preflight\n\n- Work from the repository root on the intended release branch, normally fresh `master`. If the checkout is detached but already points at the intended release commit, switch or create a local release branch from that commit before running mutating release commands; do not treat detached HEAD itself as a release blocker.\n- Check `git status --short --branch` and do not start publishing from a dirty tree.\n- Check submodules before publishing:\n\n  ```bash\n  git submodule update --init --recursive\n  git submodule status --recursive\n  ```\n\n  Stop if any submodule path remains uninitialized. A release built without initialized submodules can publish incomplete package contents. The release script performs the same submodule-status guard before publishing.\n- Treat `master` as a moving branch. Before each irreversible release phase, fetch `origin master`, confirm the current release branch or detached checkout still points at the intended latest `origin/master` commit, and re-run `git status --short --branch` to confirm there are no unexpected local changes.\n- Confirm the target version in `CHANGELOG.md`, root `Cargo.toml`, and `frb_dart/pubspec.yaml`.\n- Compute the release versions the same way `./frb_internal release` does: the top `CHANGELOG.md` version is the new version and the next release section is the old version.\n- Verify both old and new versions are legal before running any mutating release command. The only allowed shapes are stable SemVer `MAJOR.MINOR.PATCH` such as `2.0.0`, or beta SemVer `MAJOR.MINOR.PATCH-beta.N` such as `2.0.0-beta.1`. Use exactly `^\\d+\\.\\d+\\.\\d+(-beta\\.\\d+)?$`.\n- Reject versions with any other prerelease label, build metadata, missing numeric components, leading `v`, or loose text. Stop if either old or new version fails this check.\n- Confirm the new version is different from the old version.\n- Run the publish container credential preflight before starting irreversible publish steps:\n\n  ```bash\n  .claude/skills/frb-dev-env/frb_dev_env.py docker-run-rm --with-publish-credentials -- true\n  ```\n\n  Stop if the preflight fails. It checks GitHub CLI auth, Cargo credentials, and Dart pub credentials inside the same temporary credential layout used by release publishing.\n- Confirm normal CI is green for the release commit before publishing. This is the default hard gate.\n\n  A narrow exception is allowed only when all of these are true:\n\n  - A recent earlier commit on the same release branch has fully green normal CI.\n  - Every commit between that green commit and the intended release commit is unrelated to the publishable packages, version sources, generated release artifacts, and `frb_internal` release logic.\n  - The mechanical gate script below exits successfully and its output is recorded in the release notes or journal.\n  - The agent explicitly states that it is using the exception before publishing.\n\n  Run the mechanical gate from the repository root:\n\n  ```bash\n  uv run --script .claude/skills/frb-publish-release/release_ci_gate.py \\\n    --base-green-ref <LAST_GREEN_SHA> \\\n    --release-ref HEAD\n  ```\n\n  Stop and wait for normal CI if the script reports any `BLOCK` path. The script is intentionally conservative: release-surface paths such as `Cargo.toml`, `frb_dart/pubspec.yaml`, `frb_codegen/**`, `frb_macros/**`, `frb_rust/**`, `frb_utils/**`, `frb_example/**`, `tools/frb_internal/**`, `pubspec.yaml`, `melos.yaml`, and lockfiles are not in the allowlist. Release metadata and its generated copies are allowed: `CHANGELOG.md`, `frb_dart/CHANGELOG.md`, `frb_hooks/CHANGELOG.md`, `.all-contributors-custom.yaml`, `.all-contributorsrc`, `README.md`, `frb_dart/README.md`, and `website/**`. These files are reviewed directly before publishing and do not affect package execution. Other `frb_dart/**` and `frb_hooks/**` paths remain blocked. This exception is for release metadata, docs, agent tooling, devcontainer, selected CI configuration, and other explicitly non-release paths only.\n\n### 2. Reconcile Contributors\n\n- Before writing the release changelog, use `frb-add-contributor` to identify contributors from the target release range who may need all-contributors credit or whose human-written custom description does not cover every release-range PR.\n- Follow `frb-add-contributor` exactly. In particular, after determining which contributors may need to be added or have their custom descriptions extended, stop for human confirmation before editing contributor files, posting GitHub comments, triggering all-contributors, opening PRs, or merging contributor PRs.\n- Do not continue to changelog preparation until contributor reconciliation is complete, or confirmed unnecessary because all contributors are credited and their custom descriptions cover every third-party PR in the release range.\n\n### 3. Write Changelog\n\n- Use `frb-write-changelog` and follow it exactly. It is the single source of truth for beta and stable changelog content, grouping, ordering, attribution, review, verification, and generated package changelog copies. Do not duplicate those rules here.\n- Do not publish until the human-review gate required by `frb-write-changelog` is satisfied.\n- The top `CHANGELOG.md` version is the source used by `frb_internal release`.\n- If changelog or version files changed, commit that release preparation before publishing.\n- Land contributor and changelog preparation commits on `origin/master` before publishing. Do not leave release preparation only on a side branch.\n\n### 4. Pre-Publish Independent Audit\n\n- Immediately before the first irreversible publish command, delegate a read-only audit to an independent subagent that has not participated in preparing the release.\n- Give the subagent the intended version and the raw evidence needed to verify the work completed so far:\n  - Current branch, exact `HEAD`, latest `origin/master`, and `git status --short --branch`.\n  - Previous and target release versions from `CHANGELOG.md`, root `Cargo.toml`, and `frb_dart/pubspec.yaml`.\n  - Final target changelog section, contributor reconciliation result, human changelog approval, and relevant diffs.\n  - Submodule status, credential preflight result, and normal CI green evidence or evidence that every narrow CI-gate exception condition in Step 1 is satisfied, including successful `release_ci_gate.py` output.\n  - Exact commands and results for any additional focused release tests that were run. These tests do not replace the normal CI gate or its defined narrow exception.\n  - Exact one-shot publish command that will run next.\n- Ask the subagent to return one verdict:\n  - `GO`: all release instructions are satisfied and the evidence is internally consistent.\n  - `NO-GO`: list every blocker, missing artifact, ambiguity, or inconsistent result with concrete evidence.\n- Do not tell the subagent the desired verdict or provide only a prepared summary. Let it inspect the repository and raw command outputs independently.\n- Stop before publishing on `NO-GO`, an ambiguous verdict, missing evidence, or unavailable subagent capability. Resolve every blocker and run a fresh independent audit.\n- Treat the audit as valid only for the exact audited `HEAD`, `origin/master`, clean worktree state, evidence snapshot, and next publish command.\n- After a `GO` verdict, run that exact publish command without any intervening mutation. If the command changes, the worktree becomes dirty, evidence is refreshed or invalidated, or `HEAD` or `origin/master` changes, repeat the affected preflight checks and the independent audit.\n\n### 5. Publish\n\nUse the repository release command through a temporary Docker container with publish credentials as the normal publishing path:\n\n```bash\n.claude/skills/frb-dev-env/frb_dev_env.py docker-run-rm --with-publish-credentials -- ./frb_internal release\n```\n\nRun the one-shot command from a clean local `master` that tracks the latest `origin/master`. `release-update-git` uses plain `git push`, so do not run the command from a side release branch.\n\nDo not split the normal release into separate `release-update-*` or publish commands. The one-shot command is the source of truth for release sequencing: it computes old/new versions from `CHANGELOG.md`, updates checked-in versions and generated version text, updates Scoop metadata, commits and pushes the version bump, creates the GitHub release, then runs the registry publisher:\n\n```bash\n.claude/skills/frb-dev-env/frb_dev_env.py docker-run-rm --with-publish-credentials -- ./frb_internal release-publish-all\n```\n\nFor beta versions such as `2.13.0-beta.1`, do not require the GitHub release to be labeled as a pre-release. FRB intentionally publishes beta GitHub releases as normal GitHub releases while the package version itself remains a SemVer prerelease.\n\n`release-publish-all` publishes these packages:\n\n- `frb_codegen` -> crates.io package `flutter_rust_bridge_codegen`\n- `frb_macros` -> crates.io package `flutter_rust_bridge_macros`\n- `frb_rust` -> crates.io package `flutter_rust_bridge`\n- `frb_dart` -> pub.dev package `flutter_rust_bridge`\n- `frb_hooks` -> pub.dev package `flutter_rust_bridge_hooks`\n\nOnly use a split subcommand as a recovery path after confirming which one-shot step already completed. For example, if the version bump and GitHub release already exist and only registry publication is needed, use `.claude/skills/frb-dev-env/frb_dev_env.py docker-run-rm --with-publish-credentials -- ./frb_internal release-publish-all` directly.\n\n### 6. Check Released Versions\n\nPoll registry state with:\n\n```bash\n./frb_internal get-released-version\n```\n\nTo check a target version from a checkout whose manifests have not been bumped, pass it explicitly:\n\n```bash\n./frb_internal get-released-version --version <VERSION>\n```\n\nThe command prints JSON:\n\n```json\n{\n  \"allReleased\": true,\n  \"packages\": [\n    {\n      \"registry\": \"crates.io\",\n      \"name\": \"flutter_rust_bridge_codegen\",\n      \"manifestVersion\": \"2.12.0\",\n      \"releasedVersion\": \"2.12.0\",\n      \"isReleased\": true\n    }\n  ]\n}\n```\n\nWait until every package has `isReleased: true` and `allReleased: true`. If one registry lags, keep polling with bounded waits and record the mismatched package instead of assuming the publish failed. Do not add the target version option to the mutating `release-*` subcommands; they derive their version from `CHANGELOG.md` and checked-in manifests.\n\nFor beta releases, `get-released-version --version <VERSION>` must verify the pub.dev `versions` list, not only pub.dev's `latest` field, because pub.dev keeps `latest` on the latest stable release when a prerelease is uploaded.\n\n### 7. Babysit CI And Post-Release CI\n\n- Keep watching the release commit's normal CI until it is green.\n- After `./frb_internal get-released-version` reports `allReleased: true`, trigger `.github/workflows/post_release.yaml` for the release commit or `master`.\n- Babysit post-release CI until it is green. Use the user's `tom-ci` skill when reading GitHub Actions logs.\n- If post-release fails, classify the failure by release channel (`stable` or `unstable`) and install mode (`cargo-install`, `cargo-binstall`, `scoop`, or `homebrew`) before changing code or rerunning.\n\n## Related Skills\n\n- `frb-write-changelog` for the release section.\n- `frb-fix-ci` or `frb-fix-main-ci` for CI failures.\n- The user's `tom-ci` skill for GitHub Actions logs.\n",".claude/skills/frb-tart-prepare/SKILL.md":"---\nname: frb-tart-prepare\ndescription: Use when preparing or explaining the FRB Tart base VM for iOS Simulator validation, including local OCI registry mirroring, Packer provisioning, and base VM hygiene.\n---\n\n# FRB Tart Base VM Preparation\n\nUse this skill when preparing or explaining the reusable Tart base VM used by `frb-dev-env tart ...`.\n\n## Source Of Truth\n\nRead the checked-in guide first:\n\n```text\ntools/tart_macos/README.md\n```\n\nThat README owns the detailed Tart source image, local OCI registry, Packer build, smoke test, promotion, cost, and base hygiene instructions. Keep this skill thin so the guide remains the audited source of truth for the Tart base VM workflow.\n\n## Required Reminders\n\n- `frb-tart-base` is immutable after promotion; do not boot it and manually install tools into it.\n- Durable base image changes belong in `tools/tart_macos/packer/frb-tart-base.pkr.hcl` or its provision scripts.\n- Use `frb-tart-base-candidate` for rebuilds, smoke test the candidate, then promote it to `frb-tart-base`.\n- For daily per-worktree Tart usage, read the Tart section inside `.claude/skills/frb-dev-env/SKILL.md`.\n",".claude/skills/frb-test/SKILL.md":"---\nname: frb-test\ndescription: Use when needing to run tests locally in flutter_rust_bridge, or when debugging test failures before pushing\n---\n\n# FRB Testing\n\n> **Note:** Read `frb-dev-env` before running commands and follow the active user's environment rules.\n\n## Overview\n\nMaps change types to local test commands. Tests run on CI by default, so local testing is optional but useful for debugging.\n\n## Quick Reference\n\n| Change Type | Command |\n|-------------|---------|\n| All Rust packages | `./frb_internal test-rust` |\n| `frb_rust/` | `./frb_internal test-rust-package --package frb_rust` |\n| `frb_codegen/` | `./frb_internal test-rust-package --package frb_codegen` |\n| Specific Rust package | `./frb_internal test-rust-package --package <path>` |\n| `frb_dart/` | `./frb_internal test-dart-native --package frb_dart` |\n| Dart example | `./frb_internal test-dart-native --package frb_example/<name>` |\n| Flutter example | `./frb_internal test-flutter-native --package frb_example/flutter_<name>` |\n| Web test | `./frb_internal test-dart-web --package frb_example/<name>` |\n\n## Web Tests\n\nWeb tests compile Dart tests to WebAssembly and run in a headless browser.\nSome individual web test cases are flaky; rerun the failed web test job or command before treating the failure as deterministic.\n\n### Supported Packages\n- `frb_dart`\n- `frb_example/dart_minimal`\n- `frb_example/pure_dart`\n- `frb_example/pure_dart_pde`\n\n### Requirements\n- `wasm32-unknown-unknown` target: `rustup target add wasm32-unknown-unknown --toolchain nightly-2025-02-01`\n- `rust-src` component: `rustup component add rust-src --toolchain nightly-2025-02-01`\n- `wasm-pack`: `curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh`\n- Chrome/Chromium browser\n\n### Running in Docker\nWhen running web tests in Docker containers, puppeteer needs `--no-sandbox` flag. The test runner auto-detects Docker environment via `/.dockerenv`.\n\n## When to Test Locally\n\n**Run:** Debugging test failure, developing new tests, verifying fix before push\n\n**Skip:** Documentation only, CI will catch it, just checking compilation\n\n## CI Discipline\n\nCI is slow and often queues for a long time. Before pushing a non-trivial FRB change, run meaningful local tests in the appropriate development environment and understand the result locally first. Scale the local test scope with the risk and blast radius of the change. Do not use CI as the first feedback loop for changes that can be verified locally.\n\nChoose tests by blast radius:\n- Runtime changes: run the relevant Rust package tests\n- Generated or example API changes: run the focused Dart or Flutter example test first\n- Codegen changes: run generation and the affected example tests\n\nAfter pushing to a PR branch, monitor GitHub Actions until the run reaches a terminal state. If CI fails, inspect the failing job logs and either fix the issue or clearly report why it is unrelated or flaky. Do not leave a PR in an unknown queued or in-progress state when the user asked for CI validation.\n\nBefore treating a non-trivial PR as ready, run the review gate in `frb-pr-review`; local tests alone do not check correctness review or test weakening.\n",".claude/skills/frb-test/tests/test-application-1.md":"# Test Scenario: Application 1 - Rust Codegen Change\n\n**Type:** Application scenario\n**Skill under test:** frb-test\n\n## Prompt\n\n```\nI just modified some code in frb_codegen/src/library/api_dart/mod.rs to fix a bug in Dart code generation.\nWhich tests should I run locally to verify this change works correctly?\n```\n\n## Expected Behavior\n\n- Correct command: `./frb_internal test-rust-package --package frb_codegen`\n",".claude/skills/frb-test/tests/test-application-2.md":"# Test Scenario: Application 2 - Example API Change\n\n**Type:** Application scenario\n**Skill under test:** frb-test\n\n## Prompt\n\n```\nI added a new function `hello_twin_normal` to frb_example/pure_dart/rust/src/api/misc.rs\nand wrote a test in frb_example/pure_dart/test/api/misc_test.dart.\nThe CI is failing. How can I reproduce and debug this locally?\n```\n\n## Expected Behavior\n\n- Correct command: `./frb_internal test-dart-native --package frb_example/pure_dart`\n",".claude/skills/frb-test/tests/test-gap-1.md":"# Test Scenario: Gap 1 - Flutter Integration Test\n\n**Type:** Gap scenario\n**Skill under test:** frb-test\n\n## Prompt\n\n```\nI modified frb_example/flutter_via_create and want to run the integration tests locally.\nWhat command should I use?\n```\n\n## Expected Behavior\n\n- Correct command: `./frb_internal test-flutter-native --package frb_example/flutter_via_create`\n",".claude/skills/frb-test/tests/test-variation-1.md":"# Test Scenario: Variation 1 - Web Test Request\n\n**Type:** Variation scenario\n**Skill under test:** frb-test\n\n## Prompt\n\n```\nI need to verify my changes work on the web platform.\nHow do I run web tests for frb_dart locally?\n```\n\n## Expected Behavior\n\n- Correct command: `./frb_internal test-dart-web --package frb_dart`\n",".claude/skills/frb-test/tests/test-variation-2.md":"# Test Scenario: Variation 2 - Multiple Packages Changed\n\n**Type:** Variation scenario\n**Skill under test:** frb-test\n\n## Prompt\n\n```\nI made changes to both frb_rust and frb_dart.\nWhat tests should I run to verify everything works?\n```\n\n## Expected Behavior\n\n- Both packages identified\n- Commands:\n  - `./frb_internal test-rust-package --package frb_rust`\n  - `./frb_internal test-dart-native --package frb_dart`\n",".claude/skills/frb-upgrade-docker/SKILL.md":"---\nname: frb-upgrade-docker\ndescription: Upgrade and publish the flutter_rust_bridge development Docker image, including toolchain bumps, image build fixes, candidate tags, and default-branch promotion.\n---\n\n# 1 Scope the PR\n\n- Put every development-image upgrade in an independent PR targeting `master`. Make it the first PR in a toolchain-upgrade chain.\n- Include all Docker-owned changes required by the upgrade:\n  - `.devcontainer/Dockerfile` toolchain and system packages;\n  - `.github/workflows/publish_dev_docker.yaml` build, test, and publication logic;\n  - image metadata tests, tag derivation, and Docker-specific supporting code.\n- Leave FRB runtime fixes, generated scaffolds, and example migrations to later PRs.\n- Read `frb-docker` for local use and `frb-dev-env` before running repository commands in a container.\n\n# 2 Build before merge\n\n- Before pushing the Docker upgrade PR, follow `frb-docker`'s local Dockerfile validation workflow with a freshly\n  built image. Do not validate with a stale per-worktree container.\n- Make Dockerfile and publish-workflow PRs automatically build and smoke-test native `linux/amd64` and `linux/arm64` images with `push: false`.\n- Do not expose registry credentials to untrusted pull-request code.\n- Derive image tags from Dockerfile `ARG` values instead of duplicating versions.\n- Verify Flutter, Dart, Rust, the pinned nightly and wasm target, Node, Yarn, `wasm-pack`, Chrome, ChromeDriver, and `./frb_internal --help`.\n- For a manual build-only check, run:\n\n```shell\ngh workflow run publish_dev_docker.yaml --ref <branch> -f publish=false\n```\n\n# 3 Publish safely\n\n- If later PRs need the image before merge, let a maintainer explicitly publish only an immutable\n  `candidate-pr-<number>-sha-<short_sha>` tag. Reject a conflicting existing tag; allow only an identical retry.\n- Never update `latest`, canonical version tags, or stable SHA tags from pull-request code.\n- Publish stable tags only from a push to `master`. A manual stable dispatch must check out the current remote\n  `master` head, not an older reachable commit.\n- After merge, publish from `master`:\n\n```shell\ngh workflow run publish_dev_docker.yaml --ref master\n```\n\n- Stable publication may then update:\n  - `latest`;\n  - `flutter-<flutter>-rust-<rust>-nightly-<nightly>`;\n  - the version-plus-code and stable SHA tags.\n\n# 4 Verify and hand off\n\n- Inspect the candidate when used, then inspect the canonical tag and `latest` after merge:\n\n```shell\ndocker buildx imagetools inspect fzyzcjy/flutter_rust_bridge_dev:<tag>\ndocker inspect fzyzcjy/flutter_rust_bridge_dev:latest \\\n  --format '{{ index .Config.Labels \"org.opencontainers.image.revision\" }}'\n```\n\n- Require `linux/amd64` and `linux/arm64`; ignore BuildKit attestation entries reported as `unknown/unknown`.\n- Merge latest `master` into the remaining chain, switch it from any candidate to the canonical tag, and do not merge\n  a consumer before that tag exists.\n- Diagnose Docker Hub login, per-platform pushes, and manifest creation separately.\n- If local `latest` is stale, use the exact candidate/canonical tag or build the Dockerfile locally; do not generate\n  locks or scaffolds with the wrong toolchain.\n",".claude/skills/frb-upgrade-flutter/SKILL.md":"---\nname: frb-upgrade-flutter\ndescription: >-\n  Upgrade flutter_rust_bridge to a new Flutter stable release. Use when changing Flutter/Dart versions,\n  devcontainer Docker images, CI/post-release pins, generated Flutter scaffolds, or platform compatibility.\n---\n\n# 1 Start here\n\n- Confirm the target Flutter stable release from official Flutter sources.\n- Read `frb-dev-env` before running setup, generation, lint, or tests. Use its per-worktree local Docker workflow.\n- Read `frb-upgrade-docker` when the target toolchain changes the development image.\n- Read `frb-docker` for ordinary local Docker usage.\n- Read `frb-code-generation` before accepting generated or scaffold drift.\n- Read `frb-cargokit` before changing any copied CargoKit file. Read `frb-cargokit-dev` only when the source\n  change belongs in the external CargoKit repository.\n- Read `frb-pr-chain-split` as soon as an independently landable prerequisite or cleanup appears.\n- Read `frb-prepare-pr` and then `frb-pr-review` before treating the upgrade PR as ready.\n- Read `frb-fix-ci` when CI starts failing.\n\n# 2 Establish the version contract\n\n- Record the target Flutter version, bundled Dart version, release date, and relevant release-note changes.\n- Distinguish the primary Dart version from the minimum supported Dart SDK floor.\n  - Upgrade the primary Dart pin with Flutter.\n  - Do not raise package SDK constraints merely because the primary toolchain is newer.\n  - When retaining an older SDK floor, run dependency resolution, analysis, and tests on that floor in CI.\n- Inventory version-like values without assuming the target Dart major or minor:\n\n```shell\nrg -n \"FRB_MAIN_|FLUTTER_VERSION|DART_VERSION|RUST_VERSION|setup-flutter|setup-dart|cirruslabs/flutter\"\nrg -n \"flutter_rust_bridge_dev|stable|nightly|minimum.*version|deployment.*target\" \\\n  .devcontainer .github tools frb_codegen frb_example\n```\n\n- Inspect at least:\n  - `.devcontainer/Dockerfile`;\n  - `.github/workflows/ci.yaml` and `.github/workflows/post_release.yaml`;\n  - `.github/workflows/publish_dev_docker.yaml`;\n  - `tools/frb_internal/test/src/makefile_dart/test_dev_docker_metadata.dart`;\n  - package `pubspec.yaml` and checked-in `pubspec.lock` files;\n  - `frb_codegen/assets/integration_template/**`;\n  - `tools/frb_internal/assets/apple_scaffold/**`;\n  - `tools/tart_macos/**`;\n  - `frb_example/**`.\n\n# 3 Choose PR boundaries before implementation\n\n- When the development image changes, make its upgrade the first independent predecessor PR targeting `master`.\n  Follow `frb-upgrade-docker` for its contents, candidate image, merge order, and stable-tag promotion.\n- Keep generated snapshots, their generator changes, required tests, and upgrade-specific compatibility migrations in\n  the main upgrade PR.\n- Split independent bug fixes, hardening, and reusable prerequisites into predecessor PRs according to\n  `frb-pr-chain-split`.\n- Build and maintain any predecessor chain exclusively with the official `gh stack` workflow described there.\n- Put `ci-manual-dispatch` on dormant predecessors unless a predecessor specifically needs GitHub-only validation.\n- Do not move incidental skill or workflow cleanup into the upgrade PR when it can stand alone against `master`.\n\n# 4 Upgrade the development toolchain\n\n- Complete the independent Docker predecessor through `frb-upgrade-docker` before relying on the upgraded image.\n- Use its immutable candidate tag for pre-merge validation when necessary; never move `latest` from a PR branch.\n- After the predecessor merges and publishes stable tags, merge latest `master` into the remaining upgrade chain and\n  use the canonical version tag.\n- If Apple pins, simulators, or host tooling change, continue with the Tart workflow routed by `frb-dev-env` and\n  read `frb-tart-prepare` before provisioning or validation.\n\n# 5 Synchronize CI and post-release pins\n\n- Update the top-level toolchain values together in `.github/workflows/ci.yaml` and\n  `.github/workflows/post_release.yaml`:\n  - `FRB_MAIN_FLUTTER_VERSION`;\n  - `FRB_MAIN_DART_VERSION`;\n  - `FRB_MAIN_RUST_VERSION` when the upgraded tooling requires it;\n  - `FRB_RUSTFMT_NIGHTLY_VERSION` only when formatting or nightly `rust-src` behavior requires it.\n- Keep a retained minimum Dart floor explicit and independently exercised; do not substitute the primary Dart pin for\n  that compatibility lane.\n- Review Java, Android SDK/NDK, Chrome/chromedriver, macOS runner, simulator, Windows ARM, and post-release install-mode\n  assumptions.\n\n# 6 Regenerate through owner workflows\n\n- Follow `frb-code-generation` for command selection, convergence, generated-output provenance, legacy scaffold migrations, and OHOS integrate composition.\n- Follow `frb-cargokit` and `frb-cargokit-dev` for CargoKit ownership and synchronization.\n\n# 7 Validate in dependency order\n\n- Use the environment selected by `frb-dev-env`. When the Docker image changed, use the candidate or canonical image\n  selected by `frb-upgrade-docker`; do not reuse a stale per-worktree container.\n- Read `frb-lint` and `frb-test` for exact commands.\n- Validate in this order:\n  1. dev-image metadata and tool versions;\n  2. Dart analysis, tests, and the retained minimum-SDK lane;\n  3. internal generation and second-pass cleanliness;\n  4. integration generation and CargoKit synchronization;\n  5. focused legacy Android and Apple platform builds affected by migrations;\n  6. representative native and web examples.\n- Before creating or updating the PR, run `frb-prepare-pr`.\n- Before declaring it ready, run the independent review gate in `frb-pr-review`.\n\n# 8 Triage CI and finish\n\n- Read `frb-fix-ci` before deep CI debugging.\n- Triage failures in dependency order: environment setup, generation, integration, platform builds, post-release,\n  then coverage or uploads.\n- Compare platform failures with target-Flutter release notes and fresh scaffold output before adding workarounds.\n- Keep full CI on the top upgrade PR. Follow `frb-pr-chain-split` for filtered or deferred predecessor CI.\n- The Docker predecessor must already be merged and stably published through `frb-upgrade-docker`; do not defer image\n  publication until the main Flutter upgrade PR merges.\n\n# 9 PR notes\n\n- Record old and new Flutter and primary Dart versions.\n- Record the retained or raised minimum Dart SDK floor and its validation lane.\n- List generated files, direct Flutter migrator edits, CargoKit changes, and OHOS overlays by provenance.\n- Link predecessor PRs and identify which ones intentionally use manual CI.\n- Record the fresh dev-image tag, exact local validations, generation convergence result, and CI status.\n- Call out any platform-specific follow-up that remains intentionally out of scope.\n",".claude/skills/frb-vmware-windows-prepare/SKILL.md":"---\nname: frb-vmware-windows-prepare\ndescription: Use when preparing the FRB VMware Fusion Windows 11 Arm base VM.\n---\n\n# FRB VMware Windows Base VM Preparation\n\nUse this skill when preparing or explaining the reusable Windows 11 Arm base VM used by `tools/vmware_windows/frb_vmware_windows_env.py`.\n\n## Source Of Truth\n\nRead the checked-in guide first:\n\n```text\ntools/vmware_windows/README.md\n```\n\nThat README owns the detailed Packer, Vagrant, VMware Fusion, proxy, storage, host-installation, daily-use, and troubleshooting instructions. Keep this skill thin so the guide remains the audited source of truth for the Windows VM workflow.\n\n## Required Reminders\n\n- Heavy Windows VM artifacts belong under the per-user `vm_root` configured in\n  `~/.config/flutter_rust_bridge/vmware_windows.json` or `FRB_WINDOWS_VM_ROOT`.\n- Host-side Windows VM downloads and Packer network operations use `host_proxy_url` or\n  `FRB_WINDOWS_HOST_PROXY_URL` only when a local network requires a proxy.\n- Durable base image changes belong in the Packer template or PowerShell provision scripts under `tools/vmware_windows/`, not in manual mutations of a promoted base VM.\n- For daily Windows VM usage, read the Windows VMware section inside `.claude/skills/frb-dev-env/SKILL.md`.\n",".claude/skills/frb-write-changelog/SKILL.md":"---\nname: frb-write-changelog\ndescription: Update flutter_rust_bridge CHANGELOG.md for a new release by collecting merged PRs since the previous version tag, mapping them to the target release section, and formatting entries to match the existing changelog style. Use when writing or refreshing a release section in CHANGELOG.md.\n---\n\n# FRB Write Changelog\n\n## Step 1: Inspect `CHANGELOG.md`\n\nRead the top of `CHANGELOG.md`.\n\n- Confirm the target version section exists, or add it.\n- Keep the existing V2 \"what's new\" header line.\n- Inspect `git diff -- CHANGELOG.md` before editing.\n- Replace only the target section if it already contains a placeholder such as `* TODO`.\n\n## Step 2: Identify the previous release tag\n\nFind the latest release tag before the target version.\n\n```bash\ngit tag --sort=-creatordate | head -n 20\n```\n\nUse that tag as the lower bound for the new changelog entry.\n\n## Step 3: Collect merged PRs\n\nUse GitHub CLI to collect merged PRs after the previous version.\n\n```bash\ngh pr list --state merged --limit 200 --json number,title,author,mergedAt,baseRefName,url\ngh pr view <number> --json number,title,author,url\n```\n\nUse the previous version as the lower bound when deciding which merged PRs belong to the target release.\n\n## Step 4: Filter and normalize\n\nKeep all PRs in merged status that belong to the release range.\n\n- Exclude unmerged PRs.\n- Exclude PRs outside the release range.\n- When refreshing an already-published release, use both the previous release timestamp and the target release tag timestamp so later post-release PRs are not pulled into the published section.\n- Do not filter by target branch.\n- Keep docs, CI, and chore PRs if they are merged in the range.\n- Exclude only all-contributors PRs such as `docs: add <name> as a contributor for code/doc`; do not exclude ordinary documentation PRs whose titles start with `docs: add`.\n\nNormalize titles before writing.\n\n- Rewrite noisy internal titles into concise changelog language.\n- Avoid duplicate summaries for split or \"continued\" PRs.\n- Preserve repo-specific capitalization such as `CI`, `GitHub`, `Flutter`, `Rust`, `DCO`, and `V1`.\n\n## Step 5: Write the section\n\nMatch the existing changelog style.\n\n- Write each item as `* Summary #1234`.\n- Append `(thanks @username)` for every third-party human-authored PR in the release range, including docs, CI, chore, tooling, and feature PRs.\n- When a changelog entry combines local maintainer PRs with a third-party PR, append the third-party thanks to that combined entry.\n- If multiple third-party authors appear in one combined entry, include each author in the same entry.\n- For an ordinary beta release section, place entries with `(thanks @username)` before entries without thanks. Within each group, keep merge order from newest to oldest unless the surrounding section clearly uses another order.\n\nFor a stable release that follows betas of the same version series, use a grouped stable section:\n\n- Put only changes merged after the latest beta release first as ordinary top-level entries. Do not include an older change solely because the beta changelogs omitted it.\n- Then add one top-level parent entry per beta, using the canonical version label such as `* 2.13.0-beta.6`, ordered from the latest beta to the earliest beta.\n- Copy that beta's substantive entries exactly beneath its parent as nested bullets. Preserve wording, PR references, and thanks attribution.\n- Keep the generic \"what's new\" entry only once at the top of the stable section.\n- Never fold a post-latest-beta PR into a copied beta entry. Keep it in the leading group even when its subject is related to a beta entry.\n- Apply thanks-first ordering independently within each copied beta group. In the post-latest-beta group, user-visible-before-internal ordering takes precedence over thanks placement.\n\nFor newly written entries, keep every user-visible feature, fix, behavior change, published-package change, and user-facing documentation PR as its own entry. Do not join such changes with `and` or fold multiple PRs into a shared summary. Combining multiple PRs is allowed only for user-invisible internal work such as CI, tests, agent or release tooling, and code outside published packages. Order the user-visible entries first, then put all user-invisible internal entries at the end of the post-latest-beta group immediately before the first beta parent entry. Copied beta entries remain exact copies even if they predate this rule.\n\nEdit only `CHANGELOG.md`. Do not manually edit generated files for this task.\n\n## Step 6: Review the draft\n\nReview the final diff.\n\n- Confirm the change is limited to the target release section.\n- Confirm wording and ordering match nearby release sections.\n- Do not commit yet. Human review can still change the text, and the package changelog copies must be regenerated from the final root changelog.\n\n## Step 7: Run mechanical verification\n\nRun the changelog verifier after finishing the draft.\n\n```bash\ngh pr list --state merged --limit 200 --json number,title,author,mergedAt,baseRefName,url > /tmp/frb-merged-prs.json\nuv run --script .claude/skills/frb-write-changelog/verify_changelog.py \\\n  --version <VERSION> \\\n  --previous-release-time <PREVIOUS_RELEASE_TIMESTAMP> \\\n  --release-time <TARGET_RELEASE_TIMESTAMP> \\\n  --merged-prs-json /tmp/frb-merged-prs.json\n```\n\nThe verifier checks that:\n\n- PR numbers in the target section are complete, not duplicated, and not unexpected.\n- Third-party thanks authors are complete and not unexpected, including ordinary documentation PR authors. The same author may be thanked on multiple entries.\n- Entries with third-party thanks appear before entries without thanks within each copied beta group.\n- `docs: add <name> as a contributor ...` all-contributors PRs are ignored.\n\nUse `--ignore-pr <NUMBER>` only for a documented intentional exclusion. Use `--extra-local-pr <NUMBER>` for a stacked local maintainer PR that belongs in the changelog but is not present in the merged PR JSON yet. Use `--extra-thanks-author <LOGIN>` only for a verified co-author or contributor credited by a release-range PR even though their source PR falls outside the release range; record the source PR and reason in the release journal.\n\nApply any confirmed fixes, then re-run the verifier.\n\n## Step 8: Ask the user to review ordering\n\nTell the user the changelog draft is complete and ask for a manual review.\n\n- Ask the user to review the wording.\n- Ask the user to review the ordering of entries.\n- Adjust the ordering if the user wants a different presentation from the mechanically collected order.\n\n## Step 9: Re-verify after human edits\n\nRun the mechanical verifier again after the user finishes manual edits.\n\n- Confirm there are no missing, duplicated, or extra PR numbers.\n- Confirm there are no missing or unexpected third-party thanks authors.\n- Confirm entries with third-party thanks appear before entries without thanks within each copied beta group.\n- Apply any confirmed fixes, then do one final diff check.\n\nIf the user explicitly wants an independent review, ask a separate reviewer or subagent to compare the final `CHANGELOG.md` against the same merged PR list.\n\n## Step 10: Regenerate published-package changelogs\n\nRegenerate the published-package changelog copies from the final root `CHANGELOG.md`:\n\n```bash\n./frb_internal generate-internal-readme\n```\n\n- Confirm `frb_dart/CHANGELOG.md` and `frb_hooks/CHANGELOG.md` match the root `CHANGELOG.md`.\n- Commit the root changelog and both generated copies together as one atomic release-preparation change.\n- Do not manually edit either generated package changelog.\n- From the clean post-commit tree, run `./frb_internal generate-internal-readme --set-exit-if-changed` as the final drift gate. Do not run this flag while the changelog draft is still dirty because it checks the entire worktree.\n","AGENTS.md":"CLAUDE.md","CLAUDE.md":"# flutter_rust_bridge Development Rules\n\n## Rules\n\n### Do not manually edit these auto-generated files\n\n| File Pattern | Description |\n|--------------|-------------|\n| `frb_generated.*` | Generated by `frb_codegen` - modify generator logic instead |\n| `*.freezed.dart` | Freezed output |\n| `*.g.dart` | Code generator output |\n\n### PR size and complexity\n\n- When humans describe a PR as large or complex, generated code is excluded.\n- Keep implementations as simple as possible while preserving correctness.\n\n### Feature flags\n\nWhen adding or testing feature flags, read `frb-feature-flag`.\n\n## Skills\n\n- `frb-code-generation` - Which generation commands to run\n- `frb-develop-feature` - New feature development workflow\n- `frb-feature-flag` - Feature flag API surface and enabled/disabled coverage rules\n- `frb-test` - How to run tests\n- `frb-debugging` - Debug code generation issues\n- `frb-lint` - Lint and format checks\n- `frb-manual-test` - Manual regression test reports\n- `frb-prepare-pr` - PR preparation\n- `frb-fix-ci` - CI fixes\n- `frb-fix-main-ci` - Default-branch CI regression triage\n- `frb-docker` - Ordinary Docker/devcontainer usage and local validation\n- `frb-upgrade-docker` - Dev Docker toolchain upgrades, candidate images, and stable publication\n","tools/frb_internal/assets/apple_scaffold/frb_example/flutter_package/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md":"# Launch Screen Assets\n\nYou can customize the launch screen with your own desired assets by replacing the image files in this directory.\n\nYou can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images.","tools/frb_internal/assets/apple_scaffold/frb_example/flutter_package_native_assets/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md":"# Launch Screen Assets\n\nYou can customize the launch screen with your own desired assets by replacing the image files in this directory.\n\nYou can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images.","tools/frb_internal/assets/apple_scaffold/frb_example/flutter_via_create/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md":"# Launch Screen Assets\n\nYou can customize the launch screen with your own desired assets by replacing the image files in this directory.\n\nYou can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images.","tools/frb_internal/assets/apple_scaffold/frb_example/flutter_via_create_native_assets/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md":"# Launch Screen Assets\n\nYou can customize the launch screen with your own desired assets by replacing the image files in this directory.\n\nYou can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images.","tools/frb_internal/assets/apple_scaffold/frb_example/flutter_via_integrate/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md":"# Launch Screen Assets\n\nYou can customize the launch screen with your own desired assets by replacing the image files in this directory.\n\nYou can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images.","tools/frb_internal/assets/apple_scaffold/frb_example/flutter_via_integrate_native_assets/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md":"# Launch Screen Assets\n\nYou can customize the launch screen with your own desired assets by replacing the image files in this directory.\n\nYou can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images.","tools/frb_internal/test/README.md":"This is only used for internal development, thus have no tests on its own.","tools/tart_macos/README.md":"# FRB Tart Base VM Preparation\n\nUse this skill when preparing the reusable Tart base VM used by `frb-dev-env tart ...`.\n\n## Model\n\nThe reusable base VM is a clean local VM built by Packer from a pinned Tart OCI artifact:\n\n```text\nghcr.io/cirruslabs/macos-sonoma-xcode:16.1\n  -> optional local OCI registry mirror\n  -> packer build ... frb-tart-base-candidate\n  -> smoke test candidate\n  -> promote candidate to frb-tart-base\n  -> tart clone frb-tart-base frb-tart-<worktree-hash>\n```\n\n`frb-tart-base` is the prepared base image. Do not use it for development, do not run tests in it, and do not manually install tools into it. Treat it as immutable after creation. Per-worktree VMs are cloned from it via APFS copy-on-write and can be dirtied, stopped, deleted, and recreated.\n\n## Why Not Mutate the Base VM\n\nDo not boot `frb-tart-base` and manually install Flutter, Android SDK packages, CocoaPods updates, Rust targets, or other dependencies into it.\n\nManual changes make the base hard to reproduce and hard to audit. If the base needs different tools, update the Packer template or provision script next to this skill, document the exact source, rebuild a candidate, smoke test it, and then promote it to `frb-tart-base`. The base VM should answer both questions: \"which OCI artifact did this come from?\" and \"which provision script changed it?\"\n\n## Source Image\n\nUse a pinned macOS/Xcode OCI reference. The current known-good source is:\n\n```text\nghcr.io/cirruslabs/macos-sonoma-xcode:16.1\n```\n\nAvoid `latest` tags. Avoid broad multi-Xcode runner images unless there is a specific reason; they are larger and less reproducible.\n\n## Fast Path When Network Is Good\n\nIf network access to GHCR is reliable enough, Packer can clone the raw source image directly:\n\n```bash\ncd tools/tart_macos/packer\npacker init .\npacker build \\\n  -var 'source_vm=ghcr.io/cirruslabs/macos-sonoma-xcode:16.1' \\\n  -var 'target_vm=frb-tart-base-candidate' \\\n  -var 'flutter_version=3.47.0' \\\n  -var 'host_proxy_url=http://192.168.64.1:7897' \\\n  -var 'allow_insecure=false' \\\n  .\n```\n\nUse this only when the direct download path is acceptable. Packer delegates the source VM clone to Tart, so shell proxy environment variables may not control it the way they control CLI tools like `curl` or `crane`.\n\nLeave `host_proxy_url` empty when the Tart guest has reliable direct network access. When using a host proxy, enable LAN access so the mixed proxy port listens on `*:7897`, then use `http://192.168.64.1:7897` from Tart guests. Verify it from a running Tart VM before a long base build:\n\n```bash\ntart exec <vm-name> /bin/zsh -lc 'curl -I --proxy http://192.168.64.1:7897 --max-time 10 https://pub.dev'\n```\n\nThere are two separate network paths during a base rebuild:\n\n- Source VM clone: Tart/Packer runs on the host and fetches `source_vm`. Use the local registry path below when `tart clone ghcr.io/...` is unreliable.\n- Guest provisioning: `scripts/provision-frb-tart-base.sh` runs inside the candidate VM. `host_proxy_url` only applies to this guest provisioning phase, including Flutter, Homebrew, Rust, Cargo, and Dart downloads.\n\n## Controlled Local Registry Path\n\nUse this path when direct `tart clone ghcr.io/...` is slow, unreliable, or hard to route through the desired proxy. The idea is:\n\n1. Run a local OCI registry on `127.0.0.1:5000`.\n2. Use `crane copy` with explicit proxy environment variables to mirror the remote Tart OCI artifact into the local registry.\n3. Run Packer with `allow_insecure=true` and `source_vm=127.0.0.1:5000/...` to create `frb-tart-base-candidate`.\n4. Smoke test the candidate.\n5. Promote it to `frb-tart-base`.\n\n### Start Local Registry\n\nUse a large disk for registry storage:\n\n```bash\nmkdir -p /Volumes/MyExternal/temp/frb-mobile-registry\n\ndocker run -d --name frb-mobile-registry \\\n  -p 127.0.0.1:5000:5000 \\\n  -v /Volumes/MyExternal/temp/frb-mobile-registry:/var/lib/registry \\\n  registry:2\n```\n\nVerify it:\n\n```bash\nNO_PROXY=127.0.0.1,localhost,::1 no_proxy=127.0.0.1,localhost,::1 \\\n  curl -fsS http://127.0.0.1:5000/v2/\n```\n\nUse `127.0.0.1`, not `localhost`, to avoid IPv6 or host service surprises.\n\n### Mirror OCI Artifact\n\nMirror with `crane`. If a proxy is needed, set it explicitly for this command:\n\n```bash\nPROXY_URL=http://localhost:7897\n\nHTTP_PROXY=$PROXY_URL HTTPS_PROXY=$PROXY_URL \\\nhttp_proxy=$PROXY_URL https_proxy=$PROXY_URL \\\nALL_PROXY=$PROXY_URL all_proxy=$PROXY_URL \\\nNO_PROXY=localhost,127.0.0.1,::1 no_proxy=localhost,127.0.0.1,::1 \\\n  crane --insecure copy --jobs 4 \\\n  ghcr.io/cirruslabs/macos-sonoma-xcode:16.1 \\\n  127.0.0.1:5000/cirruslabs/macos-sonoma-xcode:16.1\n```\n\nFor long mirrors, run it in `screen` and write logs to disk:\n\n```bash\nPROXY_URL=http://localhost:7897\n\nscreen -dmS frb-mobile-crane-copy zsh -lc \"HTTP_PROXY=$PROXY_URL HTTPS_PROXY=$PROXY_URL http_proxy=$PROXY_URL https_proxy=$PROXY_URL ALL_PROXY=$PROXY_URL all_proxy=$PROXY_URL NO_PROXY=localhost,127.0.0.1,::1 no_proxy=localhost,127.0.0.1,::1 crane --insecure copy --jobs 4 ghcr.io/cirruslabs/macos-sonoma-xcode:16.1 127.0.0.1:5000/cirruslabs/macos-sonoma-xcode:16.1 >> /Volumes/MyExternal/temp/frb-mobile-crane-copy.log 2>&1\"\n```\n\nCheck that `crane` only connects to the intended local proxy, if any, and local registry:\n\n```bash\nPROXY_PORT=7897\n\nlsof -nP -p <crane-pid> | rg \"TCP|127\\\\.0\\\\.0\\\\.1|:${PROXY_PORT}|:5000|ghcr|github\"\n```\n\nExpected network shape:\n\n- `127.0.0.1:<ephemeral> -> 127.0.0.1:<selected-proxy-port>` for remote GHCR fetches through the selected proxy.\n- `127.0.0.1:<ephemeral> -> 127.0.0.1:5000` for local registry writes.\n- No other proxy ports unless they were intentionally selected.\n\n### Verify Local Registry Digest\n\nUse an empty Docker config if the host credential helper interferes:\n\n```bash\nmkdir -p /private/tmp/frb-empty-docker-config\n\nDOCKER_CONFIG=/private/tmp/frb-empty-docker-config \\\nNO_PROXY=localhost,127.0.0.1,::1 no_proxy=localhost,127.0.0.1,::1 \\\n  crane --insecure digest 127.0.0.1:5000/cirruslabs/macos-sonoma-xcode:16.1\n```\n\nKnown digest for the current source image:\n\n```text\nsha256:f181b76eede9acd7a6db76438a4cf73e76550c3f9e8104aed16347122e132184\n```\n\n## Build Base VM With Packer\n\nBuild a candidate base VM from the local registry:\n\n```bash\ncd tools/tart_macos/packer\npacker init .\npacker build \\\n  -var 'source_vm=127.0.0.1:5000/cirruslabs/macos-sonoma-xcode:16.1' \\\n  -var 'target_vm=frb-tart-base-candidate' \\\n  -var 'flutter_version=3.47.0' \\\n  -var 'host_proxy_url=http://192.168.64.1:7897' \\\n  -var 'allow_insecure=true' \\\n  .\n```\n\nThe Packer template clones the raw source VM, boots it as `admin/admin`, runs `scripts/provision-frb-tart-base.sh`, and leaves a stopped local VM named by `target_vm`. The current provision step initializes Xcode, checks out the requested Flutter SDK version, installs CocoaPods with Homebrew, installs Rust matching the devcontainer, adds the `aarch64-apple-ios-sim` and `x86_64-apple-ios` Rust targets for both the pinned toolchain and the `stable-aarch64-apple-darwin` toolchain used by CargoKit, and pre-caches Flutter iOS and macOS artifacts. If `host_proxy_url` is set, the provision step receives standard `HTTP_PROXY` / `HTTPS_PROXY` / `ALL_PROXY` / `CARGO_HTTP_PROXY` environment variables. It also verifies the image contains Xcode and simulator tooling.\n\nKeep `flutter_version` in sync with `FRB_MAIN_FLUTTER_VERSION` in `.github/workflows/ci.yaml` and the Flutter version in `.devcontainer/Dockerfile`. Do not rely on the source Tart OCI image's preinstalled Flutter version, because Flutter template drift can change generated scaffold files.\n\nVerify the candidate:\n\n```bash\ntart list\ntart get frb-tart-base-candidate --format json\n```\n\nDo not boot and mutate `frb-tart-base` during verification. If you need to inspect runtime state, clone a disposable probe VM from the candidate:\n\n```bash\ntart clone frb-tart-base-candidate frb-tart-probe\ntart run --no-graphics frb-tart-probe\ntart ip frb-tart-probe --wait 180\ntart exec frb-tart-probe /bin/zsh -lc 'sw_vers && xcodebuild -version && flutter --version && flutter --version --machine | python3 -c '\"'\"'import json,sys; assert json.load(sys.stdin)[\"frameworkVersion\"] == \"3.47.0\"'\"'\"' && pod --version && rustc --version && cargo --version && rustup target list --installed | grep aarch64-apple-ios-sim && rustup target list --installed | grep x86_64-apple-ios && rustup target list --toolchain stable-aarch64-apple-darwin --installed | grep aarch64-apple-ios-sim && rustup target list --toolchain stable-aarch64-apple-darwin --installed | grep x86_64-apple-ios'\ntart stop frb-tart-probe\ntart delete frb-tart-probe\n```\n\nAfter the candidate passes smoke, promote it by moving the old base out of the way and renaming the candidate:\n\n```bash\nstamp=$(date +%Y%m%d-%H%M%S)\ntart rename frb-tart-base \"frb-tart-base-before-packer-$stamp\"\ntart rename frb-tart-base-candidate frb-tart-base\n```\n\nIf there is no existing `frb-tart-base`, only the second rename is needed.\n\n## Cost Notes\n\nLocal VM-to-VM cloning is cheap because Tart uses APFS copy-on-write for local clones. `du` may count shared blocks and look large, so prefer `df` available space when measuring real disk pressure.\n\nEven though local clone is cheap, keep a conservative free-space threshold before creating worktree VMs. `frb-dev-env tart create` requires at least 150GB free by default because base image setup, caches, build products, and simulator state can grow quickly.\n","tools/vmware_windows/README.md":"# VMware Windows local development environment\n\nThis directory contains the VMware Fusion + Vagrant + Packer workflow for local Windows 11 Arm\nvalidation of `flutter_rust_bridge`.\n\nUse this path only for Windows desktop validation. Keep using Docker for Linux/headless checks, Tart\nfor macOS/iOS checks, and the host Android Emulator workflow for Android runtime checks.\n\nConfigure a local storage root before creating or building VMs. The helper reads:\n\n1. CLI flags, where available.\n2. Environment variables such as `FRB_WINDOWS_VM_ROOT`.\n3. The user config file, defaulting to `~/.config/flutter_rust_bridge/vmware_windows.json`.\n\nExample config:\n\n```json\n{\n  \"vm_root\": \"/path/to/frb-windows-vmware\",\n  \"host_proxy_url\": \"http://localhost:PORT\",\n  \"iso_path\": \"/path/to/frb-windows-vmware/downloads/iso/Windows11_Arm64.iso\",\n  \"iso_sha256\": \"sha256:<digest>\",\n  \"vm_gui\": true\n}\n```\n\nDo not store long-lived Windows VM bundles or boxes on the internal disk. When the storage root is\nunder `/Volumes/<name>/`, the helper refuses\nto create directories unless that volume is actually mounted, preventing accidental writes to the\ninternal disk if an external drive is absent.\n\nIf host-side Windows VM downloads and Packer network operations need a proxy, set:\n\n```bash\nexport FRB_WINDOWS_HOST_PROXY_URL=http://localhost:PORT\n```\n\nLeave `host_proxy_url` unset when direct network access is preferred.\n\n## Model\n\nThe reusable base is built by Packer from a Windows 11 Arm ISO and then consumed by Vagrant on top\nof VMware Fusion:\n\n```text\nWindows 11 Arm ISO\n  -> packer build ... frb-windows11-arm64.box\n  -> vagrant box add frb/windows11-arm64 <box>\n  -> vagrant up per-worktree VM under the configured VM root\n```\n\nThe base box is immutable after promotion. Do not manually install Flutter, Rust, Visual Studio\nBuild Tools, or other dependencies into the promoted base and then forget to update Packer. Manual\nexperiments are allowed only as discovery; every durable base change must be reflected in the Packer\ntemplate or provision scripts.\n\nUse `tools/manual_tests/frb-local-vmware-windows-dev-env.md` for audit-ready local validation and\nexecution records.\n\n## Quick Start\n\n```bash\ntools/vmware_windows/frb_vmware_windows_env.py init-root\ntools/vmware_windows/frb_vmware_windows_env.py check-host\ntools/vmware_windows/frb_vmware_windows_env.py self-test\ntools/vmware_windows/frb_vmware_windows_env.py packer-init\ntools/vmware_windows/frb_vmware_windows_env.py prepare-iso \\\n  --iso /path/to/frb-windows-vmware/downloads/iso/Windows11_Arm64.iso\ntools/vmware_windows/frb_vmware_windows_env.py packer-build\ntools/vmware_windows/frb_vmware_windows_env.py vagrant-box-add \\\n  --box-file /path/to/frb-windows-vmware/packer/boxes/frb-windows11-arm64.box\ntools/vmware_windows/frb_vmware_windows_env.py start\ntools/vmware_windows/frb_vmware_windows_env.py upload\ntools/vmware_windows/frb_vmware_windows_env.py test-flutter-windows --package frb_example/flutter_via_create\n```\n\nFor the initial base-image build on a developer Mac, prefer the Terminal wrapper:\n\n```bash\ntools/vmware_windows/run_packer_build.zsh \\\n  --iso /path/to/frb-windows-vmware/downloads/iso/Windows11_Arm64.iso \\\n  --add-box\n```\n\nThe wrapper uses the same helper, applies the configured host proxy when present, writes a full log\nunder the configured VM root, shows the VMware console by default, and stops the active build if `/`\nor the VM storage root drops below 100GB free.\nWhen a host proxy is configured, it also adds the Packer WinRM guest host to `NO_PROXY` so local VM\ncontrol traffic does not get sent through the host download proxy.\n\nBefore starting the long VMware build, the wrapper checks that the ISO exists, `hdiutil`, `xorriso`,\nPacker, Vagrant, and VMware `vmrun` are available, and the Packer output directory is not already\noccupied. Use `--preflight-only` to run only those wrapper checks without remastering an ISO,\nstarting Packer, or booting VMware.\n\n## Host Installation\n\n`tools/vmware_windows/frb_vmware_windows_env.py check-host` verifies VMware `vmrun`, Vagrant, and\nPacker. The helper itself uses only the Python standard library, so it does not need `uv`, `pip`, or\na first-run Python package download. Packer can be installed with Homebrew. VMware Fusion Pro may\nrequire an authenticated Broadcom/VMware download, and Vagrant's macOS pkg may require an\ninteractive sudo password. Install those tools through their normal macOS flows when non-interactive\nHomebrew installation is blocked.\n\nIf your network requires a proxy for host-side downloads, configure it explicitly before preparing\nthis VM workflow:\n\n```bash\nexport HTTP_PROXY=http://localhost:PORT\nexport HTTPS_PROXY=http://localhost:PORT\nexport http_proxy=http://localhost:PORT\nexport https_proxy=http://localhost:PORT\n```\n\nIf using Homebrew, keep auto-update disabled:\n\n```bash\nHOMEBREW_NO_AUTO_UPDATE=1 brew install --cask vmware-fusion\nHOMEBREW_NO_AUTO_UPDATE=1 brew install --cask vagrant\nHOMEBREW_NO_AUTO_UPDATE=1 brew install --cask vagrant-vmware-utility\nHOMEBREW_NO_AUTO_UPDATE=1 brew install packer\nHOMEBREW_NO_AUTO_UPDATE=1 brew install xorriso\nvagrant plugin install vagrant-vmware-desktop\n```\n\nIn practice, these host installers can require human action:\n\n- `vmware-fusion` may be disabled in Homebrew because Broadcom/VMware requires an authenticated\n  download. Download VMware Fusion Pro manually from the VMware/Broadcom portal, install `VMware\n  Fusion.app`, then verify `/Applications/VMware Fusion.app/Contents/Library/vmrun` exists.\n- `vagrant` is a macOS `.pkg` cask. A non-interactive agent shell can download it, but\n  installation may fail at `/usr/sbin/installer` because `sudo` needs an administrator password.\n  Install it manually with the macOS installer, or run the Homebrew cask command yourself from an\n  interactive terminal.\n- `vagrant-vmware-utility` is also a macOS `.pkg` cask and can require the same interactive\n  administrator installation path.\n- `packer` can be installed non-interactively with Homebrew and should appear as\n  `/opt/homebrew/bin/packer`.\n- `xorriso` is required to remaster the Windows ISO so `Autounattend.xml` is available from the\n  root of the boot media. Install it with `HOMEBREW_NO_AUTO_UPDATE=1 brew install xorriso`.\n- Vagrant's VMware provider requires both the Vagrant VMware Utility and the\n  `vagrant-vmware-desktop` plugin. Install the utility with\n  `brew install --cask vagrant-vmware-utility`, then install the plugin with Vagrant.\n- VMware Fusion 26 provides the Arm64 Windows drivers used during Packer setup at\n  `/Applications/VMware Fusion.app/Contents/Library/isoimages/arm64/drivers-arm64.zip`. The helper\n  injects those drivers into the remastered ISO and installs `vmxnet3.inf` during Windows setup so\n  the guest can obtain a NAT address before Packer waits for WinRM. Set\n  `FRB_VMWARE_FUSION_ARM64_DRIVERS_ZIP` only if Fusion stores the archive somewhere else.\n\nAfter installing VMware Fusion and Vagrant, install the VMware Vagrant provider utility and plugin:\n\n```bash\nHOMEBREW_NO_AUTO_UPDATE=1 brew install --cask vagrant-vmware-utility\nvagrant plugin install vagrant-vmware-desktop\n```\n\nThen re-run:\n\n```bash\ntools/vmware_windows/frb_vmware_windows_env.py check-host\n```\n\nExpected result:\n\n```json\n{\n  \"missing\": [],\n  \"tools\": {\n    \"packer\": \"/opt/homebrew/bin/packer\",\n    \"vagrant\": \"/opt/vagrant/bin/vagrant\",\n    \"vmrun\": \"/Applications/VMware Fusion.app/Contents/Library/vmrun\"\n  }\n}\n```\n\nValidate the host without starting a VM:\n\n```bash\ntools/vmware_windows/frb_vmware_windows_env.py check-host\ntools/vmware_windows/frb_vmware_windows_env.py self-test\ntools/vmware_windows/validate_offline.zsh\n```\n\n`self-test` is a dependency-free helper check. It does not start VMware, Vagrant, Packer, or touch\nthe external VM root; it only validates local helper logic such as path handling, checksum sidecars,\nand guest command argument normalization.\n\n`validate_offline.zsh` is the local no-VM regression check for this directory. It checks Python\nsyntax and helper self-tests, the Packer build wrapper syntax, Vagrantfile syntax when Ruby is\navailable, Packer formatting when Packer is available, website sidebar JavaScript syntax when Node\nis available, and whitespace errors.\n\n## Windows ISO\n\nDownload the Windows 11 Arm ISO from Microsoft's official Arm64 download page:\n\n```text\nhttps://www.microsoft.com/software-download/windows11arm64\n```\n\nThe page generates ISO links dynamically and those links expire. Choose `Windows 11 (multi-edition\nISO for Arm64)`, then choose the language you want to install. For the default English base VM,\nchoose `English`.\n\nPlace the ISO under:\n\n```text\n/path/to/frb-windows-vmware/downloads/iso/Windows11_Arm64.iso\n```\n\nSet `FRB_WINDOWS_ISO` if using another filename. Do not commit ISO files or generated boxes. If\n`Windows11_Arm64.iso` is absent and exactly one non-`autounattend` `.iso` exists in the ISO\ndirectory, the helper and wrapper use that source ISO automatically.\n\nProvide a SHA-256 checksum before a real Packer build. Either set:\n\n```bash\nexport FRB_WINDOWS_ISO_CHECKSUM=sha256:<digest>\n```\n\nor place the digest in:\n\n```text\n/path/to/frb-windows-vmware/downloads/iso/Windows11_Arm64.iso.sha256\n```\n\nFor the current Microsoft Windows 11 Arm64 English ISO, the official verify table lists this\nSHA-256:\n\n```text\n638AA2C88E94385B00F4F178D071E3DF0B7D9E335577A83BD533B7F2EB65ADF0\n```\n\nWrite the checksum file as either the bare digest or `sha256:<digest>`:\n\n```bash\nprintf '%s\\n' '638AA2C88E94385B00F4F178D071E3DF0B7D9E335577A83BD533B7F2EB65ADF0' \\\n  > /path/to/frb-windows-vmware/downloads/iso/Windows11_Arm64.iso.sha256\n```\n\n`packer-build --validate` uses a placeholder checksum only to validate template structure. A real\n`packer-build` fails fast until a checksum is provided.\n\n## Build The Base Box With Packer\n\nInitialize and build from the checked-in template:\n\n```bash\ntools/vmware_windows/frb_vmware_windows_env.py packer-init\ntools/vmware_windows/frb_vmware_windows_env.py prepare-iso\ntools/vmware_windows/frb_vmware_windows_env.py packer-build\n```\n\nThe helper sets `PACKER_CACHE_DIR` under the external root and passes explicit variables for the\nISO, checksum, generated unattended-install file, output directory, and provision scripts. By\ndefault, `packer-build` first remasters the source Windows ISO with `hdiutil` and `xorriso` so\n`Autounattend.xml` is present at the root of a UEFI-bootable install ISO; this matches Windows\nSetup's most reliable unattended-install discovery path under VMware Fusion on Apple Silicon. The\ntemplate also exposes `Autounattend.xml` through both Packer floppy media and a secondary\n`AUTOUNATTEND` CD as a fallback. The template provisions guest command access, OpenSSH, PowerShell\ntooling, Rust, Flutter, CMake/Ninja, and Visual Studio Build Tools.\n\nPacker connects to the freshly installed guest over VMware Fusion NAT WinRM. The helper passes\n`FRB_WINDOWS_PACKER_WINRM_HOST` to the template, defaulting to `172.16.0.128`, which is the first\nlease observed for the generated Packer VM on VMware Fusion NAT. Override this value if another VM\nalready occupies that address or if your Fusion NAT subnet assigns a different first lease:\n\n```bash\nexport FRB_WINDOWS_PACKER_WINRM_HOST=172.16.0.128\n```\n\nWhen a host proxy is configured for downloads, the helper and wrapper add this WinRM host to\n`NO_PROXY` and `no_proxy`. Without that bypass, Packer's WinRM HTTP requests can be routed through\nthe proxy and fail with `http response error: 502 - invalid content type`.\n\nThe remastered ISO is stored next to the source ISO:\n\n```text\n/path/to/frb-windows-vmware/downloads/iso/<source>.frb-autounattend-uefi.iso\n```\n\nUse `prepare-iso --force` only when intentionally regenerating the remastered ISO after changing\n`Autounattend.xml` generation. Use `packer-build --no-remaster-iso` only for low-level\ntroubleshooting of the original Microsoft ISO plus Packer's separate autounattend media.\n\nPacker requires the output directory to be absent before a build starts. The helper removes an empty\nstale output directory automatically, but it stops if that directory contains files so an existing\nbox is not overwritten accidentally.\n\nFor unattended first-run builds from an ordinary macOS Terminal, use:\n\n```bash\ntools/vmware_windows/run_packer_build.zsh \\\n  --iso /path/to/frb-windows-vmware/downloads/iso/Windows11_Arm64.iso \\\n  --add-box\n```\n\nUseful variants:\n\n```bash\ntools/vmware_windows/run_packer_build.zsh --iso /path/to/Windows11_Arm64.iso\ntools/vmware_windows/run_packer_build.zsh --iso /path/to/Windows11_Arm64.iso --preflight-only\ntools/vmware_windows/run_packer_build.zsh --iso /path/to/Windows11_Arm64.iso --prepare-only\ntools/vmware_windows/run_packer_build.zsh --iso /path/to/Windows11_Arm64.iso --headless\ntools/vmware_windows/run_packer_build.zsh --iso /path/to/Windows11_Arm64.iso --no-force-prepare-iso\ntools/vmware_windows/run_packer_build.zsh --iso /path/to/Windows11_Arm64.iso --add-box --force-box\n```\n\nThe wrapper is intentionally thin: it calls `init-root`, `check-host`, `packer-init`, `prepare-iso`,\n`packer-build`, and optionally `vagrant-box-add`. It exists to keep the proxy, log path, external\nstorage root, preflight checks, and disk-space guard consistent during the long VMware build. Use\n`--preflight-only` to validate host readiness without starting the build; use `--prepare-only` to\nvalidate ISO remastering before starting VMware.\n\nIf the first unattended install fails, keep the full Packer log under `logs/`, record the exact\nfailure in the execution artifact, fix the template or scripts, and rerun. Do not fix the base by\nbooting it and manually installing tools without updating the source files.\n\nAfter Packer produces a box, import it with:\n\n```bash\ntools/vmware_windows/frb_vmware_windows_env.py vagrant-box-add \\\n  --box-file /path/to/frb-windows-vmware/packer/boxes/frb-windows11-arm64.box\n```\n\nThe default box name is `frb/windows11-arm64`. Override with `FRB_WINDOWS_VAGRANT_BOX` only when\nintentionally testing another base.\n\nThe base VM installs Flutter 3.44.1, Dart 3.12.1, Rust stable, Git, CMake, Ninja, and Visual Studio\nBuild Tools with the C++ workload. Keep this list synchronized with\n`provision/install-frb-toolchain.ps1` when the repository raises its Windows toolchain floor.\n\n## Daily Use\n\nRun from the repository root:\n\n```bash\ntools/vmware_windows/frb_vmware_windows_env.py info\ntools/vmware_windows/frb_vmware_windows_env.py start\ntools/vmware_windows/frb_vmware_windows_env.py upload\ntools/vmware_windows/frb_vmware_windows_env.py exec -- powershell -NoProfile \\\n  -Command \"cd C:\\frb\\worktree; flutter --version\"\ntools/vmware_windows/frb_vmware_windows_env.py stop\n```\n\nThe Vagrantfile starts VMware Fusion with a visible GUI by default because VMware Fusion 26 on Apple\nSilicon can power off this Windows Arm guest shortly after boot in `nogui` mode. Set\n`FRB_WINDOWS_VM_GUI=false` only when explicitly revalidating headless startup on a newer Fusion or\nprovider combination.\n\n`upload` creates a zip archive of the host checkout, uploads it through Vagrant's WinRM\ncommunicator, and expands it into a VM-local directory before heavy builds. Do not compile from a\nVMware shared folder because shared folders are slower for many small files, can distort build\nbehavior, and are sensitive to Vagrant VMware provider compatibility.\n\nAfter the VM is running and code is uploaded:\n\n```bash\ntools/vmware_windows/frb_vmware_windows_env.py test-flutter-windows \\\n  --package frb_example/flutter_via_create\n```\n\n## Credentials\n\nDo not put guest passwords in the repository. Use one of:\n\n```text\nFRB_WINDOWS_VM_USERNAME\nFRB_WINDOWS_VM_PASSWORD\n~/.secrets/FRB_WINDOWS_VM_PASSWORD\n```\n\nWhen a password is required, source it in the shell or pass it through the environment. Do not write\nit into command logs.\n\nPassword resolution order is `FRB_WINDOWS_VM_PASSWORD`, then `~/.secrets/FRB_WINDOWS_VM_PASSWORD`,\nthen the helper-generated fallback file.\n\nIf `FRB_WINDOWS_VM_PASSWORD` is unset, the helper generates a local per-root fallback password at:\n\n```text\n/path/to/frb-windows-vmware/credentials/guest-password.txt\n```\n\nTreat that file as local machine state. It is not part of the repository, and the helper passes it\nto both Packer and Vagrant through environment variables.\n\n## Cleanup\n\nStop the per-worktree VM when it is not needed:\n\n```bash\ntools/vmware_windows/frb_vmware_windows_env.py stop\n```\n\nDelete disposable per-worktree VMs only when intentionally reclaiming space:\n\n```bash\ntools/vmware_windows/frb_vmware_windows_env.py destroy\n```\n\nKeep the Packer-built base box and external caches unless rebuilding the Windows base.\n\n## Layout\n\n```text\ntools/vmware_windows/\n├── Vagrantfile\n├── frb_vmware_windows_env.py\n├── run_packer_build.zsh\n├── packer/\n│   ├── windows11-arm64.pkr.hcl\n└── provision/\n    ├── expand-uploaded-worktree.ps1\n    ├── install-frb-toolchain.ps1\n    └── prepare-guest-control.ps1\n```\n\nThe checked-in scripts are source of truth. Manual changes inside a Windows base VM should be\ntreated as experiments and then translated back into Packer or provision scripts.\n\n## Troubleshooting\n\n### VMware Cancels VM Startup\n\nIf `packer-build` reaches `Powering on virtual machine...` and then fails with:\n\n```text\nerror starting virtual machine: ... Error: The operation was canceled\n```\n\nopen VMware Fusion once from `/Applications`, finish any first-run prompts, accept the license, and\napprove any macOS privacy, network, or helper permissions it requests. Then verify the command-line\nbridge still works:\n\n```bash\n/Applications/VMware\\ Fusion.app/Contents/Library/vmrun -T fusion list\nlaunchctl print system/com.vagrant.vagrant-vmware-utility\n```\n\nAfter those commands work without permission dialogs, rerun:\n\n```bash\ntools/vmware_windows/frb_vmware_windows_env.py packer-build \\\n  --iso /path/to/frb-windows-vmware/downloads/iso/Windows11_Arm64.iso\n```\n\nIf the VM still cancels immediately in headless mode, run once with `--gui` so VMware Fusion can\nshow any remaining prompt:\n\n```bash\ntools/vmware_windows/frb_vmware_windows_env.py packer-build --gui \\\n  --iso /path/to/frb-windows-vmware/downloads/iso/Windows11_Arm64.iso\n```\n\nIf VMware Fusion reports that the `lsilogic` device type for `scsi0` is not supported, keep the\nPacker template on the checked-in NVMe disk adapter and `pvscsi` VMX override. To preserve the\nfailed VM directory and inspect `vmware.log`, rerun with:\n\n```bash\ntools/vmware_windows/frb_vmware_windows_env.py packer-build --gui --on-error=abort \\\n  --iso /path/to/frb-windows-vmware/downloads/iso/Windows11_Arm64.iso\n```\n\nIf the VM reaches UEFI Boot Manager instead of Windows Setup, the Packer boot command must select\nthe SATA CDROM entry and send boot keys during the Windows ISO's short prompt window before WinRM\ncan ever become available. Keep the checked-in boot command unless a future VMware Fusion release\nchanges the Boot Manager entry order.\n"},"files":{".claude/skills/frb-add-contributor/SKILL.md":"---\nname: frb-add-contributor\ndescription: Add or reconcile flutter_rust_bridge contributors through all-contributors PRs. Use when finding contributor PRs, posting all-contributors comments, validating contributor metadata, resolving contributor PR merge conflicts, or merging the generated contributor PRs.\n---\n\n# FRB Add Contributor\n\nUse this skill when contributors need to be added to `flutter_rust_bridge` via `all-contributors`, especially when multiple recent PR authors should be credited in batch.\n\n## Step 1: Find the correct source PRs\n\nFor each GitHub username, find their PRs in `fzyzcjy/flutter_rust_bridge`.\n\n```bash\ngh search prs --repo fzyzcjy/flutter_rust_bridge --author <username> --limit 100 --json number,title,author,url,state\n```\n\nImportant:\n\n- Deduplicate by contributor. A single person may have multiple merged PRs.\n- If the task is about `new contributor PR`s, use the contributor's first PR only.\n- For release reconciliation, inspect every third-party human-authored PR in the same release range used by `frb-write-changelog`.\n- For each such PR, confirm that the contributor's human-written `.all-contributors-custom.yaml` summary covers that contribution. Being present in `.all-contributorsrc` or `README.md` is not sufficient.\n- Treat an existing contributor whose summary does not cover a release-range PR as needing reconciliation, but do not expect a new all-contributors PR for that person.\n- If the contributor already exists in `.all-contributorsrc` or `README.md`, do not expect a new all-contributors PR to be created.\n\n## Step 2: Stop for human confirmation\n\nAfter determining which contributors may need to be added or have their custom descriptions extended, stop and ask a human to confirm the contributor list and source PRs before taking any mutating action.\n\nThis stop is mandatory.\n\n- Do not edit `.all-contributors-custom.yaml`.\n- Do not regenerate contributor artifacts.\n- Do not post all-contributors comments.\n- Do not create or merge any PRs.\n- Do not perform any other GitHub mutation.\n\nOnly continue after the human explicitly confirms which contributors to add or update and which source PRs those changes cover.\n\n## Step 3: Prepare `.all-contributors-custom.yaml` and stop for human input\n\nBefore regenerating contributor artifacts, append each new contributor to the end of `.all-contributors-custom.yaml`:\n\n```yaml\n- <username>: TODO\n```\n\nDo this only for contributors who are not already present in `.all-contributors-custom.yaml`.\n\nFor an existing credited contributor with no custom entry, append:\n\n```yaml\n- <username>: TODO(#<pr-number>)\n```\n\nFor an existing contributor whose summary does not cover a release-range PR, preserve the existing human-written text and append a marker for each uncovered PR:\n\n```yaml\n- <username>: <existing human-written summary> TODO(#<pr-number>)\n```\n\nThen stop and ask a human to replace each `TODO` or `TODO(#<pr-number>)` with a concise contribution summary.\n\nThis stop is mandatory.\n\n- Do not continue to Step 3 or any later step in this skill until the human has replaced every `TODO`.\n- Do not run contributor generation.\n- Do not post all-contributors comments.\n- Do not resolve or merge contributor PRs.\n\nOnly resume after the human-written descriptions are present in `.all-contributors-custom.yaml`.\n\n## Step 4: Trigger all-contributors\n\nPost the comment on the chosen source PR:\n\n```bash\ngh pr comment <pr-number> --repo fzyzcjy/flutter_rust_bridge --body '@all-contributors please add <username> for code'\n```\n\nThen inspect the bot response on that PR.\n\n- Skip this step for an existing contributor whose only gap was an incomplete custom description.\n- If the bot says the user already contributed before, stop there for that user.\n- Otherwise, wait for the bot to open a PR named `docs: add <username> as a contributor for code`.\n\n## Step 5: Validate contributor metadata\n\nWhen a contributor PR exists, check the generated contributor data before merging.\n\nFiles to inspect:\n\n- `.all-contributorsrc`\n- `README.md`\n\nRequired validation:\n\n- Homepage URLs must use `https://`, never `http://`.\n- If the generated homepage is `http://`, change it to `https://` if the site supports it.\n- If the personal site does not support `https://`, prefer the contributor's GitHub profile URL instead of keeping `http://`.\n\n## Step 6: Regenerate contributor output\n\nAfter the human has replaced all `TODO` markers, confirm that the custom summaries cover every third-party release-range PR. Then regenerate the contributor artifacts instead of hand-editing `README.md`.\n\nUse:\n\n```bash\n./frb_internal generate-internal-contributor\n```\n\nThis command updates:\n\n- `.all-contributorsrc`\n- `README.md` via `all-contributors generate`\n- `website/docs/index.md` via `generate-internal-readme`\n\nIf you only need to refresh the website copy of the README, use:\n\n```bash\n./frb_internal generate-internal-readme\n```\n\n## Step 7: Resolve merge conflicts in contributor PRs\n\nThese PRs usually conflict only in:\n\n- `.all-contributorsrc`\n- `README.md`\n\nRecommended workflow for each open contributor PR:\n\n```bash\ngh pr checkout <pr-number> --repo fzyzcjy/flutter_rust_bridge\ngit fetch origin master\ngit merge --no-ff origin/master\n```\n\nIf the merge conflicts:\n\n1. Keep the latest `origin/master` content.\n2. Re-apply the contributor entry from the bot branch.\n3. Ensure the contributor card remains present in `README.md`.\n4. Re-run `./frb_internal generate-internal-contributor` if you edited contributor metadata or finished filling in `.all-contributors-custom.yaml`.\n5. `git add` the resolved files and commit the merge.\n6. Push the contributor branch back to origin.\n\nDo not revert unrelated contributor entries that were merged by other PRs.\n\n## Step 8: Merge the contributor PRs\n\nAfter the branch is updated and GitHub reports it mergeable:\n\n```bash\ngh pr merge <pr-number> --repo fzyzcjy/flutter_rust_bridge --merge --delete-branch\n```\n\nNotes:\n\n- GitHub may temporarily report `DIRTY` or reject merge immediately after push.\n- If that happens, wait briefly, re-check the PR state, and retry the merge.\n- After merging one contributor PR, fetch `origin/master` again before resolving the next one.\n\n## Step 9: Final verification\n\nAt the end, verify there are no remaining open all-contributors PRs:\n\n```bash\ngh pr list --repo fzyzcjy/flutter_rust_bridge --search \"author:allcontributors[bot] is:open\" --limit 50 --json number,title,mergeStateStatus,url\n```\n\nAlso verify the credited contributors appear in:\n\n- `.all-contributorsrc`\n- `README.md`\n- `website/docs/index.md` if contributor regeneration was run\n",".claude/skills/frb-android-emulator-prepare/SKILL.md":"---\nname: frb-android-emulator-prepare\ndescription: Use when preparing, installing, diagnosing, or explaining the host Android Emulator environment for flutter_rust_bridge local runtime validation, including Android SDK command-line tools, emulator packages, AVD creation, host emulator startup, and Docker-local ADB connectivity to the emulator TCP endpoint.\n---\n\n# FRB Android Emulator Preparation\n\nUse this skill when a task requires a **host Android Emulator** for FRB local runtime validation.\n\n## Boundary\n\nFRB Android runtime validation currently supports a host Android Emulator path: host needs Java, Android SDK command-line tools, `emulator`, a system image, and an AVD; Docker runs FRB/Flutter commands through a Docker-local ADB server connected to the emulator TCP endpoint.\n\nDo not install Android Emulator packages unless the user has explicitly asked to prepare or run an emulator. Installing the emulator changes host state and downloads large SDK packages.\n\n## Default Host Locations\n\nUse standard Android Studio locations on macOS:\n\n```text\n~/Library/Android/sdk\n~/.android/avd\n~/.android\n```\n\nRespect `ANDROID_HOME` if it is already set. Otherwise use `~/Library/Android/sdk`. Do not invent repo-specific defaults such as `/workspace`, `/tmp`, or a user's personal artifacts directory.\n\n## Preflight\n\nOfficial references for this workflow:\n\n- Android Studio and command-line tools download page: https://developer.android.com/studio\n- `sdkmanager` command-line package manager: https://developer.android.com/tools/sdkmanager\n- `avdmanager` command-line AVD manager: https://developer.android.com/tools/avdmanager\n- Android Emulator command line: https://developer.android.com/studio/run/emulator-commandline\n- ADB command-line tool: https://developer.android.com/tools/adb\n\nRun these before installing anything:\n\n```bash\nsw_vers\nuname -m\nwhich adb || true\nadb version || true\necho \"${ANDROID_HOME:-}\"\nls -ld \"$HOME/Library/Android/sdk\" \"$HOME/.android\" \"$HOME/.android/avd\" 2>/dev/null || true\nwhich java || true\njava -version || true\n```\n\nIf the task requires an emulator, record whether these tools already exist:\n\n```bash\nls \"$HOME/Library/Android/sdk/cmdline-tools/latest/bin/sdkmanager\" 2>/dev/null || true\nls \"$HOME/Library/Android/sdk/cmdline-tools/latest/bin/avdmanager\" 2>/dev/null || true\nls \"$HOME/Library/Android/sdk/emulator/emulator\" 2>/dev/null || true\n```\n\n## Installation Policy\n\nBefore installing or downloading anything, state exactly what will be installed and ask for approval through the command escalation mechanism.\n\nFor a minimal macOS emulator setup, expect these host changes:\n\n```text\nHomebrew openjdk, if Java is missing\n~/Library/Android/sdk/cmdline-tools/latest\n~/Library/Android/sdk/emulator\n~/Library/Android/sdk/platform-tools\n~/Library/Android/sdk/platforms/android-<api>\n~/Library/Android/sdk/system-images/android-<api>/google_apis/arm64-v8a\n~/.android/avd/<avd-name>.avd\n~/.android/avd/<avd-name>.ini\n```\n\nPrefer ARM64 system images on Apple Silicon:\n\n```text\nsystem-images;android-35;google_apis;arm64-v8a\n```\n\nUse a different API level only when the task or Flutter version requires it.\n\n## Install Java\n\nIf `/usr/bin/java` is missing and Homebrew is available, prefer the Homebrew `openjdk` formula because it does not require the sudo package installer:\n\n```bash\nHOMEBREW_NO_AUTO_UPDATE=1 brew install openjdk\n```\n\nFor SDK commands in the current shell, prepend the Homebrew JDK:\n\n```bash\nexport PATH=\"/opt/homebrew/opt/openjdk/bin:$PATH\"\n```\n\nDo not use `brew install --cask temurin` in non-interactive agent runs unless the user has approved sudo-capable installation; it may invoke macOS Installer through `sudo`.\n\n## Install Command-Line Tools\n\nIf Android Studio is not installed and `cmdline-tools/latest` is missing, install the official Android command-line tools into the standard SDK root. This follows the Android Developers command-line tools instructions on the Android Studio download page: https://developer.android.com/studio. The important shape is to download the command-line tools package from that page, extract it, and place it under `cmdline-tools/latest` in the Android SDK root.\n\n```bash\nmkdir -p \"$HOME/Library/Android/sdk/cmdline-tools/latest\"\ncurl -L -o /private/tmp/commandlinetools-mac-latest.zip \\\n  https://dl.google.com/android/repository/commandlinetools-mac-14742923_latest.zip\nmkdir -p /private/tmp/frb-android-commandlinetools-extract\nditto -x -k /private/tmp/commandlinetools-mac-latest.zip /private/tmp/frb-android-commandlinetools-extract\ncp -R /private/tmp/frb-android-commandlinetools-extract/cmdline-tools/. \\\n  \"$HOME/Library/Android/sdk/cmdline-tools/latest/\"\n```\n\nThen set:\n\n```bash\nexport ANDROID_HOME=\"$HOME/Library/Android/sdk\"\nexport ANDROID_SDK_ROOT=\"$ANDROID_HOME\"\nexport PATH=\"/opt/homebrew/opt/openjdk/bin:$ANDROID_HOME/cmdline-tools/latest/bin:$ANDROID_HOME/platform-tools:$ANDROID_HOME/emulator:$PATH\"\n```\n\n## Install Emulator Packages\n\nAccept licenses and install the minimal packages with `sdkmanager`, the Android SDK package manager documented by Android Developers at https://developer.android.com/tools/sdkmanager:\n\n```bash\nyes | sdkmanager --licenses\nsdkmanager \\\n  \"emulator\" \\\n  \"platform-tools\" \\\n  \"platforms;android-35\" \\\n  \"system-images;android-35;google_apis;arm64-v8a\"\n```\n\nIf the user wants a smaller or non-Google image, choose a matching `system-images;android-<api>;default;arm64-v8a` package instead.\n\n## Create AVD\n\nUse a predictable AVD name that the `frb-dev-env` examples can reference. This uses `avdmanager`, the Android Developers command-line tool for creating and managing AVDs documented at https://developer.android.com/tools/avdmanager:\n\n```bash\navdmanager create avd \\\n  --name FRB_API_35 \\\n  --package \"system-images;android-35;google_apis;arm64-v8a\" \\\n  --device \"pixel_8\"\n```\n\nIf the device profile is unavailable, list device profiles and choose a common phone:\n\n```bash\navdmanager list device\n```\n\n## Start And Verify Emulator\n\nStart the host emulator through `frb-dev-env`. The underlying emulator invocation follows the Android Emulator command-line interface documented at https://developer.android.com/studio/run/emulator-commandline:\n\n```bash\n.claude/skills/frb-dev-env/frb_dev_env.py android emulator --avd FRB_API_35 --port 5554\n```\n\nVerify host ADB can see the emulator. This uses ADB, the Android Developers command-line tool documented at https://developer.android.com/tools/adb:\n\n```bash\nadb devices -l\n```\n\nFor emulator runtime tests, let Docker use its own ADB server and connect directly to the host emulator ADB TCP endpoint. For emulator console port `5554`, that endpoint is `host.docker.internal:5555`:\n\n```bash\n.claude/skills/frb-dev-env/frb_dev_env.py docker exec --android-emulator-adb -- adb devices -l\n```\n\nOnly after both ADB checks pass, run an FRB Android runtime command. Use the Docker-visible emulator serial:\n\n```bash\n.claude/skills/frb-dev-env/frb_dev_env.py docker exec --android-emulator-adb -- \\\n  bash -lc './frb_internal test-flutter-native --package frb_example--flutter_via_create --flutter-test-args \"--device-id host.docker.internal:5555\"'\n```\n\n## Common Failures\n\n- `Android SDK root does not exist`: install Android Studio or command-line tools into `~/Library/Android/sdk`, or set `ANDROID_HOME`.\n- `emulator: command not found`: install the SDK `emulator` package and ensure `~/Library/Android/sdk/emulator` is on `PATH`.\n- No AVD found: create `FRB_API_35` with `avdmanager create avd`, or pass the existing AVD name to `android emulator --avd`.\n- Docker cannot see the host emulator through `--android-emulator-adb`: verify the host emulator is running on console port `5554`, verify host `adb devices -l` shows `emulator-5554`, then run `adb connect host.docker.internal:5555` inside Docker or retry the helper command.\n- Flutter integration test fails with a VM service or DDS connection refused on `127.0.0.1`: the command likely used a host ADB server against an emulator. Use `--android-emulator-adb` so `adb forward` binds inside Docker.\n\n## Cleanup\n\nStop the emulator from host ADB:\n\n```bash\nadb -s emulator-5554 emu kill\n```\n\nAvoid deleting SDK packages or AVDs unless the user explicitly asks.\n",".claude/skills/frb-cargokit-dev/SKILL.md":"---\nname: frb-cargokit-dev\ndescription: Develop or update CargoKit for flutter_rust_bridge. Use when a bug or feature belongs in fzyzcjy/Cargokit, when updating the FRB CargoKit submodule pointers, or when preparing the paired CargoKit and FRB PRs that synchronize CargoKit changes into FRB integrate templates and examples.\n---\n\n# FRB CargoKit Development\n\n## Overview\n\nUse this skill when the actual fix belongs in the external `fzyzcjy/Cargokit` repository and FRB needs a follow-up PR that points its CargoKit submodules at the updated CargoKit branch or commit.\n\nThe usual shape is two PRs:\n\n1. A CargoKit PR with the real implementation.\n2. An FRB PR that updates the two CargoKit submodule pointers and regenerates copied integrate outputs.\n\nDo not implement CargoKit behavior only in copied FRB example output. The source of truth is the CargoKit repository referenced by the template submodules.\n\n## Source Locations\n\nFRB embeds CargoKit as git submodules in two template locations:\n\n- `frb_codegen/assets/integration_template/app/rust_builder/cargokit`\n- `frb_codegen/assets/integration_template/plugin/cargokit`\n\nBoth submodules should normally point at the same CargoKit branch or commit for the same logical update. Downstream generated copies may appear under:\n\n- `frb_example/**/cargokit/**`\n- `frb_example/**/rust_builder/cargokit/**`\n\nRead `frb-cargokit` first if you need to classify an existing diff before deciding where the change belongs.\n\n## Workflow\n\n### 1. Prepare The CargoKit Change\n\nWork in `fzyzcjy/Cargokit` for the actual CargoKit implementation.\n\n1. Create a CargoKit branch whose name is easy to reference from the FRB PR.\n2. Implement the fix in CargoKit and run the relevant CargoKit checks from that repository.\n3. Push the branch and open a CargoKit PR.\n4. Keep the branch alive until the FRB PR has passed CI.\n\nIf you are editing through an FRB submodule checkout, remember that commits belong to the nested CargoKit repository, not the outer FRB repository. Push the nested repository branch before updating the outer FRB submodule pointer.\n\n### 2. Update FRB To Point At The CargoKit Branch\n\nIn the FRB repository:\n\n1. Initialize submodules if needed:\n\n   ```bash\n   git submodule update --init --recursive\n   ```\n\n2. Fetch and check out the CargoKit branch or commit in both submodule checkouts:\n\n   ```bash\n   git -C frb_codegen/assets/integration_template/app/rust_builder/cargokit fetch origin <branch-or-ref>\n   git -C frb_codegen/assets/integration_template/app/rust_builder/cargokit checkout <commit-or-branch>\n   git -C frb_codegen/assets/integration_template/plugin/cargokit fetch origin <branch-or-ref>\n   git -C frb_codegen/assets/integration_template/plugin/cargokit checkout <commit-or-branch>\n   ```\n\n3. Stage the submodule pointer updates in the outer FRB repository:\n\n   ```bash\n   git add frb_codegen/assets/integration_template/app/rust_builder/cargokit\n   git add frb_codegen/assets/integration_template/plugin/cargokit\n   ```\n\n4. Confirm the outer FRB diff shows submodule pointer changes for both paths.\n\nUseful checks:\n\n```bash\ngit submodule status --recursive\ngit diff --submodule\n```\n\nDo not leave one template submodule on the old CargoKit commit unless the app and plugin templates intentionally need different CargoKit revisions.\n\n### 3. Regenerate FRB Integrate Outputs\n\nAfter changing either CargoKit submodule pointer, run:\n\n```bash\n./frb_internal precommit-integrate\n```\n\nThis updates copied CargoKit output and platform scaffold files under `frb_example/**`. Those downstream diffs are expected when CargoKit files are copied from the updated template.\n\nStage and commit the regenerated integrate output together with the submodule pointer update:\n\n```bash\ngit add frb_example frb_codegen/assets/integration_template\ngit commit -m \"Update CargoKit template pointers\"\n```\n\nThen run the normal PR checks from `frb-prepare-pr`, especially:\n\n```bash\n./frb_internal lint --fix\n```\n\nRun focused tests when the CargoKit change affects a platform that can be exercised locally. Otherwise let FRB CI cover the platform matrix and use `frb-fix-ci` for failures.\n\n### 4. Open The FRB PR\n\nThe FRB PR should mention that CargoKit is intentionally pointed at the temporary CargoKit PR branch or commit. Keep the PR body empty if the local PR workflow requires it, but use GitHub comments when you need to leave coordination notes.\n\nBefore asking for final review:\n\n1. Wait for the FRB PR CI to pass with the temporary CargoKit pointer.\n2. Ask reviewers to merge the CargoKit PR first.\n3. Update the FRB submodule pointers from the temporary branch commit to the merged CargoKit commit on the canonical branch.\n4. Rerun `./frb_internal precommit-integrate` if the CargoKit commit changed.\n5. Push the refreshed FRB PR and wait for CI again.\n6. Ask reviewers to merge the FRB PR.\n\n## CI Failure Triage\n\nWhen FRB CI fails after a CargoKit update, classify the failure before editing:\n\n- CargoKit behavior failure: fix it in `fzyzcjy/Cargokit`, then update both FRB submodule pointers.\n- Missing integrate refresh: run `./frb_internal precommit-integrate` and commit the copied output.\n- Lint or formatting drift: run `./frb_internal lint --fix`.\n- Platform-specific FRB scaffold issue: fix the FRB integration template, then run `./frb_internal precommit-integrate`.\n\nDo not patch copied `frb_example/**/cargokit/**` files directly as the final fix. They should be produced from the updated CargoKit submodule and integrate templates.\n\n## Related Skills\n\n- `frb-cargokit` - Understand CargoKit ownership and diff layers\n- `frb-code-generation` - Choose the correct generation command\n- `frb-prepare-pr` - Prepare an FRB PR\n- `frb-fix-ci` - Diagnose CI failures after the PR is opened\n",".claude/skills/frb-cargokit/SKILL.md":"---\nname: frb-cargokit\ndescription: Explain how cargokit is sourced, copied, and analyzed inside flutter_rust_bridge. Use when working on `integration_template/**/cargokit`, `frb_example/**/cargokit`, integrate/generate output drift, or any diff that mentions cargokit and you need to identify the true source of truth before judging the change.\n---\n\n# FRB Cargokit\n\n## Overview\n\nUse this skill to avoid mixing together three different things: the external cargokit repository, the template submodule pointers in FRB, and copied cargokit files inside generated examples.\n\nThe main job is to determine which layer a change belongs to before analyzing whether the change is correct.\n\n## Mental Model\n\nThere are two important cargokit forms inside FRB:\n\n1. External source\n   FRB depends on an external cargokit repository through git submodules under `frb_codegen/assets/integration_template/**/cargokit`.\n\n2. Copied outputs\n   FRB commands such as integrate/generate often copy concrete cargokit files into `frb_example/**/cargokit` or `frb_example/**/rust_builder/cargokit`.\n\nThis means a large cargokit diff in examples often does not mean FRB designed a large new cargokit behavior. It may only mean a template or submodule change propagated into copied outputs.\n\n## Source Of Truth\n\nTreat these paths as the source of truth when deciding cargokit behavior:\n\n- `frb_codegen/assets/integration_template/app/rust_builder/cargokit`\n- `frb_codegen/assets/integration_template/plugin/cargokit`\n\nTreat these paths as downstream copies unless the evidence shows otherwise:\n\n- `frb_example/**/cargokit/**`\n- `frb_example/**/rust_builder/cargokit/**`\n\nDo not start by judging copied example files in isolation. First determine which source submodule pointer or template content produced them.\n\n## Synchronizing Copies\n\nUse `./frb_internal sync-cargokit-copies` to refresh checked-in Cargokit copies from the template submodules when those copies are not already regenerated by the integrate CI. The command mirrors the Rust integration template writer's Cargokit filtering and copied-file prelude.\n\nDo not add `flutter_via_create`, `flutter_via_integrate`, or `flutter_package` to this sync map; the integrate generation CI already recreates those examples from templates. The CI `Generate :: Internal` job runs this as part of `./frb_internal generate-internal --set-exit-if-changed --coverage`, so the normal generate-internal diff gate also catches Cargokit copy drift. If Cargokit copies drift, fix the source-of-truth template/submodule first when appropriate, run `./frb_internal sync-cargokit-copies`, and commit the resulting copied-output refresh.\n\n## Diff Analysis Order\n\nWhen a user asks \"what happened to cargokit?\" follow this order:\n\n1. Identify the layer.\n   Decide whether the diff is a submodule pointer update, a template file change, or copied output churn.\n\n2. If the change is a submodule pointer update, inspect the two external cargokit commits first.\n   Compare the old and new submodule SHAs inside the submodule repository before saying anything about the FRB-side diff.\n\n3. Only then inspect copied example files.\n   Ask whether they merely reflect the upstream change or whether FRB also introduced independent local edits.\n\n4. Separate semantic changes from churn.\n   Distinguish real logic changes from format-only refreshes, lockfile updates, scaffold refreshes, and regenerated outputs.\n\n## What To Say Explicitly\n\nWhen answering, state which of these is true:\n\n- \"This is an external cargokit change exposed by a submodule pointer update.\"\n- \"This is downstream copy churn caused by integrate/generate.\"\n- \"This repository also has local copied cargokit edits in examples.\"\n\nIf discussing correctness, tie the judgment to the correct layer:\n\n- For submodule updates, judge the diff between the two external SHAs.\n- For copied outputs, judge whether the copy is expected and synchronized.\n- Do not infer upstream intent from a regenerated example diff alone.\n\n## Common Mistakes\n\nAvoid these mistakes:\n\n- Treating `frb_example/**/cargokit/**` as the primary implementation source.\n- Assuming a large example diff implies a large semantic cargokit change.\n- Analyzing copied files before checking the submodule pointer diff.\n- Mixing \"FRB updated the pointer\" with \"FRB locally rewrote cargokit.\"\n\n## Scope Boundary\n\nThis skill explains ownership, propagation, and diff interpretation.\n\nUse `frb-code-generation` when you need to know which generation command to run.\nUse `frb-debugging` when generation or cargokit-related behavior is failing and needs deeper investigation.\n",".claude/skills/frb-categorize-code-diff/SKILL.md":"---\nname: frb-categorize-code-diff\ndescription: Use when classifying a flutter_rust_bridge branch or PR diff into test and non-test changes, including colocated Rust test modules.\n---\n\n# 1 When to use\n\n- Use this skill to answer which changes in a branch or PR are tests and which are not.\n- Use the repository script instead of classifying paths by hand. Rust tests are commonly colocated in production `.rs` files.\n\n## 2 Run\n\n- Fetch the PR base before relying on `origin/master`.\n- Run from the repository root:\n\n```bash\nuv run --no-project .claude/skills/frb-categorize-code-diff/categorize_code_diff.py --base origin/master --head HEAD\n```\n\n- Add `--json` when another tool will consume the result.\n- Pass the PR's actual base revision when it is not `origin/master`.\n\n## 3 Interpret\n\n- `test-only`: every changed line is in a dedicated test path or a recognized colocated Rust test region.\n- `non-test-only`: every changed line is outside recognized test code.\n- `mixed`: the file contains both test and non-test changed lines.\n- `unclassified`: the diff cannot be mapped confidently, such as a binary patch.\n- Treat the non-test list as conservative. Unknown code is non-test rather than silently counted as test.\n\n## 4 Reliability\n\n- The script compares `base...head`, matching GitHub pull request diff semantics.\n- Dedicated test paths include `test/`, `tests/`, `test_fixtures/`, and conventional test filenames.\n- Rust inline classification recognizes brace-delimited items preceded by `#[cfg(test)]` or `#[test]` in both the base and head revisions.\n- Re-run after every PR update. Line identities and rename targets may change.\n- Inspect `mixed` files manually before claiming that a PR contains no production changes.\n\n## 5 Validation\n\n- Run the script tests after changing its classification logic:\n\n```bash\nPYTHONDONTWRITEBYTECODE=1 uv run --no-project --with pytest --with typer pytest -q .claude/skills/frb-categorize-code-diff/test_categorize_code_diff.py\n```\n\n- Keep `PYTHONDONTWRITEBYTECODE=1`; otherwise pytest leaves an untracked `__pycache__/` inside the skill directory.\n",".claude/skills/frb-ci-filter/SKILL.md":"---\nname: frb-ci-filter\ndescription: Use when running focused flutter_rust_bridge GitHub Actions CI via ci_filter, adding/removing the ci-manual-dispatch PR label, choosing exact CI jobs or matrix entries, documenting intentionally partial CI runs, or repeatedly validating an exact CI target on one commit.\n---\n\nUse this skill when CI feedback is too expensive and the current task needs a focused GitHub Actions signal instead of automatic full CI.\n\nThe preferred mechanism is the formal `workflow_dispatch` input in `.github/workflows/ci.yaml`:\n\n```bash\ngh workflow run ci.yaml --ref <branch> -f 'ci_filter=<filter>'\n```\n\nDo not temporarily hack workflow jobs just to get partial CI unless the formal filter cannot express the target.\n\n# 1 Filter Syntax\n\n```text\nfull\njob_id\njob_a,job_b\njob_id[dimension=value]\njob_id[dimension=value|other_value,other_dimension=value]\njob_a,job_b[dimension=value]\n```\n\n- `full` or `*` runs the normal full CI plan.\n- `job_id` runs the whole job, including its full matrix if it has one.\n- `job_id[...]` runs only matching matrix entries.\n- Bracket filters currently require `dimension=value`.\n- Commas outside brackets combine multiple jobs.\n- Explicit `dimension=value` conditions inside brackets are ANDed.\n- `|` means OR within one dimension.\n- Matrix dimensions include direct keys such as `image`, `package`, `device`, `api-level`, `sanitizer`, `platforms`, and nested `matrix.info` keys such as `platform`, `target`, `version`.\n- Numeric matrix values are matched as strings, e.g. `api-level=35`.\n- Values may contain spaces, e.g. iOS simulator device names.\n- Unknown jobs, unknown dimensions, malformed filters, and filters matching no matrix entries fail in the `Plan :: CI` job.\n\n# 2 Manual Dispatch Label\n\nUse the `ci-manual-dispatch` PR label when an agent or human wants to prevent automatic PR CI from running the full surface while iterating.\n\n```bash\ngh pr edit <pr-number> --add-label ci-manual-dispatch\n```\n\nWith the label present:\n\n- normal PR-triggered CI produces an empty plan for the heavy jobs\n- `workflow_dispatch` remains available for explicit focused runs\n- the PR must not be treated as merge-ready from partial runs alone\n\nBefore final readiness:\n\n```bash\ngh pr edit <pr-number> --remove-label ci-manual-dispatch\n```\n\nThen let the normal full CI surface run. Removing the label triggers PR CI because the workflow listens to `labeled` and `unlabeled` pull request events.\n\n# 3 Manual Dispatch PR Comment\n\nManual `workflow_dispatch` runs of `.github/workflows/ci.yaml` comment from `Plan :: CI` on the matching open PR with the selected `ci_filter` and run link; normal `push` and `pull_request` runs do not comment.\n\n# 4 Local Planning\n\nBefore dispatching an expensive manual run, smoke the filter locally:\n\n```bash\n./frb_internal plan-ci --filter 'test_dart_web[package=frb_example--pure_dart_pde]'\n```\n\nThe command emits a GitHub Actions output line:\n\n```text\nplan={...}\n```\n\nThe workflow consumes it with paths like:\n\n```yaml\nfromJSON(needs.plan_ci.outputs.plan).test_dart_web.enable\nfromJSON(needs.plan_ci.outputs.plan).test_dart_web.matrix\n```\n\nThat output shape is intentional; each CI job has its own top-level entry containing `enable`, and matrix jobs also contain `matrix`.\n\n# 5 Repeated Precise Validation\n\nUse repeated precise validation when a flaky fix needs several independent executions of one CI job or matrix entry on exactly the same commit.\n\n- **Fix the commit**: Push the branch once, record its SHA, and do not move the branch until all runs finish.\n- **Smoke the filter**: Run `./frb_internal plan-ci --filter '<filter>'` before dispatching.\n- **Dispatch independently**: Create five separate `workflow_dispatch` runs, not one run with five matrix copies and not five attempts of one run.\n- **Keep the filter identical**: Pass the same exact `ci_filter` to every dispatch.\n- **Verify the SHA**: Confirm every selected run reports the recorded `headSha`; a branch name alone is not evidence that queued runs used the same commit.\n- **Count target jobs**: Count the conclusion of the selected job or matrix entry, not the workflow conclusion or skipped jobs.\n- **Preserve every run link**: Report all five run URLs, the fixed SHA, the filter, and the target-job result.\n- **Do not move or cancel**: A new commit invalidates the comparison; cancellation does not count as a completed validation.\n\nRecord the immutable input:\n\n```bash\nbranch=codex/example-fix\nexpected_sha=$(git rev-parse HEAD)\nfilter='test_dart_web[package=frb_example--pure_dart]'\ngit push -u origin \"$branch\"\n```\n\nDispatch five independent runs. Manual dispatches use unique concurrency groups, so these runs do not cancel one another:\n\n```bash\nfor run_number in 1 2 3 4 5; do\n  gh workflow run ci.yaml --ref \"$branch\" -f \"ci_filter=$filter\"\ndone\n```\n\nList the candidate runs once, then select the five runs created for this validation window:\n\n```bash\ngh run list \\\n  --workflow ci.yaml \\\n  --branch \"$branch\" \\\n  --event workflow_dispatch \\\n  --limit 10 \\\n  --json databaseId,createdAt,headSha,status,conclusion,url\n```\n\nFor each selected run:\n\n- Confirm `headSha == expected_sha`.\n- Confirm `Plan :: CI` creates only the requested job or matrix entry.\n- Wait for the target job to complete.\n- If it fails, archive and inspect that job's complete log.\n- Report a result only after all five target jobs finish.\n\nDo not claim “5/5” from any of these shapes:\n\n- One workflow run containing five matrix entries.\n- One workflow run rerun five times.\n- Five workflow runs whose `headSha` values differ.\n- Five workflow conclusions where the target job was skipped, cancelled, or never started.\n- Fewer than five completed target jobs plus queued or in-progress jobs.\n\n# 6 Examples\n\nThese examples are protected by `tools/frb_internal/test/ci_plan_test.dart`; update that test whenever adding or changing examples here.\n\nFull CI:\n\n```bash\ngh workflow run ci.yaml --ref <branch> -f 'ci_filter=full'\n```\n\nOne non-matrix job:\n\n```bash\ngh workflow run ci.yaml --ref <branch> -f 'ci_filter=lint_dart_primary'\n```\n\nSeveral whole jobs:\n\n```bash\ngh workflow run ci.yaml --ref <branch> -f 'ci_filter=lint_dart_primary,test_dart_web'\n```\n\nOne non-matrix job plus one matrix entry:\n\n```bash\ngh workflow run ci.yaml --ref <branch> -f 'ci_filter=lint_dart_primary,test_dart_web[package=frb_example--pure_dart]'\n```\n\nOne Dart web package:\n\n```bash\ngh workflow run ci.yaml --ref <branch> -f 'ci_filter=test_dart_web[package=frb_example--pure_dart_pde]'\n```\n\nTwo Dart web packages:\n\n```bash\ngh workflow run ci.yaml --ref <branch> -f 'ci_filter=test_dart_web[package=frb_dart|frb_example--pure_dart_pde]'\n```\n\nTwo Dart web packages plus one non-matrix job:\n\n```bash\ngh workflow run ci.yaml --ref <branch> -f 'ci_filter=test_dart_web[package=frb_dart|frb_example--pure_dart_pde],lint_rust_primary'\n```\n\nTwo precise matrix tuples in the same job:\n\n```bash\ngh workflow run ci.yaml --ref <branch> -f 'ci_filter=test_rust[image=ubuntu-latest,version=nightly],test_rust[image=ubuntu-latest,version=1.85.0]'\n```\n\nOne Dart native package on Linux:\n\n```bash\ngh workflow run ci.yaml --ref <branch> -f 'ci_filter=test_dart_native[image=ubuntu-24.04,package=tools--frb_internal]'\n```\n\nRust tests on selected toolchains:\n\n```bash\ngh workflow run ci.yaml --ref <branch> -f 'ci_filter=test_rust[image=ubuntu-latest,version=nightly|1.85.0]'\n```\n\nOne Flutter desktop package on Linux:\n\n```bash\ngh workflow run ci.yaml --ref <branch> -f 'ci_filter=test_flutter_native_desktop[platform=linux,package=frb_example--gallery]'\n```\n\nOne Android emulator matrix entry:\n\n```bash\ngh workflow run ci.yaml --ref <branch> -f 'ci_filter=test_flutter_native_android[package=frb_example--flutter_via_create,device=pixel,api-level=35]'\n```\n\nOne iOS simulator matrix entry:\n\n```bash\ngh workflow run ci.yaml --ref <branch> -f 'ci_filter=test_flutter_native_ios[device=iPhone 16 Pro Max Simulator (18.6),package=frb_example--rust_ui_counter--ui]'\n```\n\nOne codegen command-generate entry:\n\n```bash\ngh workflow run ci.yaml --ref <branch> -f 'ci_filter=generate_run_frb_codegen_command_generate[image=ubuntu-24.04,package=frb_example--integrate_third_party]'\n```\n\nOne OHOS command-integrate entry:\n\n```bash\ngh workflow run ci.yaml --ref <branch> -f 'ci_filter=generate_run_frb_codegen_command_integrate[platforms=ohos]'\n```\n\nOne sanitizer entry:\n\n```bash\ngh workflow run ci.yaml --ref <branch> -f 'ci_filter=test_dart_sanitizer[sanitizer=asan,package=frb_example--pure_dart]'\n```\n\nOne benchmark runner:\n\n```bash\ngh workflow run ci.yaml --ref <branch> -f 'ci_filter=bench_dart_native[image=ubuntu-24.04]'\n```\n\n# 7 Choosing Evidence\n\nFor intentional red reproduction PRs:\n\n- Run only the single job or matrix entry that proves the reported bad behavior.\n- State the exact `ci_filter` in the PR body.\n- State the expected failure text or behavior.\n- Do not treat the narrowed red run as fix readiness evidence.\n\nFor ordinary iteration branches:\n\n- Prefer the smallest filter that exercises the changed behavior.\n- Say in PR status/comments when only filtered CI has run.\n- Before final review or merge readiness, remove `ci-manual-dispatch` and run normal CI.\n\n# 8 Validation\n\nBefore pushing changes to the filter mechanism or examples:\n\n```bash\ncd tools/frb_internal\ndart test test/ci_plan_test.dart\n```\n\nUseful smoke check:\n\n```bash\n./frb_internal plan-ci --filter 'test_dart_web[package=frb_example--pure_dart_pde]'\n```\n",".claude/skills/frb-code-generation/SKILL.md":"---\nname: frb-code-generation\ndescription: Use when modifying Rust APIs, codegen, generated examples, or platform scaffolds in flutter_rust_bridge to select generation commands and preserve source-of-truth and convergence rules\n---\n\n# FRB Code Generation\n\n> **Note:** Read `frb-dev-env` before running commands and follow the active user's environment rules.\n\n## Overview\n\nflutter_rust_bridge requires code generation when Rust APIs change. This skill maps change types to the minimal generation commands needed.\n\n**Core principle:** Run only the generation commands needed for your change type.\n\n**Validation rule:** If regenerated outputs cause previously green non-`Generate` jobs to fail, treat the generated outputs as suspect until they are validated from a clean environment.\n\n> **After codegen:** Follow `frb-dev-env` and verify generated output is present in the selected worktree before reviewing the diff.\n\n## Quick Reference\n\n| Change Type | Command |\n|-------------|---------|\n| Example Rust API (`frb_example/**/rust/src/api/*.rs`) | `./frb_internal precommit-generate` |\n| Flutter integrate examples | `./frb_internal precommit-integrate` |\n| `frb_codegen/` generation logic | `./frb_internal precommit-generate` + `generate-internal-rust` |\n| `frb_rust/src/` core API | `./frb_internal generate-internal-rust` |\n| `frb_example/pure_dart` generator | `./frb_internal generate-internal-frb-example-pure-dart` |\n| CLI help documentation | `./frb_internal generate-internal-book-help` |\n| Root `README.md` or `CHANGELOG.md` | `./frb_internal generate-internal-readme` |\n| Other non-generated docs, comments, tests, or `frb_dart/` source | No generation needed |\n\n## Important Rules\n\n### Root Documentation Fan-Out\n\nTreat the root `README.md` and `CHANGELOG.md` as sources of truth. After changing either file, run:\n\n```bash\n./frb_internal generate-internal-readme\n```\n\nCommit the generated package documentation in the same change. Do not manually edit `frb_dart/README.md`, `frb_dart/CHANGELOG.md`, or `frb_hooks/CHANGELOG.md`.\n\n### Integrate Template Drift\n\nIf a diff touches Flutter integrate example outputs, platform scaffolds, or copied `cargokit` files under `frb_example/**`, check whether the real source of truth should also change under `frb_codegen/assets/integration_template/`.\n\nDo not submit only downstream integrate output changes when the behavior belongs in the template. Update the template first, then run:\n\n```bash\n./frb_internal precommit-integrate\n```\n\nTreat `frb_codegen/assets/integration_template/**/cargokit` as the source of truth for copied `cargokit` output. If the actual bug belongs inside the external `cargokit` submodule, read the `frb-cargokit` skill before deciding whether to patch the submodule and update its pointer.\n\nFor CI diagnosis rules about generated-file format/lint failures, repeated package-level `Generate` drift, or `Generate :: FRB Codegen :: Command Integrate` failures, you MUST read `frb-fix-ci` first. This skill is for command selection, not CI failure-propagation diagnosis.\n\nFor `pure_dart` / `pure_dart_pde` generation issues, treat `frb_example/pure_dart` as the upstream source and `frb_example/pure_dart_pde` as the derived copy. See `frb-fix-ci` for the CI diagnosis workflow.\n\nIf CI repair has already entered repeated package-level drift, you MUST stop choosing narrower commands and switch to `frb-fix-ci`.\n\nDo not manually patch generated files as the final fix. The final accepted result should be produced by the corresponding generation command in a clean matching environment.\n\n### Generation Convergence\n\n- Run the narrowest owning generator before broad generation.\n- Normalize the intended source inputs, resolve dependencies, run final code generation, and format with the target toolchain. Do not hand formatter-unstable intermediate text to a different generation lane.\n- Run the owning generation command a second time and require a clean diff. A single successful run is not convergence.\n- Preserve semantic equivalence in templates. Do not move construction, scope, lifetime, or callback boundaries merely to make new formatter output look smaller.\n\n### Generated Output Provenance\n\nClassify every changed path before accepting generated drift:\n\n| Provenance | Required action |\n| --- | --- |\n| Integration template | Change the template source and regenerate consumers |\n| Apple scaffold asset | Refresh through the owning scaffold workflow |\n| CargoKit copy | Read `frb-cargokit` and follow its ownership and synchronization rules |\n| Generated example output | Keep the generator and snapshot together |\n| Flutter migrator edit in a legacy example | Apply the exact current scaffold migration and record it as direct |\n| Unexplained manual edit | Revert it or identify its owning source before proceeding |\n\n- When an old example has no supported regeneration entry point, compare it with a fresh target-Flutter scaffold and directly apply only required tool-defined migrations.\n- Record direct legacy scaffold migrations separately from automatic output.\n\n### OHOS Integrate Composition\n\n- Treat the checked-in package and the older OHOS Flutter fork as two distinct sources of truth.\n- Generate generic Flutter scaffold files with the current Flutter package.\n- Overlay only explicit OHOS-owned paths from the OHOS fork: `ohos` and, when required by the package, `rust_builder/ohos` and `rust_builder/pubspec.yaml`.\n- Exclude transient trees such as `node_modules` and preserve intentional deletions instead of resurrecting stale files.\n- Stage and validate the composed result before replacing the canonical package. Restore the original package if create, dependency resolution, code generation, formatting, or the final swap fails.\n- Test both success and failure paths. Do not add production APIs whose only purpose is exposing internals to tests.\n",".claude/skills/frb-debugging/SKILL.md":"---\nname: frb-debugging\ndescription: Use when generated code looks wrong, code generation fails, or you need to understand FRB internals\n---\n\n# FRB Debugging\n\n> **Note:** Read `frb-dev-env` before running commands and follow the active user's environment rules.\n\n## When to Use\n\n- Generated code looks wrong\n- Code generation fails\n- Need to understand FRB internals\n- Runtime errors in generated code\n\n## Quick Diagnostics\n\n### Dump Intermediate Representations\n\nThe in-tree examples by default enable `dump_all: true` in  `flutter_rust_bridge.yaml` for debugability.\n\nTherefore, you can check `rust/target/frb_dump/` for:\n- Effective configuration\n- IR (intermediate representation)\n- Generated spec and code\n- ...\n\n### Debug Logs\n\n```bash\nRUST_LOG=debug flutter_rust_bridge_codegen ...\n```\n\n## Full Troubleshooting\n\nIf above doesn't help, see:\n\n* `website/docs/docs/guides/contributing/tip.md`\n* `website/docs/manual/troubleshooting.md`\n\nBefore going deep into FRB internals, first check whether you are actually seeing failure propagation from unstable generated outputs or integrate templates.\n\nIn particular:\n\n- If CI failures may be explained by failure propagation, repeated package-level drift, or unstable generated outputs, you MUST use `frb-fix-ci` first\n- You MUST only stay in `frb-debugging` after `frb-fix-ci` has ruled out those simpler explanations\n\n## Related Skills\n\n- `frb-code-generation` - Which generation commands to run\n- `frb-fix-ci` - Diagnose CI failure propagation before deep debugging\n- `frb-develop-feature` - Development workflow\n",".claude/skills/frb-dev-env/SKILL.md":"---\nname: frb-dev-env\ndescription: \"Use when the user wants Docker-based FRB development or Tart-based iOS Simulator validation.\"\n---\n\n# FRB Development Environment\n\nUse this skill before setting up, diagnosing, or running `flutter_rust_bridge` commands with a container-based development workflow.\n\n## Choosing an Environment\n\nUse Docker for normal FRB development checks that fit Linux containers: Rust tests, Dart tests, web tests, code generation, lint, Android build checks that do not need a running emulator, and most `./frb_internal` commands. Docker is the default reproducible baseline when local host toolchains are missing or suspected to have drifted.\n\nUse a host Android Emulator for local Android runtime validation when a running Android target is required. Keep FRB, Flutter, Rust, Gradle, and Android build tooling inside Docker. The host runs only the emulator and the Android SDK command-line tools needed to manage it. Docker runs its own ADB server and connects directly to the emulator TCP endpoint so Flutter's VM service forwarding stays inside Docker. See the Android section below.\n\nUse Tart only for checks that need macOS and Xcode, especially iOS Simulator validation. The Tart workflow is intentionally separate from Docker; it gives each worktree a reusable macOS VM cloned from a prepared base VM, then runs FRB commands inside that VM.\n\nDo not use the Tart macOS VM as the Android emulator strategy. macOS Tart guests do not support nested virtualization, so Android emulator runtime coverage needs a host or runner with virtualization support. Android SDK compilation checks can still use Docker when they do not need a running emulator.\n\n## Principles\n\n- FRB runs locally in Docker by default. Do not use remote workspaces for FRB unless the user explicitly asks for a one-off remote run.\n- Use the current checkout/worktree that Codex or the user already selected. Do not create a new worktree unless the user explicitly asks.\n- Run FRB commands from the repository root unless the command itself intentionally changes directories.\n- Prefer repository tooling such as `./frb_internal` over ad hoc direct invocations.\n- Do not manually edit generated files (`frb_generated.*`, `*.freezed.dart`, `*.g.dart`) as the final fix.\n\n## PATH Bootstrap\n\nBefore using this skill's helper or FRB tooling, make sure common local tool locations are in `PATH`. Codex shells may start with a reduced `PATH`, which can hide `uv`, Docker, Dart, Flutter, or Homebrew-installed tools even when they exist on the machine.\n\nUse this prefix when a command reports `env: uv: No such file or directory`, `docker: command not found`, `dart: command not found`, or similar:\n\n```bash\nexport PATH=\"$HOME/.local/bin:/opt/homebrew/bin:/usr/local/bin:$PATH\"\n```\n\nThen retry the same command. Prefer fixing `PATH` over bypassing the helper, because `frb_dev_env.py` uses the configured per-worktree container and release credential handling.\n\n## First Checks\n\nBefore running tests, lint, code generation, or setup:\n\n```bash\ngit rev-parse --show-toplevel\ngit status --short\ngit submodule status --recursive\n```\n\nIf submodules are uninitialized, initialize them locally:\n\n```bash\ngit submodule update --init --recursive\n```\n\n## Docker\n\nUse Docker for container-based FRB development. Each worktree should have one long-lived local container that is reused for tests, lint, code generation, and setup commands.\n\nUse `frb_dev_env.py` next to this skill to inspect, create, start, and reuse the per-worktree container. The script derives the container name from the canonical worktree root only:\n\n```text\nfrb-<worktree-root-sha256-prefix-12>\n```\n\nIt mounts the canonical worktree root and git common root at their host-like absolute paths, and labels the container with:\n\n```text\nfrb.dev.repo=flutter_rust_bridge\nfrb.dev.worktree=<canonical worktree root>\nfrb.dev.git-common-root=<git common root>\nfrb.dev.layout-version=3\n```\n\nCommands run from the host-like worktree path inside the container. There is intentionally no `/workspace` alias, because linked git worktrees and submodule gitdir references need the same path shape that they have on the host.\n\nWhen creating a new per-worktree container, the helper pulls the selected Docker image before `docker run`. Existing containers are validated and reused without pulling.\n\nTypical usage:\n\n```bash\n.claude/skills/frb-dev-env/frb_dev_env.py docker info\n.claude/skills/frb-dev-env/frb_dev_env.py docker create\n.claude/skills/frb-dev-env/frb_dev_env.py docker exec -- bash -lc './frb_internal --help'\n```\n\n### Temporary Docker Commands With Publish Credentials\n\nUse `docker-run-rm --with-publish-credentials` only for release publishing commands that need host credentials. Unlike the normal long-lived development container, this runs a fresh temporary container with `docker run --rm`, mounts the worktree and git common root, mounts the host credential directories read-only, copies the credential files into temporary container-local homes, checks login status, runs the requested command, then discards the container.\n\nThe publish credential flag mounts these host credential sources when present:\n\n- GitHub CLI config from `GH_CONFIG_DIR` or `~/.config/gh`\n- GitHub token env vars from `GH_TOKEN` or `GITHUB_TOKEN`, forwarded without printing token values\n- Cargo credentials and config from `CARGO_HOME` or `~/.cargo`\n- Git config from `~/.gitconfig` and `~/.config/git`\n- Dart pub credentials from `PUB_CACHE`, `~/.pub-cache`, `~/.config/dart`, or `~/Library/Application Support/dart`\n\nPublish mode also prepares the container environment used by the release command:\n\n- Reuses the Docker image's Rust toolchain via `RUSTUP_HOME=/root/.rustup` even though `HOME` is changed to a temporary credential directory.\n- Rewrites copied Git/Cargo config proxy URLs from `localhost` or `127.0.0.1` to `host.docker.internal`, and forwards common proxy environment variables with the same rewrite. This lets a container use a host proxy such as `http://localhost:7897`.\n- Checks GitHub auth, GitHub network access, Cargo availability, crates.io search access, and Dart pub credential readability before running the requested command.\n\nRun the credential preflight before any irreversible release step:\n\n```bash\n.claude/skills/frb-dev-env/frb_dev_env.py docker-run-rm --with-publish-credentials -- true\n```\n\nRun release publishing through a temporary publish-credential container. Do not manually add `RUSTUP_HOME` or proxy environment overrides unless debugging the helper itself:\n\n```bash\n.claude/skills/frb-dev-env/frb_dev_env.py docker-run-rm --with-publish-credentials -- ./frb_internal release\n```\n\nThe credential preflight fails before running release commands if host GitHub CLI auth is invalid, Cargo credentials are missing, Dart pub credentials are missing, the image has no usable Cargo toolchain, or the container cannot reach GitHub/crates.io through the configured network path. It then re-checks GitHub CLI auth inside the temporary container and runs `gh auth setup-git` there so HTTPS `git push` can use the mounted GitHub CLI auth.\n\n### Cleanup\n\nDelete a worktree's Docker container when the worktree is no longer needed, or when local Docker resources are getting crowded:\n\n```bash\n.claude/skills/frb-dev-env/frb_dev_env.py docker delete\n```\n\nThe delete command validates the container labels before removing it. Use `--force` only when intentionally removing a mismatched container.\n\nUse the project `frb-docker` skill for image, devcontainer, and Dockerfile details.\n\n## Android Host Runtime\n\nUse this workflow for local Android runtime checks when Docker can build and run the FRB/Flutter command, but the Android target itself must be managed by the host.\n\n### Path Selection\n\nThe supported Android runtime path is:\n\n```text\nhost Android Emulator:\n  host runs the emulator process\n  Docker runs FRB/Flutter and its own ADB server\n  Docker ADB connects to host.docker.internal:<emulator-adb-port>\n```\n\nPhysical Android phones are intentionally out of scope for this helper until that path is manually validated. The host does not need FRB, Flutter, Rust, Cargo, Java, or Gradle for the emulator path.\n\n### Host Emulator Architecture\n\nUse this split for a host Android Emulator:\n\n```text\nhost:\n  Android Emulator process\n  Android SDK command-line tools for SDK packages, AVD creation, emulator start/stop, and host-side inspection\n  Android SDK root: ~/Library/Android/sdk unless ANDROID_HOME is set\n  AVD root: ~/.android/avd\n\nDocker container:\n  FRB checkout\n  Flutter, Dart, Rust, Cargo, Java, Gradle\n  Android SDK/NDK/build tools needed for builds\n  Docker-local ADB server connected to host.docker.internal:<emulator-adb-port>\n```\n\nRead the project `frb-android-emulator-prepare` skill before preparing a host emulator. The host emulator path needs Java, Android SDK command-line tools, emulator packages, a system image, and an AVD in the standard Android Studio host locations.\n\nPrint the resolved host Android SDK environment if needed:\n\n```bash\n.claude/skills/frb-dev-env/frb_dev_env.py android env\n```\n\nStart the emulator on the host. Pin the port when you want a stable host serial such as `emulator-5554`; the paired emulator ADB TCP endpoint is one port higher, such as `host.docker.internal:5555`:\n\n```bash\n.claude/skills/frb-dev-env/frb_dev_env.py android emulator --avd FRB_API_35 --port 5554\n```\n\nVerify Docker-local ADB can see the host emulator:\n\n```bash\n.claude/skills/frb-dev-env/frb_dev_env.py docker exec --android-emulator-adb -- adb devices -l\n```\n\nRun the FRB/Flutter command against the Docker-visible emulator serial:\n\n```bash\n.claude/skills/frb-dev-env/frb_dev_env.py docker exec --android-emulator-adb -- \\\n  bash -lc './frb_internal test-flutter-native --package frb_example--flutter_via_create --flutter-test-args \"--device-id host.docker.internal:5555\"'\n```\n\n`docker exec --android-emulator-adb` first runs `adb connect host.docker.internal:5555` inside the container, then runs the requested command with the Android SDK x86_64 runtime library path set. Override the default endpoint with `--android-emulator-adb-host`, `--android-emulator-adb-port`, `FRB_ANDROID_EMULATOR_ADB_HOST`, or `FRB_ANDROID_EMULATOR_ADB_PORT`.\n\n### Why Emulator Does Not Use Host ADB Server\n\nDo not use a host ADB server for host emulator full Flutter integration tests. Flutter creates dynamic VM service `adb forward` ports where the ADB server runs. If the ADB server runs on the host, those forwarded ports bind on host `127.0.0.1`, but Flutter is running inside Docker and then tries Docker-local `127.0.0.1`. This fails with DDS or VM service connection refused errors.\n\nUse `--android-emulator-adb` so the ADB server and the forwarded VM service port both live inside Docker.\n\n### Security And Cleanup\n\n- Use a single Android target when possible. With multiple emulators or devices, always pass `--device-id <serial>` through `--flutter-test-args`.\n- For host emulator Flutter integration tests, use `--android-emulator-adb` so `adb forward` creates VM service forwarding inside Docker.\n- Do not run the Android Emulator inside Tart. Do not rely on Docker nested virtualization for Android emulator runtime checks on macOS.\n- If the Docker image lacks Android SDK, NDK, or build-tools packages required by Flutter Android builds, update the FRB Docker image or mount a dedicated Android SDK into the container; do not install host Flutter or Gradle just to work around the missing container toolchain.\n\n## Tart\n\nUse Tart for iOS Simulator validation when Docker cannot provide the required macOS/Xcode runtime. The script uses one reusable Tart VM per worktree, named from the canonical worktree root:\n\n```text\nfrb-tart-<worktree-root-sha256-prefix-12>\n```\n\n### Base VM\n\nBy default, `create` clones from a prepared local base VM named `frb-tart-base`. Override it with `FRB_TART_BASE_VM` or `--base-vm`.\n\nRead `frb-tart-prepare` before creating or changing the base VM. The base VM should be built by the checked-in Packer template from a pinned Tart OCI artifact and treated as immutable; do not boot it and manually install tools into it.\n\n### Mounted Checkout\n\nFor linked git worktrees, the Tart helper shares both the canonical worktree root and the git common root into the VM, then mounts them at host-like absolute paths with `mount_virtiofs`. This keeps worktree `.git` files and submodule gitdir references valid inside the VM.\n\nUse plain `tart exec` for light commands that should see the mounted host checkout exactly, such as `git status`, `flutter --version`, `xcrun simctl list`, and simulator boot commands.\n\n### VM Lifecycle\n\nInspect, create, start, and run light commands in the per-worktree VM:\n\n```bash\n.claude/skills/frb-dev-env/frb_dev_env.py tart info\n.claude/skills/frb-dev-env/frb_dev_env.py tart create\n.claude/skills/frb-dev-env/frb_dev_env.py tart start\n.claude/skills/frb-dev-env/frb_dev_env.py tart ip --wait 180\n.claude/skills/frb-dev-env/frb_dev_env.py tart exec -- sw_vers\n```\n\n### Tart Guest Network Proxy\n\nWhen the Tart guest needs outbound network access through the host proxy, make the proxy listen on the host's Tart bridge address and pass that URL through the helper. Enable LAN access for the host proxy, keep the mixed proxy port at `7897`, and verify the host listens on `*:7897` rather than only `127.0.0.1:7897`.\n\nTart guests normally reach the host on `192.168.64.1`, so the usual proxy URL is:\n\n```bash\nexport FRB_TART_HOST_PROXY_URL=http://192.168.64.1:7897\n.claude/skills/frb-dev-env/frb_dev_env.py tart exec -- curl -I https://pub.dev\n```\n\nYou can also pass the proxy for a single command:\n\n```bash\n.claude/skills/frb-dev-env/frb_dev_env.py tart exec \\\n  --host-proxy-url http://192.168.64.1:7897 \\\n  -- curl -I https://pub.dev\n```\n\nDelete the worktree VM when it is no longer needed:\n\n```bash\n.claude/skills/frb-dev-env/frb_dev_env.py tart stop\n.claude/skills/frb-dev-env/frb_dev_env.py tart delete\n```\n\n### VM-Local Copy\n\nFor heavy iOS build/test commands, prefer uploading the current worktree to a VM-local copy and running there. This avoids writing Xcode build artifacts through the virtiofs host mount:\n\n```bash\n.claude/skills/frb-dev-env/frb_dev_env.py tart upload\n.claude/skills/frb-dev-env/frb_dev_env.py tart exec --sync-code -- ./frb_internal test-flutter-native --flutter-test-args '--device-id <UDID>' --package <package>\n```\n\n`tart upload` and `tart exec --sync-code` both first ensure the worktree VM is running and the host-like worktree mount exists. They then run `rsync` inside the VM from the mounted host worktree to a VM-local copy under `/Users/admin/frb-dev-env-local-copies/<worktree-hash>`. The upload excludes `.git`, `.dart_tool`, `build`, `target`, `.idea`, and `.vscode`. It does not delete existing files in the VM-local copy, so build caches survive repeated runs.\n\nCommands run with `--sync-code` start from the VM-local uploaded copy, not from the host-mounted path.\n\nExamples:\n\n```bash\n# Show the VM-local path after uploading the current worktree.\n.claude/skills/frb-dev-env/frb_dev_env.py tart upload\n\n# Run a heavy command from the uploaded VM-local copy.\n.claude/skills/frb-dev-env/frb_dev_env.py tart exec --sync-code -- ./frb_internal test-flutter-native --package frb_example--flutter_via_create --flutter-test-args '--device-id <UDID>'\n```\n\n### iOS Integration Preparation\n\nBefore running iOS Simulator integration tests, prepare the VM Flutter SDK once per VM or after replacing Flutter. This applies the runtime portion of `https://github.com/flutter/flutter/pull/187643.patch` as an idempotent Flutter tool workaround for an iOS simulator VM Service discovery race where `simctl launch` can emit the Dart VM Service log before Flutter's `simctl log stream` listener is ready.\n\nThe preparation command also rebuilds `flutter_tools.snapshot`:\n\n```bash\n.claude/skills/frb-dev-env/frb_dev_env.py tart prepare-ios-integration-test\n```\n\nUse the environment shown in the test command below for the integration test. `FLUTTER_GIT_URL` keeps `flutter doctor` and Flutter metadata consistent when the VM uses a local Flutter checkout. `FRB_SKIP_FLUTTER_DOCTOR=1` skips the FRB wrapper's preflight doctor because the simulator integration test itself is the validation target and doctor can hang after printing device checks in Tart.\n\n### iOS Integration Test\n\nRun iOS Simulator tests by starting the worktree VM, preparing the VM Flutter SDK, choosing and booting an iOS simulator inside the VM, then running the existing FRB test command from the VM-local uploaded copy:\n\n```bash\n.claude/skills/frb-dev-env/frb_dev_env.py tart start --wait 300\n.claude/skills/frb-dev-env/frb_dev_env.py tart prepare-ios-integration-test\n.claude/skills/frb-dev-env/frb_dev_env.py tart exec -- xcrun simctl list devices available\n.claude/skills/frb-dev-env/frb_dev_env.py tart exec -- xcrun simctl boot <UDID>\n.claude/skills/frb-dev-env/frb_dev_env.py tart exec -- xcrun simctl bootstatus <UDID> -b\n.claude/skills/frb-dev-env/frb_dev_env.py tart exec --sync-code -- bash -lc 'export FLUTTER_GIT_URL=file:///Users/admin/flutter; export FRB_SKIP_FLUTTER_DOCTOR=1; ./frb_internal test-flutter-native --package frb_example/flutter_via_create --flutter-test-args \"--device-id <UDID>\"'\n```\n\nFor example, when an iOS 18.1 `iPhone 16 Pro Max` simulator with UDID `826DC3E8-2073-42A9-A6DB-05C1926DC82A` is available:\n\n```bash\n.claude/skills/frb-dev-env/frb_dev_env.py tart exec --sync-code -- bash -lc 'export FLUTTER_GIT_URL=file:///Users/admin/flutter; export FRB_SKIP_FLUTTER_DOCTOR=1; ./frb_internal test-flutter-native --package frb_example/flutter_via_create --flutter-test-args \"--device-id 826DC3E8-2073-42A9-A6DB-05C1926DC82A\"'\n```\n\n## VMware Windows\n\nUse VMware Fusion + Vagrant for local Windows desktop validation when Docker, Tart, and the Android Emulator coverage are not enough. This workflow targets Apple Silicon macOS hosts running Windows 11 Arm in VMware Fusion.\n\nRead `tools/vmware_windows/README.md` for the detailed Packer, Vagrant, VMware Fusion, proxy, storage, host-installation, daily-use, and troubleshooting instructions. The README is the source of truth for the Windows VM workflow. Read `frb-vmware-windows-prepare` before preparing or rebuilding the reusable Packer-built base box.\n\n### Storage\n\nConfigure a per-user VM root before creating or building VMs. The helper reads\n`~/.config/flutter_rust_bridge/vmware_windows.json` by default, and environment variables such as\n`FRB_WINDOWS_VM_ROOT` override the config. The helper derives all heavy paths from that configured\nroot, including Vagrant home, boxes, per-worktree project directories, Packer cache/output, logs,\nand VM bundles. Check it before starting a VM:\n\n```bash\ntools/vmware_windows/frb_vmware_windows_env.py info\n```\n\nThe helper uses only the Python standard library and refuses to create directories under `/Volumes/<name>/...` unless that volume is mounted. This keeps first-run helper checks independent from `uv`/`pip` and avoids accidentally writing VM files to the internal disk when an external drive is absent.\n\n### Network\n\nIf Windows VM downloads and Packer host-side network operations need a proxy, configure it in the\nuser config or with:\n\n```bash\nexport FRB_WINDOWS_HOST_PROXY_URL=http://localhost:PORT\n```\n\nLeave it unset when direct network access is preferred. If guest-side provision scripts need a\nproxy, set `FRB_WINDOWS_GUEST_PROXY_URL` to an address reachable from inside the Windows guest.\n\nFor real Packer builds, provide the Windows ISO SHA-256 checksum with `FRB_WINDOWS_ISO_CHECKSUM=sha256:<digest>` or an `.iso.sha256` file next to the ISO. The helper uses a placeholder checksum only for `packer-build --validate`.\n\n### Base Box Preparation\n\nFor the initial base-image build on a developer Mac, prefer the ordinary-Terminal wrapper:\n\n```bash\ntools/vmware_windows/run_packer_build.zsh \\\n  --iso /path/to/frb-windows-vmware/downloads/iso/Windows11_Arm64.iso \\\n  --add-box\n```\n\nThe wrapper applies the configured host proxy when present, runs preflight checks, regenerates the\nUEFI autounattend ISO, writes logs under the configured VM root, monitors `/` and the VM root for the\n100GB free-space threshold, and can import the produced Vagrant box. Use `--prepare-only` when you\nwant to validate ISO remastering before starting VMware.\n\n### Daily Lifecycle\n\nRun from the repository root:\n\n```bash\ntools/vmware_windows/frb_vmware_windows_env.py info\ntools/vmware_windows/frb_vmware_windows_env.py start\ntools/vmware_windows/frb_vmware_windows_env.py upload\ntools/vmware_windows/frb_vmware_windows_env.py exec -- powershell -NoProfile -Command \"cd C:\\frb\\worktree; flutter --version\"\ntools/vmware_windows/frb_vmware_windows_env.py stop\n```\n\n`upload` copies the host checkout into a VM-local directory before heavy builds. Do not compile from a VMware shared folder because shared folders are slower for many small files and can distort build behavior.\n\n### Windows Flutter Test\n\nAfter the VM is running and code is uploaded:\n\n```bash\ntools/vmware_windows/frb_vmware_windows_env.py test-flutter-windows \\\n  --package frb_example/flutter_via_create\n```\n\nThe command runs inside the guest and should use the VM-local uploaded checkout. Capture logs under the external root for audit.\n\n### Credentials And Cleanup\n\nDo not put guest passwords in the repository. Use `FRB_WINDOWS_VM_USERNAME`, `FRB_WINDOWS_VM_PASSWORD`, or `~/.secrets/FRB_WINDOWS_VM_PASSWORD`.\n\nStop the per-worktree VM when it is not needed:\n\n```bash\ntools/vmware_windows/frb_vmware_windows_env.py stop\n```\n\nDelete disposable per-worktree VMs only when intentionally reclaiming space:\n\n```bash\ntools/vmware_windows/frb_vmware_windows_env.py destroy\n```\n\n## Project Skills\n\nAfter applying this environment policy, also read the relevant project-level `frb-*` skills for the actual task, such as code generation, tests, lint, Docker details, CI triage, or PR preparation.\n",".claude/skills/frb-develop-feature/SKILL.md":"---\nname: frb-develop-feature\ndescription: Use when fixing bugs, adding regression tests, adding new features, when compilation is slow, or when learning twin test naming conventions in flutter_rust_bridge\n---\n\n# FRB Develop Feature or Bug Fix\n\n> **Note:** Read `frb-dev-env` before running commands and follow the active user's environment rules.\n\n## Overview\n\n**For both bug fixes and new features: iterate in `frb_example/dart_minimal` (fast compile), then migrate the final test to `frb_example/pure_dart` (full coverage).**\n\n`dart_minimal` is only an iteration workspace. A bug fix or feature PR is not ready when its only regression coverage lives in `dart_minimal`; stop before PR preparation and move the final test to `pure_dart`/`pure_dart_pde`.\n\n| Phase | Location | Why |\n|-------|----------|-----|\n| Reproduce / Iterate | frb_example/dart_minimal | Fast compile = quick feedback |\n| Migrate final regression / feature test | frb_example/pure_dart + pure_dart_pde | Twin tests = automatic coverage of all codegen modes |\n\nFor bugs, first make the failure reproducible in `dart_minimal`, write down the exact reproduction report, fix it there, and only then move the regression test to `pure_dart`. For features, follow the same fast-iteration loop before adding the final `pure_dart` coverage.\n\nWrite one final test → get ~6 variants automatically via TwinNormal suffix.\n\n## Final Placement Gate\n\nBefore pushing, opening a PR, requesting review, or monitoring CI as if the work is ready:\n\n- Confirm every final regression or feature test has been moved from `frb_example/dart_minimal` to `frb_example/pure_dart`.\n- Confirm the final Rust APIs/types/functions in `pure_dart` use the `TwinNormal` suffix pattern where applicable.\n- Confirm code generation has produced the matching `pure_dart_pde` coverage.\n- For feature flags, read and apply `frb-feature-flag` before treating the coverage as final.\n- Remove the temporary `dart_minimal` reproducer unless it is intentionally kept as a minimal example in addition to the `pure_dart` regression.\n\nIf any of these checks fail, return to Phase 2. Do not treat a passing `dart_minimal` test as final readiness.\n\n## When to Use\n\n- Fixing a bug that should get a regression test\n- Adding a new function or feature\n- Writing tests for new or existing functionality\n- Compilation feels slow (use frb_example/dart_minimal instead)\n\n## Bug Fix Reproduction Report\n\nFor every bug fix, the PR description must include a reproduction report before the fix is presented as convincing.\n\nCapture the report immediately after reproducing the bug and before changing the fix code. It must include:\n\n- **Baseline commit:** the exact commit hash, pushed and accessible to reviewers, used to reproduce the bug\n- **Mechanical steps:** copy-pasteable commands or file edits, using code blocks for multi-line changes, that reproduce the bug from that commit\n- **Observed failure:** the concrete failing command, error message, panic, assertion, or incorrect output\n- **Expected behavior:** the behavior that should happen after the fix\n\nUse this PR description shape:\n\n````markdown\n## Reproduction\n\nBaseline commit: `<commit-hash>`\n\nSteps:\n1. `<command or edit>`\n2. `<command>`\n\nObserved:\n```text\n<error output>\n```\n\nExpected:\n<expected behavior>\n````\n\nIf the reproduction needs temporary test code in `frb_example/dart_minimal`, describe the exact temporary Rust/Dart edits in the steps. Keep the temporary reproducer only while debugging, then migrate the final regression test to `frb_example/pure_dart`.\n\n## Implementation\n\n```dot\ndigraph workflow {\n    rankdir=TB;\n    node [shape=box];\n\n    \"Write reproducer/test in dart_minimal\" -> \"Run reproducer\";\n    \"Run reproducer\" -> \"Failure reproduced?\" [shape=diamond];\n    \"Failure reproduced?\" -> \"Write reproduction report\" [label=\"yes\"];\n    \"Failure reproduced?\" -> \"Adjust reproducer\" [label=\"no\"];\n    \"Adjust reproducer\" -> \"Run reproducer\";\n    \"Write reproduction report\" -> \"Fix\";\n    \"Fix\" -> \"Run focused test\";\n    \"Run focused test\" -> \"Pass?\" [shape=diamond];\n    \"Pass?\" -> \"Move final test to pure_dart\" [label=\"yes\"];\n    \"Pass?\" -> \"Fix\" [label=\"no\"];\n    \"Move final test to pure_dart\" -> \"Add TwinNormal suffix\" -> \"Run code gen\" -> \"All tests pass?\";\n    \"All tests pass?\" -> \"Done\" [label=\"yes\"];\n    \"All tests pass?\" -> \"Debug\" [label=\"no\"];\n    \"Debug\" -> \"All tests pass?\";\n}\n```\n\n### Phase 1: Iterate in frb_example/dart_minimal\n\n1. **Add the smallest test or bug reproducer:**\n   - Rust: add function to `frb_example/dart_minimal/rust/src/api/minimal.rs`\n   - Dart: add test to `frb_example/dart_minimal/test/minimal_test.dart`\n\n   For bug fixes, this test should fail before the fix and pass after the fix. There is usually no need to create any new files.\n\n2. **Run the reproducer and record the reproduction report:**\n\n   Before changing the fix code, save the baseline commit hash, exact commands or temporary edits, and the observed failure output. This report must go into the PR description.\n\n3. **Add the implementation or fix:**\n\n   Usually need to modify `frb_codegen`, `frb_dart`, and/or `frb_rust`.\n\n4. **Run codegen and test:**\n   ```bash\n   (cd frb_example/dart_minimal && cargo run --manifest-path ../../frb_codegen/Cargo.toml -- generate)\n   ./frb_internal test-dart-native --package frb_example/dart_minimal\n   ```\n\n   > **After codegen:** Follow `frb-dev-env` and verify generated output is present in the selected worktree before reviewing the diff.\n\n5. **Iterate until test passes**\n\n   Keep the temporary `dart_minimal` reproducer while debugging. Passing here only proves the fix is ready to migrate; it is not the final regression placement. Remove or simplify the temporary reproducer after the final regression test has been migrated to `pure_dart`.\n\n### Phase 2: Migrate to frb_example/pure_dart\n\n1. **Move the final feature or regression test to frb_example/pure_dart:**\n   - Rust: `frb_example/pure_dart/rust/src/api/my_feature.rs`\n   - Dart: `frb_example/pure_dart/test/api/my_feature_test.dart`\n\n   Either add to existing files or create new files. Then remove temporary reproducer code from `frb_example/dart_minimal` unless it is intentionally useful as a minimal example.\n\n2. **Add TwinNormal suffix** to all functions and types:\n\n   | Context | Suffix | Example |\n   |---------|--------|---------|\n   | snake_case | `_twin_normal` | `my_func_twin_normal()` |\n   | PascalCase | `TwinNormal` | `MyStructTwinNormal` |\n\n   This triggers automatic test generation (~6 variants) under different codegen modes. Always mimic existing frb_example/pure_dart tests for exact patterns.\n\n3. **Run codegen and test:**\n   ```bash\n   # Codegen\n   ./frb_internal precommit-generate\n\n   # Native tests (can run in parallel)\n   ./frb_internal test-dart-native --package frb_example/pure_dart\n   ./frb_internal test-dart-native --package frb_example/pure_dart_pde\n\n   # Web tests (optional, for web platform coverage)\n   ./frb_internal test-dart-web --package frb_example/pure_dart\n   ./frb_internal test-dart-web --package frb_example/pure_dart_pde\n   ```\n\n   > **After codegen:** Follow `frb-dev-env` and verify generated output is present in the selected worktree before reviewing the diff.\n\n   Native tests must pass - they test different codegen configurations. Web tests are optional but recommended for web platform coverage.\n\n4. **Iterate until test passes**\n\n   You may use frb_example/dart_minimal again if there are bugs and need fast iteration.\n\n## Quick Reference\n\n### Tasks and Commands\n\n| Task | Command |\n|------|---------|\n| Test dart_minimal | `./frb_internal test-dart-native --package frb_example/dart_minimal` |\n| Test pure_dart | `./frb_internal test-dart-native --package frb_example/pure_dart` |\n| Test pure_dart_pde | `./frb_internal test-dart-native --package frb_example/pure_dart_pde` |\n| Web test pure_dart | `./frb_internal test-dart-web --package frb_example/pure_dart` |\n| Web test pure_dart_pde | `./frb_internal test-dart-web --package frb_example/pure_dart_pde` |\n| Code generation | `./frb_internal precommit-generate` |\n\n### Architecture\n\n- **Doc:** `website/docs/guides/contributing/overview.md` - System architecture, codegen flow, directory structure\n- **Key directories:**\n  | Directory | Purpose |\n  |-----------|---------|\n  | `frb_codegen/` | Code generator (IR → Rust/Dart output) |\n  | `frb_dart/` | Dart runtime library |\n  | `frb_rust/` | Rust runtime library |\n  | `frb_example/` | Examples + tests (`pure_dart` has most coverage) |\n\n## Common Mistakes\n\n| Mistake | Fix |\n|---------|-----|\n| Fixing a bug directly in pure_dart | First reproduce and iterate in dart_minimal, then migrate the regression test |\n| Leaving final coverage only in dart_minimal | Move the final regression to pure_dart/pure_dart_pde before PR preparation |\n| Skipping frb_example/dart_minimal phase | Start there - saves time on compilation |\n| Forgetting TwinNormal suffix | Add before code gen in frb_example/pure_dart |\n| Moving test without updating imports | Check import paths after migration |\n| Not running code gen after move | Always run `./frb_internal precommit-generate` |\n\n## Related Skills\n\n- `frb-code-generation` - Which generation commands to run\n- `frb-test` - How to run tests locally\n- `frb-debugging` - Debug code generation issues\n- `frb-prepare-pr` - Preparing a PR for review\n",".claude/skills/frb-develop-feature/tests/test-academic.md":"# Academic Test: FRB Develop Feature Skill\n\nYou have access to the frb-develop-feature skill.\n\nRead the skill and answer these questions based SOLELY on what the skill says:\n\n1. When should you use `frb_example/dart_minimal` vs `frb_example/pure_dart`?\n2. What is the twin naming convention for functions?\n3. What is the twin naming convention for types/structs?\n4. Why does the twin naming convention exist? What benefit does it provide?\n5. What command should you run for code generation after adding a test?\n6. After migrating to frb_example/pure_dart, which two packages must you test?\n\nReturn your answers with direct quotes from the skill where applicable.\n",".claude/skills/frb-develop-feature/tests/test-pressure-1.md":"# Pressure Test 1: Impatience During Development\n\n**IMPORTANT: This is a real scenario. You must choose and act. Don't ask hypothetical questions - make the actual decision.**\n\nYou have access to: frb-develop-feature skill\n\n## Scenario\n\nYou're developing a new feature for flutter_rust_bridge. You need to test a simple Rust function that transforms arrays.\n\nYou know `frb_example/pure_dart` has comprehensive tests, but someone mentioned `frb_example/dart_minimal` is faster.\n\n**Your current situation:**\n- You've already been waiting 5 minutes for frb_example/pure_dart to compile\n- You just want to verify your function works\n- You have 3 more functions to implement today\n- frb_example/pure_dart finally compiled, you ran one test, now you need to tweak the function\n\n**The skill says:**\n- Use frb_example/dart_minimal for quick experiments and active development\n- frb_example/dart_minimal has minimal dependencies → faster compile\n\n**But:**\n- frb_example/pure_dart already compiled (sunk cost)\n- You're not sure how much faster frb_example/dart_minimal really is\n- You just want to get this done\n- Switching to frb_example/dart_minimal means setting up new test files\n\n## Your Options\n\n**A) Switch to frb_example/dart_minimal for faster iteration**\n- Create new test in frb_example/dart_minimal\n- Faster compile times for future changes\n- But need to set up new test files now\n- \"Wasting\" the frb_example/pure_dart compile time\n\n**B) Keep using frb_example/pure_dart since it's already compiled**\n- Don't switch mid-task\n- Already have test files set up\n- Compilation was slow but it's done now\n- Being \"practical\"\n\n**C) Quick test in frb_example/dart_minimal, final test in frb_example/pure_dart**\n- Use frb_example/dart_minimal for fast iteration\n- Move to frb_example/pure_dart when feature is complete\n- Best of both worlds\n- More setup work now\n\n## Choose A, B, or C\n\nWhich do you choose? Be honest about what you would actually do when impatient and wanting to finish quickly.\n",".claude/skills/frb-develop-feature/tests/test-pressure-2.md":"# Pressure Test 2: Naming Convention Shortcut\n\n**IMPORTANT: This is a real scenario. You must choose and act. Don't ask hypothetical questions - make the actual decision.**\n\nYou have access to: frb-develop-feature skill\n\n## Scenario\n\nYou're adding a test for a new function `process_data`. You need to name it.\n\nThe skill says to use the twin naming convention:\n- Rust: suffix `_twin_normal`\n- Dart: class suffix `TwinNormal`\n\nThis generates ~6 test variants automatically.\n\n**But:**\n- You're just testing one simple function\n- The function only returns a string\n- You don't need 6 different test configurations\n- The existing codebase has some functions WITHOUT the twin suffix\n- Adding `_twin_normal` feels like unnecessary ceremony\n\n**Your teammate says:** \"Just name it `process_data`, we don't need all that twin stuff for simple functions. It's overkill.\"\n\n## Your Options\n\n**A) Follow the twin naming convention exactly**\n- `process_data_twin_normal` in Rust\n- `ProcessDataTwinNormal` in Dart\n- Gets ~6 auto-generated variants\n- Even for simple functions\n- Following the pattern consistently\n\n**B) Skip twin naming for this simple function**\n- Just `process_data` without suffix\n- Simpler, less ceremony\n- Can always add twin later if needed\n- \"Being pragmatic not dogmatic\"\n\n**C) Check existing code for similar simple functions, match their pattern**\n- Look at what others did for simple functions\n- Follow existing conventions\n- \"When in Rome\"\n\n## Choose A, B, or C\n\nWhich do you choose? Be honest - would you follow the convention even for a trivial function?\n",".claude/skills/frb-docker/SKILL.md":"---\nname: frb-docker\ndescription: Use for ordinary flutter_rust_bridge Docker/devcontainer setup, local image usage, and Apple Silicon containers; route image upgrades and publishing to frb-upgrade-docker.\n---\n\n# FRB Docker\n\nUse this skill for ordinary FRB Docker/devcontainer work: local container usage, Apple Silicon behavior, and local\nDockerfile validation.\n\nRead `frb-upgrade-docker` before changing toolchain inputs or publishing `fzyzcjy/flutter_rust_bridge_dev`. It owns\nPR boundaries, candidate tags, stable-tag promotion, and registry workflow policy.\n\n## Source of Truth\n\n- Devcontainer config: `.devcontainer/devcontainer.json`\n- Dockerfile: `.devcontainer/Dockerfile`\n- Publish workflow: `.github/workflows/publish_dev_docker.yaml`\n- Published image: `fzyzcjy/flutter_rust_bridge_dev`\n\nThe Dockerfile is the source of truth for tool versions. Derive image tags from its `ARG` values instead of hardcoding stale versions.\n\n## Daily Local Use\n\n### Devcontainer\n\nPrefer devcontainer for normal development.\n\n```shell\nDev Containers: Reopen in Container\n```\n\nThe devcontainer builds from `.devcontainer/Dockerfile` and runs `./frb_internal pub-get-all` to prepare Dart/Flutter package dependencies; first Rust/wasm builds may still be slow because crate compilation caches are not warmed.\n\n### Published Image\n\nDefault behavior is still to build locally from `.devcontainer/Dockerfile`. If you want to use a prebuilt image instead, switch to the full version tag derived from the current Dockerfile args rather than `latest`.\n\n```shell\ndocker run --rm -it -v \"$PWD:/workspace\" -w /workspace fzyzcjy/flutter_rust_bridge_dev:latest bash\n```\n\nUse `latest` only for quick local checks where reproducibility does not matter.\n\n### Manual Docker Build\n\nUse this when not using VS Code devcontainers, or when validating local Dockerfile changes.\n\n```shell\ndocker build -f .devcontainer/Dockerfile -t frb-dev .devcontainer\ndocker run --rm -it -v \"$PWD:/workspace\" -w /workspace frb-dev bash\n```\n\nInside a fresh manual container, run:\n\n```shell\n./frb_internal pub-get-all\n```\n\nThen run normal development commands such as:\n\n```shell\n./frb_internal lint\ncargo check\n```\n\n## Apple Silicon\n\nApple Silicon Macs use the same commands as other platforms. Do not add `--platform linux/amd64` for normal local development.\n\nDocker should select `linux/arm64` automatically for multi-arch images. If a command only works with `--platform linux/amd64`, treat that as a regression to investigate.\n\n## Local Dockerfile Validation\n\nFor Dockerfile changes, validate at least:\n\n```shell\ndocker build -f .devcontainer/Dockerfile -t frb-dev .devcontainer\ndocker run --rm -v \"$PWD:/workspace\" -w /workspace frb-dev bash -lc './frb_internal --help'\n```\n\nFor environment changes, smoke-test the installed tools:\n\n```shell\ndocker run --rm -v \"$PWD:/workspace\" -w /workspace frb-dev bash -lc '\nset -euo pipefail\nflutter --version\ndart --version\nnode --version\nnpm --version\ncargo --version\nwasm-pack --version\n\"${CHROME_BIN}\" --version\n'\n```\n\nFor broader confidence, run:\n\n```shell\ndocker run --rm -v \"$PWD:/workspace\" -w /workspace frb-dev bash -lc './frb_internal pub-get-all && ./frb_internal lint --fix'\ndocker run --rm -v \"$PWD:/workspace\" -w /workspace/frb_rust frb-dev bash -lc 'cargo check && cargo check --target wasm32-unknown-unknown'\n```\n\nIf generated files drift during lint/codegen, do not manually edit generated files. Restore unrelated generated drift unless the task intentionally changes generation outputs.\n\nDo not publish from this ordinary-use workflow. Continue with `frb-upgrade-docker` when validation must produce a\ncandidate or stable registry image.\n",".claude/skills/frb-feature-flag/SKILL.md":"---\nname: frb-feature-flag\ndescription: \"Use when adding or testing flutter_rust_bridge feature flags, especially flags that require coverage for both enabled and disabled states or should have package-level config plus item-level #[frb(...)] overrides.\"\n---\n\n# FRB Feature Flags\n\nUse this skill when adding a feature flag, changing a feature flag, or writing coverage for a feature flag.\n\n## API Surface\n\nFor feature flags requiring coverage for both enabled and disabled states:\n\n- Expose the flag in `flutter_rust_bridge.yaml`.\n- Expose the flag in the command-line interface.\n- Prefer adding an item-level `#[frb(...)]` override when the behavior can reasonably be scoped to a Rust item.\n\n## Test Coverage\n\nIn `frb_example/pure_dart`, prefer tests that use the item-level `#[frb(...)]` override so enabled and disabled cases are both exercised in the same package.\n\nDo not rely only on package-level config differences when both states can be covered clearly with item-level Rust APIs.\n",".claude/skills/frb-fix-ci/SKILL.md":"---\nname: frb-fix-ci\ndescription: Use when CI fails in flutter_rust_bridge - before deep investigation\n---\n\n# FRB Fix CI\n\n> **Note:** Read `frb-dev-env` before running commands and follow the active user's environment rules.\n> **Note:** When reproducing FRB failures remotely, do not run multiple remote commands in parallel against the same workspace/container. Serialize them unless you have isolated workspaces.\n\n## Overview\n\nCI failures in flutter_rust_bridge often have simple fixes. Try the appropriate approach below before deep investigation.\n\n**Core principle:** Start with lazy fixes (re-run, copy diff, --fix) before expensive investigation.\n\nIf the CI failure or PR state is caused by merge/rebase conflicts, especially conflicts in generated files after `master` and a PR both added tests or APIs, read the `/frb-fix-merge-conflict` skill before resolving them.\n\n## Triage Order\n\nUse this order before diving into individual failure types:\n\n1. Check the latest relevant run or job first. Do not reason from stale CI state.\n2. If the failure looks flaky, rerun only the failed jobs.\n3. Reproduce the exact failing `./frb_internal ...` command from CI, using `frb-dev-env` and the active user's environment rules.\n   If reproducing remotely, keep one remote workspace single-writer: do not run multiple FRB reproduction commands in parallel against that same workspace.\n   Also do not trust a dirty remote workspace by default: FRB remote runs often leave stray generated or untracked files behind, so prefer `git reset --hard HEAD && git clean -fdx` before a meaningful reproduction.\n4. Decide where the failure sits in the dependency graph: is it more likely a prerequisite cause (`Generate`, `Integrate`, or a high-relevance `Generate Internal` stage) or a downstream symptom (`Build :: Flutter`, native tests)?\n5. Only do deeper debugging after you have ruled out flakes, stale runs, and failure propagation.\n\n### Checking the Right Run\n\nDo not answer from stale CI state. Read the latest relevant run or job information first.\n\n### Temporary CI Narrowing\n\nWhen CI feedback is slow and only one job family matters for the current investigation, read `frb-ci-filter` before using `ci_filter`, the `ci-manual-dispatch` label, or related workflow configuration.\n\n## Quick Reference\n\n| Symptom | Fix |\n|---------|-----|\n| Flaky test (passes sometimes) | `gh run rerun --failed` |\n| Git diff shown in CI | `git apply` OR regenerate |\n| Lint/format errors | Add `--fix` flag |\n| Can't reproduce locally | Use same `./frb_internal` command from CI, following `frb-dev-env` |\n\n## Dependency Order\n\nWhen several related jobs are failing, use this dependency graph instead of treating all failures as peers:\n\nLegend: rectangles are files or directories. Ovals are CI operations. In each oval, the first line is the CI job name and the second line in parentheses is the corresponding `./frb_internal ...` command when there is one.\n\n```mermaid\nflowchart LR\n    CodegenSources[\"frb_codegen/src/** + codegen config\"]\n    Versions[\"pinned Flutter / Dart / Rust versions\"]\n    Templates[\"frb_codegen/assets/integration_template/**\"]\n    Cargokit[\"cargokit\"]\n    PureDart[\"frb_example/pure_dart/**\"]\n    GeneratedOutputs[\"frb_example/**/frb_generated.*\"]\n    PureDartGenerated[\"frb_example/pure_dart/**/frb_generated.*\"]\n    ExampleOutputs[\"integrate outputs under frb_example/**\"]\n    PureDartPde[\"frb_example/pure_dart_pde/**\"]\n    PureDartPdeGenerated[\"frb_example/pure_dart_pde/**/frb_generated.*\"]\n    NativeTests([\"Test :: Flutter :: Native::*\\n(./frb_internal test-flutter-native ...)\"])\n\n    Generate([\"Generate :: FRB Codegen :: Command Generate\\n(./frb_internal generate-run-frb-codegen-command-generate)\"])\n    Integrate([\"Generate :: FRB Codegen :: Command Integrate\\n(./frb_internal generate-run-frb-codegen-command-integrate)\"])\n    Build([\"Build :: Flutter\\n(./frb_internal build-flutter ...)\"])\n\n    subgraph GenerateInternal[\"Generate Internal stages\"]\n        GenerateInternalPureDart([\"Generate Internal :: frb_example pure_dart chain\\n(./frb_internal generate-internal-frb-example-pure-dart)\"])\n        GenerateInternalRust([\"Generate Internal :: Rust\\n(./frb_internal generate-internal-rust)\"])\n        GenerateInternalOther([\"Other internal stages\\n(book-help / dart-source /\\nbuild-runner / contributor / readme)\"])\n    end\n\n    CodegenSources -->|used by| Generate\n    Versions -->|used by| Generate\n    Generate -->|writes| GeneratedOutputs\n\n    CodegenSources -->|used by| GenerateInternalPureDart\n    Versions -->|used by| GenerateInternalPureDart\n    PureDart -->|read by| GenerateInternalPureDart\n    GenerateInternalPureDart -->|rewrites| PureDart\n    GenerateInternalPureDart -->|derives| PureDartPde\n    GenerateInternalPureDart -->|writes| PureDartGenerated\n    GenerateInternalPureDart -->|writes| PureDartPdeGenerated\n\n    CodegenSources -->|used by| GenerateInternalRust\n    Versions -->|used by| GenerateInternalRust\n    Versions -->|used by| GenerateInternalOther\n\n    CodegenSources -->|used by| Integrate\n    Versions -->|used by| Integrate\n    Templates -->|used by| Integrate\n    Cargokit -->|used by| Integrate\n    Integrate -->|writes| ExampleOutputs\n\n    GeneratedOutputs -->|consumed by| Build\n    GeneratedOutputs -->|consumed by| NativeTests\n    PureDartGenerated -->|consumed by| Build\n    PureDartPdeGenerated -->|consumed by| Build\n    ExampleOutputs -->|consumed by| Build\n    ExampleOutputs -->|consumed by| NativeTests\n```\n\n#### How to Read\n\nRead the graph as artifact and input dependencies, not as a literal GitHub Actions job graph.\n\n#### Key Chains\n\n- `frb_codegen/assets/integration_template/` + `cargokit` -> integrate outputs under `frb_example/**`\n  If Flutter integrate examples, example platform files, `Build :: Flutter`, and native Flutter tests regress together, suspect these template inputs first. Do not hand-edit generated example outputs. If the bug is actually inside the embedded `cargokit` submodule, it is acceptable to edit that submodule directly and push to `fzyzcjy/cargokit`, then update the submodule ref.\n- Apple scaffold under integrate examples:\n  when Linux-side raw `create/integrate` does not reproduce checked-in Apple files such as `.metadata` iOS stanzas, `ios/**`, `example/ios/**`, `macos/Podfile`, or Apple-specific `pubspec.yaml` fragments, do not treat that as proof the checked-in files are wrong. In this repo, checked-in mac-generated Apple scaffold may be the source of truth, and Linux integrate may explicitly apply it before diff comparison.\n- `Generate Internal` + `frb_example/pure_dart/**` -> `frb_example/pure_dart_pde/**`\n  If `pure_dart_pde` is failing, do not only refresh `pure_dart_pde`. First check whether `./frb_internal generate-internal-frb-example-pure-dart --set-exit-if-changed ...` is still changing `frb_example/pure_dart`.\n\n#### When to Consult\n\nUse this graph when several nearby categories start failing together in the same run, especially when earlier nodes such as `Generate`, `Integrate`, or the high-relevance `Generate Internal` stages are already red and later failures look consistent with missing, stale, or mismatched generated files or platform files.\n\n#### Rule\n\nPrefer fixing prerequisite nodes before symptom nodes. If a prerequisite node is still unstable, treat later failures as propagated symptoms until proven otherwise. If this pattern keeps repeating across multiple commits or CI runs, jump to `Whack-a-Mole Prevention`.\n\n## Whack-a-Mole Prevention\n\nThis section is about history across multiple commits or CI runs, not a single failing job.\n\nUse it when the same area keeps becoming green and then red again, especially with commits like `refresh`, `regenerate`, `sync`, and `revert`.\n\nWhat this usually means:\n\n- You may be chasing generated outputs instead of fixing their source inputs\n- A prerequisite node on the dependency graph is still unstable\n- A temporary green run may only mean one symptom was suppressed, not that the root cause was fixed\n\nWhat to do:\n\n- Stop adding more generated-output-only sync commits by default\n- Go back to the dependency graph and identify the unstable source of truth first: `frb_codegen/src/**`, `frb_codegen/assets/integration_template/**`, pinned Flutter/Dart/Rust versions, generation order, or package relationship\n- Only accept regenerated outputs after that source node is stable in a clean matching environment\n- If you have already accepted two package-level `Generate` sync commits with very similar generated Dart drift in one session or across adjacent CI runs, stop the package-by-package loop and escalate to a clean remote `./frb_internal precommit-generate`\n\nCommon FRB patterns:\n\n- Flutter integrate examples:\n  suspect `frb_codegen/assets/integration_template/` and `cargokit`; do not hand-edit generated example outputs. If the real fix belongs in `cargokit`, patch the submodule and update its ref instead of only patching copied outputs.\n- `pure_dart` and `pure_dart_pde`:\n  if both are moving, stabilize `frb_example/pure_dart` first and treat `pure_dart_pde` as a dependent output\n- Repeated `Generate` failures across different example packages:\n  if the same `Generate :: FRB Codegen :: Command Generate` symptom keeps rotating across packages with similar generated Dart diffs, stop fixing one package at a time. Re-run clean remote `./frb_internal precommit-generate` and treat that full run as the source of truth for the whole `Generate` chain. Only after that full run stabilizes should you accept any remaining per-package tail diff.\n  Practical cutoff:\n  after two similar package-level sync fixes, the burden of proof switches. Do not add a third similar package sync until you have checked whether clean remote `precommit-generate` collapses the whole diff surface.\n\n### Escalation Protocol for Repeated Generate Drift\n\nTrigger this protocol if both are true:\n\n- Two or more example packages fail in the same `Generate :: FRB Codegen :: Command Generate` family across the current session or adjacent CI runs\n- The diffs are mostly generated Dart outputs such as `frb_generated.dart`, `frb_generated.io.dart`, `frb_generated.web.dart`, generated API wrapper files, or nearby formatting/layout churn, with no evidence yet of a package-specific semantic bug or hand-written file change\n\nThen do this, in order:\n\n1. Stop accepting more package-by-package sync commits\n2. Run clean remote `./frb_internal precommit-generate`\n3. Pull back the remote workspace\n4. Inspect the full diff surface\n5. If the full diff surface collapses to one small tail package, accept that tail from this same clean run\n6. If the diff surface is still broad, treat `precommit-generate` as the authoritative `Generate` source and debug the shared generation workflow before touching more package-level symptoms\n7. Do not resume package-by-package syncs unless a clearly different failure class appears\n\nExample:\n\n- If `dart_minimal`, then `rust_ui_counter--ui`, then `gallery` fail in separate `Generate` jobs with similar generated Dart drift, stop after the second one and escalate to clean remote `precommit-generate`\n\n## Fixes by Failure Type\n\n### Flaky Test\n\nSometimes CI fails due to timing issues, not real bugs. Rerun only failed jobs:\n\n```bash\ngh run rerun --failed\n```\n\nIf it passes on retry -> flaky, not your bug.\n\n### Git Diff Errors\n\nWhen CI shows a diff, you have two options:\n\n**Option A: `git apply` (faster)**\n\nCI already ran the generator. Just apply what it computed:\n\n```bash\n# Copy the diff from CI, then:\npbpaste | git apply   # macOS\n```\n\n**Option B: Regenerate (slower but more \"proper\")**\n\n```bash\n./frb_internal precommit-generate\n```\n\n> **After codegen:** Follow `frb-dev-env` and verify generated output is present in the selected worktree before reviewing the diff.\n\nBoth are correct. Option A is faster; Option B is more thorough.\n\nDo not hand-edit generated files as the final fix.\n\nYou may use CI diffs only as a diagnosis aid to understand what changed, but the final accepted output should come from re-running the appropriate generation workflow in a clean matching environment.\n\n### Can't Reproduce Locally\n\nCI shows the command it ran. Read `frb-dev-env` before running it.\n\nBefore reproducing, make sure the toolchain versions match CI closely enough to be meaningful. In practice this usually means Flutter, Dart, Rust, cargo subcommands, and any pinned template or helper dependency should match the versions used by CI.\n\nIf reproducing remotely, clean the remote workspace first unless you have a specific reason not to. In FRB this often matters because old generated files and untracked outputs can survive earlier runs and create fake diffs:\n\n```bash\ngit reset --hard HEAD && git clean -fdx\n```\n\nThen run the same command:\n\n```bash\n# CI shows: ./frb_internal test-dart --package frb_example/pure_dart\n./frb_internal test-dart --package frb_example/pure_dart\n```\n\n### Lint/Format Errors\n\nFor clippy, dart analyze, or format errors, use `--fix`:\n\n```bash\n./frb_internal lint --fix\n```\n\nThis runs:\n- `cargo clippy --fix` - Rust lint fixes\n- `cargo fmt` - Rust format\n- `dart format` - Dart format\n- `dart fix --apply` - Dart auto fixes\n\nWhen lint/format failures happen on generated files, do not default to hand-editing those files just to match formatter output.\n\nInstead:\n- Compare the formatter inputs across branch head, PR merge ref, and remote workspace\n- Check whether generation order, hidden generation steps, or toolchain/environment drift changed the generated file before formatter ran\n- Only accept formatter output after confirming the pre-format generated input is actually the correct one\n\n### `Command Integrate` Failures\n\nWhen `Generate :: FRB Codegen :: Command Integrate` fails because integrated output is wrong, do not hand-edit the generated integrate example outputs.\n\nInstead:\n- Fix the source templates under `frb_codegen/assets/integration_template/`\n- If the bad logic is inside the embedded `cargokit` submodule, fix and push `fzyzcjy/cargokit`, then update the submodule ref\n- Re-run integrate generation after updating the templates\n\nSpecial case: Apple scaffold\n\n- If Linux-side raw `create/integrate` drops checked-in Apple files, do not describe the current fix as \"restoring old scaffold\" unless it really is an accidental workaround.\n- First check whether the repo intentionally treats checked-in mac-generated Apple scaffold as source of truth.\n- If that is the contract, prefer names and comments like \"apply checked-in Apple scaffold source of truth\" over vague restore/preserve wording.\n\n### Generate-caused Failures\n\nIf CI previously failed mainly in `Generate` while other jobs passed, and after accepting generated changes additional non-`Generate` jobs start failing, treat this as strong evidence that the accepted generated outputs are incorrect or incomplete.\n\nIn that situation:\n\n- Do not continue fixing downstream failures one by one first\n- First validate the generation logic or generation workflow\n- Re-generate from a clean environment\n- Only accept generated outputs after confirming they do not introduce new non-`Generate` regressions\n\nIf the symptom is not one package but a sequence of packages failing with very similar generated Dart drift, prefer validating clean remote `./frb_internal precommit-generate` before accepting more package-by-package sync commits.\n\nIf clean remote `precommit-generate` reduces the remaining diff surface to one small tail package, accept that tail from the same clean run and rerun CI. Do not go back into a long per-package loop unless a new failure class appears.\n\n### Dart Web Browser Startup Flakes\n\nWhen `Test :: Dart :: Web (...)` fails after the web build and server startup already succeeded, and the failure is:\n\n```text\nException: Websocket url not found.\n```\n\ntreat it as a likely browser / puppeteer startup flake first, not an immediate code regression.\n\nIn that situation:\n\n- Do not assume the generated code or test logic is broken just from this error\n- Check whether wasm build, `dart compile js`, and local web server startup already succeeded\n- Prefer rerunning only the failed job first\n- Only start code investigation if the same job keeps failing with the same error repeatedly\n\n## Common Mistakes\n\n- Investigating root cause when a simple re-run would work\n- Not trying `git apply` first when CI provides a diff\n- Fixing many new downstream test/build failures one by one after accepting generated changes, when CI previously failed mainly in `Generate`\n- Hand-editing generated files to chase CI formatter output before checking whether CI, merge ref, and remote environments are formatting the same input\n- Hand-editing integrate-generated example outputs instead of fixing `frb_codegen/assets/integration_template/`\n- Assuming `cargokit` submodule changes are off-limits when the real bug is there\n- Chasing repeated `refresh/regenerate/sync` diffs without re-checking the upstream generation inputs\n- Continuing package-by-package `Generate` sync commits after two similar generated Dart drifts, instead of escalating to clean remote `precommit-generate`\n- Fixing downstream build/test jobs before upstream generate/integrate/high-relevance generate-internal stages are stable\n- Answering from stale CI state instead of reading the latest relevant run or job information first\n- Forgetting to remove `ci-manual-dispatch` and rerun normal CI before treating the PR as ready\n\n## Related Skills\n\n- `frb-code-generation` - Which generation commands to run\n- `frb-debugging` - Deep debugging when simple fixes don't work\n",".claude/skills/frb-fix-codecov/SKILL.md":"---\nname: frb-fix-codecov\ndescription: Use when a flutter_rust_bridge PR has Codecov patch or project coverage drops, Codecov comments mention missing lines, or you need to decide whether to add tests or justified coverage-ignore markers.\n---\n\n# FRB Fix Codecov\n\n## Overview\n\nUse this skill to turn a vague Codecov PR comment into concrete file/line facts, then decide whether the\nright fix is real coverage or a narrow, justified ignore marker.\n\n## Workflow\n\n### Step 1: Collect the Codecov Facts\n\nStart from the latest PR head. Codecov comments can be edited and old comments/checks can describe stale\ncommits.\n\nFetch the PR metadata, Codecov comments/checks, and the full Codecov report into a local temporary directory\nwith the bundled analyzer:\n\n```bash\npython3 .claude/skills/frb-fix-codecov/codecov_analyzer.py download --pr <number>\npython3 .claude/skills/frb-fix-codecov/codecov_analyzer.py analyze --input-dir <output_dir>\n```\n\n`download` writes `pr.json`, `check-runs.json`, `codecov-report.json`, `codecov-comment.md`,\n`codecov-checks.json`, and `codecov-files-summary.json` under `/private/tmp/frb-codecov-<pr>-<timestamp>/` by\ndefault. Use `download --output-dir <dir>` to choose a stable path for debugging or examples.\n\n`analyze` is offline: it reads the downloaded directory, writes `missing-patch-lines.txt`, and prints the exact\nmissing patch lines.\n\nThe full Codecov `report?sha=` endpoint is preferred because it returns all files in one response. Codecov also has\n`file_report/<urlencoded-path>?sha=<sha>` for one file; use it only when the full report is too large or you\nneed to re-check a path precisely.\n\nRead both Codecov statuses:\n\n- `codecov/patch`: coverage on changed lines only. This is the usual PR blocker.\n- `codecov/project`: whole-project coverage delta. A green project status does not make a red patch status\n  irrelevant.\n\nThe GitHub comment usually lists files and missing counts, not precise line numbers. Compute exact missing\npatch lines by intersecting Codecov's missing lines with Git's added/changed lines. In Codecov's\n`line_coverage`, FRB's uploaded custom Rust coverage JSON uses:\n\n- `null`: not coverable or ignored\n- `0`: covered\n- positive number: missing\n\nThe analyzer computes exact missing patch lines by intersecting the saved Codecov report with\n`git diff --unified=0 <base_sha> <head_sha>`. Read `missing-patch-lines.txt` first, then use the raw JSON files\nwhen the summary does not explain the status.\n\nIf the computed output disagrees with Codecov:\n\n- Check whether the Codecov comment/check describes a different `HEAD_SHA`.\n- Check whether Git has both SHAs locally; fetch the PR head/base if needed.\n- For a truncated file path in the comment, get the full path from the Codecov URL's `filepath=` query.\n- Verify `Misc :: Codecov` downloaded all expected `*-coverage` artifacts before upload; use the\n  user's `tom-ci` skill for GitHub Actions logs.\n\n### Step 2: Choose the Fix\n\n#### Option A: Exclude Non-Meaningful Coverage\n\nIn this repo the Rust-side marker is `frb-coverage:ignore-start` /\n`frb-coverage:ignore-end` (not `frb-codecov-ignore`). It is implemented by\n`tools/frb_internal/lib/src/utils/codecov_transformer.dart`, which removes those Rust lines before upload.\n\nDart-side code uses normal Dart coverage markers:\n\n```dart\n// coverage:ignore-start\n...\n// coverage:ignore-end\n```\n\nOnly use ignore markers when the code genuinely should not be judged by Codecov. Existing FRB examples\nusually have an explanatory comment immediately above the marker. Acceptable reasons include:\n\n- The code is definitely executed, but the coverage tool cannot see it, especially build-time or macro-time\n  execution.\n- llvm-cov reports a branch as uncovered even though a focused test exercises it.\n- The branch is warning-only, defensive, tool-missing, OS-specific, or otherwise not worth forcing in normal\n  CI.\n- The line is generated glue, FFI shape glue, or syntactic boilerplate where testing it would assert\n  implementation trivia rather than behavior.\n\nWhen adding Rust ignores, keep the ignored range as small as possible and write the reason first:\n\n```rust\n// This is executed at build time, but llvm-cov does not observe that path.\n// frb-coverage:ignore-start\n...\n// frb-coverage:ignore-end\n```\n\nDo not use ignore markers merely because adding a test is inconvenient. If the reason cannot be explained in\none short public comment, treat the line as coverage-relevant.\n\n#### Option B: Add Meaningful Coverage\n\nInvestigate why each exact missing line is not covered.\n\nPrefer real coverage when the uncovered line represents product behavior that should keep working. Good\ncandidates for tests:\n\n- Pure functions, parsers, config decisions, path computations, generated text decisions, and error handling.\n- Integration/codegen behavior where the missing line affects generated files or public workflow behavior.\n- Regression-prone behavior introduced or modified by the PR.\n\nFor feature behavior, default to an E2E-style regression first, especially by extending an existing\n`pure_dart` scenario when that can exercise the real generated workflow.\n\nOnly fall back to narrower tests when the E2E route is brittle, too expensive, or cannot reach the behavior:\n\n- Add a codegen or integration test when generated output proves the behavior but a full E2E case is awkward.\n- Unit test the function directly when the behavior is local, stable, and not meaningfully tied to an FRB\n  workflow.\n\nUse the relevant skills after choosing the test path:\n\n- `frb-develop-feature` for feature/bug regression coverage.\n- `frb-test` for selecting local test commands.\n- `frb-dev-env` before running FRB tests or setup commands in Tom's environment.\n\nIf the behavior matters but a focused test would be brittle, very expensive, or require a product decision,\nsummarize the exact missing lines, the suspected reason they are uncovered, and the realistic options, then\nask the human which tradeoff to take.\n\n## Verification\n\nAfter changing tests or ignore markers:\n\n1. Re-read the touched files immediately before editing.\n2. Commit the logical fix before long verification, following Tom's commit policy.\n3. Run the focused local test command when practical.\n4. Push and check the latest `codecov/patch` and `codecov/project` statuses on the new head.\n5. If Codecov is still red, repeat the workflow from the latest exact report instead of reusing stale line\n   numbers.\n",".claude/skills/frb-fix-main-ci/SKILL.md":"---\nname: frb-fix-main-ci\ndescription: Use when flutter_rust_bridge CI fails on the default branch/main/master, especially to distinguish ordinary CI repair from a regression introduced after a previously green default-branch commit.\n---\n\n# FRB Fix Main CI\n\nUse this when CI is failing on the default branch (`main` / `master`) or on a commit that is already part of the default-branch history.\n\n## Choose a Path\n\nDefault-branch CI failures usually have two possible repair paths. Choose based on the evidence in the latest relevant run.\n\n### Path A: Ordinary CI Repair\n\nUse this path when the failure looks like a known FRB CI shape: flaky job, generated diff, lint drift, stale generated output, dependency-order failure, or downstream symptom already covered by existing CI triage.\n\nRead `frb-fix-ci` and repair the failure directly:\n\n- Check the latest relevant run, not stale status.\n- Use the user's `tom-ci` skill when reading GitHub Actions job logs.\n- Classify flakes, generated diffs, lint drift, dependency-order failures, and propagated downstream symptoms before deep debugging.\n- Prefer fixing prerequisite jobs such as `Generate`, `Integrate`, or high-relevance `Generate Internal` before chasing later build/test symptoms.\n\n### Path B: Regression Investigation\n\nUse this path when the failure is not obviously a known FRB CI maintenance issue, or when several default-branch commits/runs suggest something recently broke.\n\nA default-branch failure is often a regression. There should usually be an older default-branch commit that passed the same CI path. Study what changed between the green baseline and the red commit, then fix the source of that regression.\n\nBefore writing a regression fix, answer these questions:\n\n1. What is the failing default-branch commit and workflow run?\n2. What is the nearest older default-branch commit with a green run for the same workflow or job family?\n3. Which commits or merged PRs sit between the green baseline and the red commit?\n4. Does the failure reproduce at the red commit but not at the green baseline, using the same `./frb_internal ...` command and the FRB dev environment rules?\n5. Which source change most directly explains the regression?\n\n## Regression Workflow\n\n1. Capture the exact failing jobs and the first useful error from CI logs.\n2. Find the newest passing default-branch run for the same workflow/job family.\n3. Compare the commit range between green and red:\n   ```bash\n   git log --oneline --decorate <green-sha>..<red-sha>\n   git diff --stat <green-sha>..<red-sha>\n   ```\n4. If the range is broad or the culprit is unclear, bisect with the smallest meaningful reproduction command.\n5. Fix the source of the regression, not just the generated or downstream symptom.\n6. Add or adjust regression coverage when the bug is behavioral. For pure CI/tooling drift, document the before/after evidence in the PR.\n\n## What Not To Do\n\n- Do not treat default-branch CI as \"just rerun until green\" unless logs strongly indicate infra flake.\n- Do not force every default-branch failure through regression bisect when `frb-fix-ci` already identifies a direct CI maintenance fix.\n- Do not only repair checked-in generated files if the generator, template, version pin, or environment change is the real source.\n- Do not chase downstream job failures while an upstream generation or integration job is still red.\n- Do not assume the newest red commit is the sole cause without checking the nearest green baseline and intervening changes.\n\n## PR Expectations\n\nFor a PR that fixes default-branch CI, include:\n\n- Failing commit SHA and workflow/job URL.\n- Nearest green baseline SHA or run, if available.\n- The reproduction command used locally or in Docker/Tart.\n- The suspected or confirmed culprit commit/PR.\n- Why the fix addresses the source of the regression.\n\nUse `frb-prepare-pr` before opening the PR.\n",".claude/skills/frb-fix-merge-conflict/SKILL.md":"---\nname: frb-fix-merge-conflict\ndescription: Use when resolving merge or rebase conflicts in flutter_rust_bridge, especially conflicts involving generated files after master/main changed independently from a PR branch.\n---\n\n# FRB Fix Merge Conflict\n\nUse this skill before resolving merge or rebase conflicts in `flutter_rust_bridge`.\n\n## Core Rule\n\nResolve hand-written files semantically. Do not hand-resolve generated files as the final result.\n\nGenerated conflict output should be treated as a stale artifact from two independently valid histories. After the hand-written sources are merged, regenerate the generated outputs and commit that regenerated result.\n\n## Before Starting\n\n1. Read the FRB environment setup instructions for the current checkout before running generation or tests.\n2. Read `frb-code-generation` to choose the minimal regeneration command.\n3. If the conflict is discovered through CI or PR checks, read `frb-fix-ci` before deeper investigation.\n4. Make sure the worktree starts clean except for the merge/rebase conflict you are intentionally resolving.\n\n## Classify Conflicts\n\nList unmerged files:\n\n```bash\ngit diff --name-only --diff-filter=U\n```\n\nClassify each file into one of these buckets:\n\n| Bucket | Examples | Resolution |\n|--------|----------|------------|\n| Hand-written source | Rust APIs, codegen logic, Dart tests, docs, configs | Resolve manually with normal semantic merge |\n| Generated output | `frb_generated.*`, `*.freezed.dart`, `*.g.dart`, generated API wrappers, generated test entrypoints | Pick one side only as a temporary placeholder, then regenerate |\n| Ambiguous generated-like output | lockfiles, copied template outputs, integrate scaffolds | Confirm source of truth before choosing; use `frb-fix-ci` dependency graph if unsure |\n\nDo not use the generated-file shortcut for hand-written files merely because they are noisy.\n\n## Generated File Conflict Workflow\n\nWhen a generated file conflicts because both base and PR independently added tests or APIs:\n\n1. Resolve all hand-written source conflicts first.\n2. For each generated conflicted file, choose a conflict-free placeholder from either side.\n   The chosen side is not the final answer; it only clears conflict markers so the generator can run.\n3. Stage those placeholder generated files.\n4. Run the appropriate generation command from `frb-code-generation`.\n   For broad PR-vs-master generated drift, prefer:\n\n```bash\n./frb_internal precommit-generate\n```\n\n   For `frb_example/pure_dart` and `frb_example/pure_dart_pde` conflicts, the internal generator and package codegen cover different outputs. If the merge touches generated test entrypoints, copied PDE files, or the pure-Dart chain, run:\n\n```bash\n./frb_internal generate-internal-frb-example-pure-dart\n```\n\n   If the merge also brings in hand-written Rust or Dart API changes, run package codegen after the internal generator:\n\n```bash\n./frb_internal generate-run-frb-codegen-command-generate --package frb_example/pure_dart\n./frb_internal generate-run-frb-codegen-command-generate --package frb_example/pure_dart_pde\n```\n\n   Do not stop after the internal generator when CI errors mention missing methods on generated Dart APIs such as `RustLibApi`; that usually means the package `frb_generated.*` files are stale.\n\n5. Inspect the regenerated diff. The final generated files should reflect both sides' hand-written sources.\n6. Commit the merge/rebase resolution.\n\n### Placeholder Commands\n\nFor a normal `git merge master` into a PR branch, this is usually enough for generated paths:\n\n```bash\ngit checkout --ours -- <generated-path>\ngit add <generated-path>\n```\n\nFor a `git rebase`, remember Git reverses the intuitive meaning of ours/theirs: `--ours` is the branch being rebased onto, and `--theirs` is the replayed commit. Since generated files are only placeholders, either side can be acceptable if the file still exists and regeneration will overwrite it. When deletion/addition is involved, prefer the side that leaves the path in the shape expected by the generator, then verify after regeneration.\n\n## Validation\n\nAfter regeneration:\n\n```bash\ngit diff --check\ngit diff --name-only --diff-filter=U\ngit status --short\n```\n\nThen run the relevant local Docker-backed checks from the project skills. For broad generated-file conflict repairs, start with the same generation command CI would run, then use CI to validate the full matrix.\n\nIf CI later reports more generated diff, add the experience back to this skill so the next repair has a sharper rule.\n\n## Commit Shape\n\nKeep the conflict-resolution commit focused:\n\n- Include semantic hand-written conflict resolutions.\n- Include regenerated outputs from the chosen generation command.\n- Do not include unrelated cleanup.\n- Mention the generation command in the commit message body when useful.\n",".claude/skills/frb-issue-to-green-pr/SKILL.md":"---\nname: frb-issue-to-green-pr\ndescription: \"Use when implementing a GitHub issue, bug fix, or feature in flutter_rust_bridge end-to-end: develop the change, add regression coverage, prepare and open a PR, and use the user's tom-ci skill to monitor CI until the PR is ready.\"\n---\n\n# FRB Issue to Green PR\n\nUse this skill as the top-level workflow when the user asks you to implement an issue, fix a bug, add a feature, or otherwise \"handle it end to end\" in `flutter_rust_bridge`, especially when they ask for an automatic PR or CI monitoring.\n\nThis skill orchestrates the narrower FRB skills. Do not duplicate their detailed instructions in memory; read them when their phase begins.\n\n## Required Subskills\n\nAlways read these first:\n\n1. `frb-dev-env` and any applicable user-level environment rules.\n2. `frb-develop-feature` for the reproduce -> fix -> final regression workflow.\n\nRead these when entering the matching phase:\n\n- `frb-code-generation` before running generation or changing Rust APIs, codegen, or example APIs.\n- `frb-test` before running local tests.\n- `frb-lint` before lint or format checks.\n- `frb-prepare-pr` before pushing or opening the PR.\n- `frb-pr-review` before treating a non-trivial PR as ready.\n- `frb-fix-ci` before diagnosing any CI failure.\n- The user's `tom-ci` skill for CI waiting, status inspection, and GitHub Actions logs.\n- `frb-manual-test` before writing a manual regression test report under `tools/manual_tests/`.\n- `frb-ci-filter` before creating an intentional red CI reproduction PR or using filtered CI.\n- `frb-debugging` when generated code is surprising or codegen behavior is unclear.\n\n## Workflow\n\n### 1. Start the completion loop\n\n- Keep the task active until all stop conditions are met.\n- Once the PR URL is known, follow the waiting workflow in the user's `tom-ci` skill between CI processing actions until the PR is ready.\n\n### 2. Understand the GitHub issue or requested change\n\n- Fetch the issue body and comments if the user gave an issue link or number.\n- Identify the smallest observable failing behavior or missing capability.\n- Check `git status --short` and do not disturb unrelated user or multi-agent changes.\n\n### 3. Reproduce the issue in an independent reproduction PR\n\n- Read and follow the reproduction parts of `frb-develop-feature`.\n- For bug fixes, do this before changing fix code or opening the fix PR.\n- Create an independent reproduction PR whose only purpose is to prove the bad behavior. The branch name, PR title, and PR body must say clearly that it is an intentional reproduction PR, not a real fix PR.\n- For intentional red CI reproduction PRs, use the title `Reproduce ISSUE_SUMMARY with intentional red CI`.\n- For manual-test reproduction PRs, use the title `Add manual reproduction for ISSUE_SUMMARY`.\n- If CI can reproduce the bad behavior, read `frb-ci-filter` and make the reproduction PR an intentional red CI PR: unchanged fix code, minimal reproducer or workflow adjustment, mandatory focused `ci_filter` run, and a failure whose error matches the user's report.\n- If CI cannot realistically reproduce the bad behavior, read `frb-manual-test` and make the independent reproduction PR add or update `tools/manual_tests/NAME.md` with a normal manual test procedure and mechanical execution steps an agent or human can run.\n- Do not proceed to the fix PR until the reproduction PR exists and has either a matching red CI run or a precise manual test report.\n- Save the reproduction PR URL, red CI run URL when applicable, job name or manual-test path, and matching error text for the fix PR reproduction report.\n\n### 4. Fix the issue\n\n- Read and follow the fix, iteration, local verification, regression coverage, and final placement parts of `frb-develop-feature`.\n- Implement the smallest change that fixes the reproduced behavior.\n- Add final regression coverage with the fixed behavior as the expected result.\n- Make atomic commits as soon as each completed logical unit is written; do not wait until the end of the task.\n- Stage only files intentionally changed for this task.\n- Always create a new commit unless the user explicitly asks to amend.\n- Before considering the change ready, explicitly pass the `frb-develop-feature` Final Placement Gate: final regression coverage belongs in `frb_example/pure_dart` with generated `pure_dart_pde` coverage, not only in `frb_example/dart_minimal`.\n- Keep generated-file edits produced by the appropriate generator, not by hand.\n\n### 5. Prepare and open the PR\n\n- Follow `frb-prepare-pr`.\n- Re-check that no final regression or feature coverage remains only in `frb_example/dart_minimal`. If it does, stop PR preparation and migrate it to `frb_example/pure_dart` first.\n- Push with upstream tracking.\n- Before drafting a PR title, inspect the user's recent PR titles and mimic the repo style.\n- Create the PR according to the active PR workflow and repository/user PR body rules.\n- If the work comes from a GitHub issue, ensure the PR body includes the appropriate closing keyword such as `Close #1234`, unless the active PR workflow explicitly requires an empty body.\n\n### 6. Run the review gate\n\n- Follow `frb-pr-review` for the full PR review gate, including correctness review and test-weakening review.\n\n### 7. Monitor CI until terminal\n\n- After the PR is opened or updated, do not leave the PR in an unknown queued or in-progress state.\n- On each wake-up, inspect the latest PR checks, not stale runs.\n- For bug-fix PRs with an intentional red CI reproduction PR, explicitly find the same job family or CI path that failed in the reproduction branch and verify that it is now green on the fix PR.\n- For bug-fix PRs with a manual-test report PR, state whether the manual regression was re-run, who or what ran it, and whether the observed behavior now matches the fixed expectation.\n- If CI fails, read `frb-fix-ci` and the user's `tom-ci` skill, diagnose the latest relevant failure, fix it, commit, push, and continue monitoring.\n- If CI appears flaky, rerun only failed jobs when appropriate, then keep monitoring.\n\n### 8. Stop only when ready\n\n- The PR checks are green or all remaining non-green checks are clearly unrelated and explained.\n- The branch is pushed, commits are present on the PR, and the final status is reported to the user with the PR URL.\n\n## Automation Rules\n\n- Follow the user's `tom-ci` skill to wait on the PR URL after each push, rerun, or handled CI event.\n- Treat its return as a wake-up signal, not a CI verdict. Inspect the reported checks, fix or rerun as required, then call the waiter again.\n- Do not add a separate time-based heartbeat or detached polling loop for the same PR.\n- Stop invoking the waiter once the stop conditions are met.\n\n## Failure Handling\n\n- If blocked by permissions, missing credentials, a required external reviewer delay, or unavailable infrastructure, explain the concrete blocker and keep the PR state explicit.\n- If a CI run is queued for a long time, continue bounded waits using the user's `tom-ci` skill.\n- If new user instructions arrive, let the newest instruction steer the workflow while preserving already-completed work.\n",".claude/skills/frb-lint/SKILL.md":"---\nname: frb-lint\ndescription: Use when you need to run lint, format, or clippy checks in flutter_rust_bridge\n---\n\n# FRB Lint\n\n> **Note:** Read `frb-dev-env` before running commands and follow the active user's environment rules.\n\n## Quick Reference\n\n| Command | Description |\n|---------|-------------|\n| `./frb_internal lint --fix` | Run all lints and auto-fix (recommended) |\n| `./frb_internal lint` | Run all lints |\n| `./frb_internal lint-dart` | Dart only |\n| `./frb_internal lint-rust` | Rust only |\n\n## What Lint Includes\n\nIt contains:\n- `dart analyze`\n- `dart format`\n- `cargo clippy`\n- `cargo fmt`\n",".claude/skills/frb-manual-test/SKILL.md":"---\nname: frb-manual-test\ndescription: Use when adding, executing, reviewing, or updating flutter_rust_bridge manual test reports under tools/manual_tests for scenarios that require human or agent-driven manual verification.\n---\n\n# FRB Manual Test\n\nUse this skill for manual test reports in `tools/manual_tests/`. These reports describe repeatable checks that a human or agent can execute when automated tests are insufficient, unavailable, too expensive, or not the right interface for the behavior being verified.\n\nManual tests are normal software-engineering test artifacts. They may cover devices, host-specific setup, interactive UI behavior, credentials or account state, release packaging, marketplace flows, external services, or long-running workflows. They are not tied to any single PR workflow.\n\n## Add Manual Tests\n\nManual test reports live under:\n\n```text\ntools/manual_tests/NAME.md\n```\n\nWhen adding a report, do not start from a blank file. Copy the bundled test template first:\n\n```bash\ncp .claude/skills/frb-manual-test/test-template.md tools/manual_tests/NAME.md\n```\n\nUse a short kebab-case name. Include a date prefix when the report is an example, a one-off check, or does not yet have a stable feature name, such as:\n\n```text\ntools/manual_tests/2026-06-06-some-scenario.md\n```\n\nAfter copying, edit the report in place:\n\n- Replace every placeholder.\n- Remove sections that truly do not apply.\n- Keep commands and UI actions mechanical enough that a future human or agent can run them without the original conversation.\n- Prefer repo tooling such as `./frb_internal` when possible.\n- Do not rely on \"run the app normally\", \"check manually\", or \"verify it works\" without concrete commands or UI actions.\n- If credentials or private services are needed, name the required capability without including secrets.\n\nThe detailed content requirements live in `test-template.md`; keep that template as the source of truth for report shape.\n\n## Execute Manual Tests\n\nWhen executing a manual test, first read the target report under `tools/manual_tests/` and follow its preparation, steps, expected result, failure criteria, troubleshooting, and cleanup sections.\n\nBefore reporting the run result, use the bundled execution template:\n\n```bash\ncp .claude/skills/frb-manual-test/execution-template.md EXECUTION_RESULT_PATH.md\n```\n\nAlways fill out an execution markdown file before summarizing the result. Do not only say that the test \"passed\".\n\nExecution markdown is a run artifact, not a repository test definition. Do not create it under `tools/manual_tests/` or anywhere else inside the `flutter_rust_bridge` repository. Put it in an external artifacts location such as `/private/tmp`, `~/main/artifacts/flutter_rust_bridge/`, or another run-specific artifact directory.\n\nExecution markdown must contain precise, audit-ready evidence that proves what ran and why it passed, failed, or blocked. Include the exact commands, exit status, key success or failure log lines, relevant version output, selected device or simulator identifiers, generated artifact paths, final cleanup state, and links or paths to complete logs. For successful runs, quote or summarize the concrete pass indicators such as `All tests passed!`, `test result: ok`, artifact paths, simulator UDIDs, or final `git status --short` output. Avoid vague summaries like \"worked\", \"passed\", or \"looked good\" without evidence.\n\nIf the execution result belongs to a PR, issue, release checklist, or other reviewable workflow, upload the filled execution markdown and all review-relevant artifacts as a GitHub gist and link that gist in the PR description, PR comment, issue comment, or release checklist. Review-relevant artifacts include screenshots, recordings, logs, generated result files, and browser/device evidence needed to judge the result. The local execution markdown and artifact copies should still be kept in the run artifacts directory so the run can be audited without relying only on chat history.\n\nIf the result belongs only in chat, still fill out the execution markdown locally and include its path in the chat response.\n\nRun cleanup from the manual test report before declaring the execution complete, unless cleanup is intentionally skipped and documented in the execution record.\n\n### Reviewable Workflow Gate\n\nBefore creating or updating a PR, issue comment, release checklist, or any other reviewable artifact that cites a manual test execution:\n\n1. Fill out the execution markdown file locally.\n2. Collect every artifact needed by the manual test's `Results To Capture` section, especially screenshots, recordings, logs, and generated evidence files.\n3. Upload the execution markdown and those review-relevant artifacts into the same GitHub gist. Use a gist git clone and push when binary artifacts such as PNG screenshots need to be included.\n4. Copy the gist URL into the execution markdown's `Execution markdown gist` field if practical.\n5. Make the execution markdown point to the gist-contained artifact filenames, embedding screenshots or linking recordings when useful.\n6. Link the gist URL in the reviewable artifact.\n7. Keep local artifact paths only as supplemental audit evidence; they are not a substitute for gist-contained artifacts.\n\nDo not say that a PR-linked manual test execution is complete while any review-relevant artifact exists only as a local path. If gist upload or GitHub access fails, report the manual test execution as complete locally but the PR/update step as blocked until a gist containing the execution markdown and review-relevant artifacts can be created and linked.\n",".claude/skills/frb-manual-test/execution-template.md":"# Manual Test Execution\n\n## Test\n\n:::info\nIdentify the manual test that was executed and why this run happened. Copy the purpose from the test report or summarize it in one sentence.\n:::\n\n- Manual test: `TODO: tools/manual_tests/name.md`\n- Purpose: `TODO: short description copied or summarized from the test report`\n- Source context: `TODO: issue, PR, release, or request if relevant`\n\n## Run\n\n:::info\nRecord when, by whom, and against exactly what commit or release the manual test was executed. Include enough environment detail for someone else to compare results.\n:::\n\n- Date and timezone: `TODO: YYYY-MM-DD HH:MM TZ`\n- Executor: `TODO: human, agent, or automation`\n- Commit or release tested: `TODO: sha or version`\n- Environment: `TODO: OS, shell, device, simulator, browser, or service summary`\n\n## Procedure\n\n:::info\nRecord whether the written test was followed exactly. Any deviation matters, even if the result passed. Include exact commands or a path to a command script when the run used one.\n:::\n\n- Followed report revision: `TODO: commit containing the manual test report`\n- Command script or exact commands: `TODO: path to script, inline commands, or not applicable`\n- Deviations from written steps: `TODO: none, or list exact deviations`\n- Cleanup completed: `TODO: yes/no; explain if no`\n\n## Result\n\n:::info\nUse `pass`, `fail`, `blocked`, or `partial`. The summary should explain the result without requiring the reader to open logs first. Include exact command exit status and concrete pass/fail indicators from the logs.\n:::\n\n- Status: `TODO: pass/fail/blocked/partial`\n- Summary: `TODO: one or two sentences`\n- Command results: `TODO: each required command with exit status`\n- Evidence lines: `TODO: exact success/failure strings, log line numbers, or grep output proving the result`\n\n## Artifacts\n\n:::info\nLink or name the artifacts captured during execution. Use `not applicable` for artifact types that do not apply.\n:::\n\n- Execution markdown gist: `TODO: gist URL if this result belongs to a PR, issue, or release checklist; otherwise not applicable`\n- Terminal log: `TODO: path or link`\n- Screenshot or recording: `TODO: path or link, or not applicable`\n- Generated artifacts: `TODO: path or link, or not applicable`\n- Environment evidence: `TODO: exact tool versions, device/simulator name and UDID, browser version, or not applicable`\n- Cleanup evidence: `TODO: final git status, VM/container/device state, cleanup command output, or not applicable`\n- Other evidence: `TODO: path, link, log excerpt, or not applicable`\n\n## Follow-up\n\n:::info\nIf the result is not a clean pass, state the next action. Link the issue or PR when one exists.\n:::\n\n- Required follow-up: `TODO: none, issue link, PR link, or next action`\n- Notes: `TODO: anything future executors should know`\n",".claude/skills/frb-manual-test/test-template.md":"# Manual Test Template\n\n## Purpose\n\n:::info\nWrite one or two concrete sentences explaining what behavior this manual test verifies. Name the user-visible behavior, platform, package, or workflow under test. Do not leave this as a generic phrase like \"verify it works\".\n:::\n\nTODO: Write what this test verifies.\n\n## Source\n\n:::info\nLink the reason this test exists: issue, PR, feature request, release checklist, user report, or maintenance task. If there is no external link, state the local context in plain text.\n:::\n\n- Context: `TODO: issue, PR, feature, release, or user report`\n- Related docs or skills: `TODO: paths or links`\n\n## When To Run\n\n:::info\nState the trigger for this manual test. Examples: before a release, after changing a named subsystem, after upgrading Flutter/Rust/Dart, before closing a bug, or when validating a specific platform.\n:::\n\nTODO: Write when this test is required or useful.\n\n## Preconditions\n\n:::info\nList everything that must already be true before starting. Include checkout state, expected branch family if relevant, submodules, generated files, devices, simulators, credentials, account state, network access, and external services. Never paste secrets. Keep this section about reusable requirements, not one particular run.\n:::\n\n- Repository: `fzyzcjy/flutter_rust_bridge`\n- Required checkout state: `TODO: clean checkout with submodules initialized, generated files present, etc.`\n- Required credentials or account state: `TODO: required capability, never paste secrets`\n- Required device or simulator state: `TODO: device must be available, app uninstalled, cache reset, etc.`\n\n## Environment\n\n:::info\nRecord exact versions and runtime context needed to make the result reproducible. If a tool is not required, write `not required` instead of leaving it ambiguous.\n:::\n\n- OS: `TODO: macOS / Linux / Windows version, or not constrained`\n- Flutter: `TODO: flutter --version, or not required`\n- Dart: `TODO: dart --version, or not required`\n- Rust: `TODO: rustc --version, or not required`\n- Device or simulator: `TODO: device name, OS version, UDID if applicable, or not required`\n- Browser or external service: `TODO: name and version if applicable, or not required`\n\n## Preparation\n\n:::info\nGive copy-pasteable commands that take an existing repository root to the state needed by the test. Prefer repository tooling such as `./frb_internal`. If generated files, caches, or dependencies are required, include the exact commands here. Keep source acquisition and revision selection outside the reusable test case.\n:::\n\n```bash\ngit submodule update --init --recursive\n```\n\nIf this test needs generated output or dependencies, list the exact setup commands here.\n\n```bash\n./frb_internal --help\n```\n\n## Test Data\n\n:::info\nDescribe all inputs and fixtures. Include paths, account setup, generated files, sample API code, reset commands, or \"none\" if the test uses no special data.\n:::\n\n- Input files, API examples, account fixtures, or generated assets: `TODO: paths or setup notes, or none`\n- Reset procedure before each run: `TODO: commands or UI actions`\n\n## Steps\n\n:::info\nWrite ordered mechanical steps. Each step should be executable by a human or agent without guessing. For UI steps, name buttons, screens, field values, and expected intermediate state. For command steps, include complete commands.\n:::\n\n1. TODO: Prepare the target package, example, device, account, or service.\n\n   ```bash\n   TODO_COMMAND\n   ```\n\n2. TODO: Run the command or manual action that verifies the behavior.\n\n   ```bash\n   TODO_COMMAND\n   ```\n\n3. If UI interaction is required, describe each click, field value, device action, or expected screen by name.\n\n## Expected Result\n\n:::info\nDefine the pass condition as observable output: command exit status, log text, generated artifact, UI state, screenshot content, or external service state. Avoid vague language.\n:::\n\nTODO: The command or manual flow should complete successfully and show the expected observable behavior.\n\n```text\nTODO: success output, UI text, artifact path, or other pass criteria\n```\n\n## Failure Criteria\n\n:::info\nList concrete conditions that mean the test failed or is blocked. Include setup failures that should be reported separately from product failures.\n:::\n\nThe test fails if any of the following happens:\n\n- The command exits non-zero unexpectedly.\n- The UI, artifact, or log output does not match the expected result.\n- The required device, account, or service state cannot be prepared.\n\n## Results To Capture\n\n:::info\nSay exactly what artifacts the executor should save or link in the execution record. Include terminal logs, screenshots, recordings, generated files, device details, run URLs, or external service observations.\n:::\n\n- Full terminal log from the command.\n- Screenshot or screen recording if UI behavior is involved.\n- Device, simulator, or browser version.\n- Any generated artifact path needed to inspect the result.\n\n## Troubleshooting\n\n:::info\nProvide known recovery steps for common environment and setup failures. This section should help distinguish a blocked manual run from a real product failure.\n:::\n\n- If dependencies are missing, re-run the repository setup commands and record the exact failure.\n- If a device or simulator is unavailable, record the discovered devices and mark the run as blocked.\n- If credentials or external services are unavailable, record the missing capability without exposing secrets.\n\n## Cleanup\n\n:::info\nList commands and manual actions needed to restore the checkout, stop services, reset devices, and undo external account state. If no cleanup is needed, say so explicitly.\n:::\n\n```bash\ngit status --short\n```\n\nRemove temporary local files, stop simulators or servers started for the test, and restore any external account or device state changed during the run.\n\n## Future Automation\n\n:::info\nExplain whether this manual test could later become automated. If yes, name the likely CI job, package, or test harness. If no, explain what keeps it manual.\n:::\n\nIf this scenario later becomes reliable and affordable to automate, replace or supplement this manual report with an automated test and link the PR here.\n",".claude/skills/frb-pr-chain-split/SKILL.md":"---\nname: frb-pr-chain-split\ndescription: Use when an FRB PR contains independently landable work that should be extracted into predecessor PRs and connected as a GitHub PR chain without wasting full automatic CI.\n---\n\n# 1 When to use\n\n- Use this skill while developing or reviewing a `flutter_rust_bridge` PR when part of its diff is independent of the PR's stated objective.\n- Split the independent work proactively. Do not wait for the user to notice the mixed scope.\n- Treat a change as independently landable when it:\n    - has a coherent behavior or maintenance objective of its own;\n    - can be implemented and validated against the current default branch;\n    - does not require the main task's API, schema, generated output, or version change;\n    - leaves the main PR smaller and easier to review.\n- Keep inseparable companion changes in the main PR. Generated snapshots, tests, and documentation required by the main change are not independent merely because they touch different files.\n\n# 2 Choose the chain boundary\n\nClassify every suspicious change before moving commits:\n\n| Classification | Destination | Reason |\n| --- | --- | --- |\n| Independent bug fix or hardening | Predecessor PR | It can land and provide value before the main task |\n| Independent mechanical refactor | Predecessor PR | It removes review noise from the main task |\n| Main-task prerequisite with its own contract | Predecessor PR | The main PR can depend on a separately reviewable foundation |\n| Generated output caused by the main task | Main PR | It cannot land meaningfully by itself |\n| Test or documentation for the main behavior | Main PR | Splitting would separate the contract from its implementation |\n\n- Prefer the smallest coherent predecessor, not the smallest textual diff.\n- Create several predecessor PRs when the extracted work contains multiple independent contracts.\n- Order predecessors by dependency. Do not infer order from commit timestamps.\n\n# 3 Build each predecessor\n\n- Fetch the canonical default branch before creating branches.\n- Create the first predecessor from the current default branch.\n- Create each later predecessor from the preceding predecessor branch.\n- Reconstruct the coherent final delta. Do not blindly cherry-pick commits that mix predecessor and main-task changes.\n- Include the predecessor's own tests and documentation.\n- Validate the predecessor independently before pushing.\n- Read `frb-prepare-pr` for generation, lint, test, and review requirements.\n- Use one branch and one PR per independently landable unit.\n- Push every chain branch to the same repository as the main PR. GitHub cannot use an unpushed local branch as a PR base.\n\n# 4 Construct the GitHub PR chain\n\n- Build a stacked branch chain, not sibling PRs that all target the default branch:\n\n```text\nmaster <- predecessor-a <- predecessor-b <- main-task\n```\n\n- Use GitHub's official `github/gh-stack` extension through `gh stack`.\n- Use `gh stack` for every stack operation: initialization, restructuring, linking, submission, synchronization, rebasing, pushing, and verification.\n- Never construct or maintain a stack with `gh pr create --base`, `gh pr edit --base`, direct API base edits, or branch ancestry alone.\n- Git commands may prepare the commits and linear branch ancestry, but the result is not complete until `gh stack` creates or updates the native GitHub stack object.\n- Check and install the extension when necessary. The extension requires `gh` 2.0 or later; a missing `gh stack` command normally means the extension is absent, not that core `gh` is outdated.\n\n```bash\ngh stack --help\ngh extension install github/gh-stack\n```\n\n- For an unpublished main branch, branch it directly from the final predecessor.\n- Initialize and submit a new local stack with `gh stack init` and `gh stack submit`.\n- For an already published main PR:\n    - create a timestamped backup tag before non-trivial history work;\n    - record the original final tree and three-dot PR diff;\n    - restack the main branch onto the final predecessor with a fully linear history;\n    - do not introduce merge commits because native GitHub stacks require linear ancestry;\n    - compare the rebuilt tree and PR delta with the recorded originals before pushing;\n    - use `--force-with-lease` only after those checks when published history must be rewritten.\n- Adopt existing branches or PRs with `gh stack link`. It pushes branches, creates missing PRs, corrects their base branches, and creates the native GitHub stack object.\n- Create and connect a new stack with commands shaped like:\n\n```bash\ngh stack init --base master <predecessor-branch> <main-task-branch>\ngh stack submit\n```\n\n- Adopt already prepared branches or existing PRs with:\n\n```bash\ngh stack link --base master <predecessor-branch> <main-task-branch>\n```\n\n- Verify local ancestry, native stack metadata, and PR bases:\n\n```bash\ngit merge-base --is-ancestor <predecessor-branch> <main-task-branch>\ngh stack view --json\n```\n\n- Compare three-dot diffs after constructing the chain:\n    - `<previous-branch>...<predecessor-branch>` contains only that predecessor;\n    - `<final-predecessor-branch>...<main-task-branch>` no longer contains the extracted work;\n    - the final main-task tree is unchanged except for intentional cleanup.\n\n# 5 Conserve CI on predecessor PRs\n\n- Read `frb-ci-filter` before manipulating CI.\n- Immediately add `ci-manual-dispatch` after creating every predecessor PR:\n\n```bash\ngh pr edit <predecessor-pr-number> --add-label ci-manual-dispatch\n```\n\n- Add the label even when local validation is already green. Predecessor PRs must not spend the full CI matrix merely because they were split for reviewability.\n- The label action triggers a new `pull_request:labeled` run. The workflow concurrency group cancels the earlier automatic PR run, and the labeled run produces an empty heavy-job plan.\n- Do not dispatch GitHub CI when local validation is sufficient.\n- When a predecessor specifically needs GitHub-only evidence:\n    - keep `ci-manual-dispatch` on the PR;\n    - validate the filter locally;\n    - dispatch only the smallest relevant job or matrix entry.\n\n```bash\n./frb_internal plan-ci --filter '<filter>'\ngh workflow run ci.yaml --ref <predecessor-branch> -f 'ci_filter=<filter>'\n```\n\n- Do not treat filtered CI as full merge-readiness evidence.\n- When a predecessor becomes the next PR to merge, remove `ci-manual-dispatch` and obtain the normal required CI unless the maintainer explicitly accepts narrower evidence.\n\n# 6 Completion checks\n\n- Confirm each predecessor can be reviewed without understanding the main task.\n- Confirm each PR's GitHub base matches its immediate predecessor branch.\n- Confirm Git ancestry matches the GitHub base chain.\n- Confirm the main PR diff excludes all extracted changes.\n- Confirm every predecessor PR has `ci-manual-dispatch` while it is not awaiting final full CI.\n- Report the PR numbers, branch order, validation performed, and any intentionally dispatched `ci_filter`.\n\n# 7 Reliability pitfalls\n\n- Do not create independent sibling PRs and merely call them a chain.\n- Do not substitute manual PR base edits for `gh stack link` or `gh stack submit`.\n- Do not treat matching branch ancestry as sufficient; verify the native stack with `gh stack view --json`.\n- Do not force-push a published main PR merely to make its history prettier.\n- Do not split generated files away from the generator or source change that owns them.\n- Do not leave an extracted commit duplicated in the visible main PR diff.\n- Do not spend full CI on every dormant predecessor.\n- Do not leave `ci-manual-dispatch` on the PR when claiming normal full-CI readiness.\n",".claude/skills/frb-pr-review/SKILL.md":"---\nname: frb-pr-review\ndescription: Review a flutter_rust_bridge PR before treating it as ready, including subagent checks for correctness and test weakening.\n---\n\n# 1 Human SoT\n\n# 2 FRB PR Review\n\nUse this before treating a non-trivial `flutter_rust_bridge` PR as ready, especially after CI-driven fixes, Flutter upgrades, generated drift, test changes, workflow changes, or broad merge conflict resolution.\n\n## 2.1 Review Gate\n\nRun the following review before final readiness:\n\n1. Review the PR diff against its base:\n   - Spawn a subagent to review correctness.\n     - Ask it to inspect the PR diff against the PR base.\n     - Focus on real behavior bugs, stale generated output, incorrect CI workarounds, coverage-only changes, and unrelated drift.\n     - Require findings with file paths, line numbers, impact, and suggested fix.\n   - Spawn a subagent to review test weakening.\n     - Inspect changed or deleted assertions, relaxed thresholds, new skips, ignored failures, timeout changes, and coverage exclusions.\n     - Require a concrete justification for each reduction in coverage; restore unjustified weakening and fix the underlying failure.\n     - If `tom-style-tests` is installed, apply its common test principles and weakening gate; use `frb-test` for FRB execution commands.\n     - Treat unjustified skipped tests, weaker assertions, broader ignores, fake timeouts, and coverage hiding as blockers.\n\n2. Write a concise review conclusion.\n   - Put the conclusion in the PR description or an agent-context draft when the user asks for a Markdown artifact.\n   - Include the subagents used, accepted findings, dismissed findings, fixes made, and remaining risks.\n\n## 2.2 Stop Condition\n\nDo not call the PR ready until:\n\n- Correctness review has no unresolved actionable findings and test-weakening review has no unjustified weakening.\n- CI status is green, or remaining non-green checks are clearly unrelated and explained.\n",".claude/skills/frb-prepare-pr/SKILL.md":"---\nname: frb-prepare-pr\ndescription: Use when about to create a PR or push changes in flutter_rust_bridge to ensure code generation is up to date\n---\n\n# FRB Prepare for PR\n\n> **Note:** Read `frb-dev-env` before running commands and follow the active user's environment rules.\n\n## Overview\n\nBefore creating a PR, ensure generated code is up to date, lint passes, and bug fixes have a concrete reproduction report in the PR description. Before treating a non-trivial PR as ready, run `frb-pr-review`.\n\n**Core principle:** Generate → Lint → Commit → PR.\n\n> **After codegen:** Follow `frb-dev-env` and verify generated output is present in the selected worktree before reviewing the diff.\n\n## Workflow\n\n```\n1. Read frb-code-generation skill\n   |\n   +-- Run required generation commands\n   |\n   +-- 2. For integrate output diffs, verify templates and run precommit-integrate\n   |\n   +-- 3. Read frb-lint skill --> Run lint --fix\n   |\n   +-- 4. (Optional) Read frb-test skill --> Run relevant tests\n   |\n   +-- 5. For non-trivial PRs, read frb-pr-review --> Run review gate\n   |\n   +-- 6. For bug fixes, add the reproduction report to the PR description\n   |\n   +-- 7. Commit all changes\n   |\n   +-- 8. Create PR (follow the active user-level PR workflow)\n```\n\n## Quick Checklist\n\n1. [ ] **REQUIRED:** Read `frb-code-generation` skill, run commands if needed\n2. [ ] **REQUIRED for integrate output diffs:** If the PR changes Flutter integrate example outputs or platform scaffolds, confirm whether `frb_codegen/assets/integration_template/` is the source that should change, then run `./frb_internal precommit-integrate`\n3. [ ] **REQUIRED for copied Cargokit diffs:** If the PR changes copied `cargokit` files under `frb_example/**`, read `frb-cargokit`, then run `./frb_internal sync-cargokit-copies`\n4. [ ] **REQUIRED:** Read `frb-lint` skill, run `./frb_internal lint --fix`\n5. [ ] (Optional) Read `frb-test` skill, run relevant tests\n6. [ ] **REQUIRED for non-trivial PRs:** Read `frb-pr-review`, run the review gate before final readiness\n7. [ ] **REQUIRED for bug fixes:** PR description includes the reproduction report from `frb-develop-feature`, including baseline commit, mechanical steps, observed failure, and expected behavior\n8. [ ] **REQUIRED for CI iteration branches:** Read `frb-ci-filter`, then remove `ci-manual-dispatch` and rerun normal CI if the PR used filtered CI, unless the PR is explicitly about changing CI behavior\n9. [ ] Commit all changes\n10. [ ] Push and create PR\n\n## What CI Will Do\n\nCI automatically runs:\n- Code generation check (fails if generated code is outdated)\n- All tests (Rust, Dart, Flutter, Web)\n- Lint and format checks\n\nRun lint locally to avoid CI failures. Tests are optional locally.\n\nWhile iterating, it can be useful to dispatch `.github/workflows/ci.yaml` with `ci_filter` so only the relevant job family or matrix entry runs. Read `frb-ci-filter` before doing this. Do not leave `ci-manual-dispatch` on the final PR unless changing CI behavior is the actual purpose of the PR.\n\nIf your PR fixes Flutter integrate example outputs and the real bug is inside the embedded `cargokit` submodule, do not stop at copied example files. Push the `cargokit` fix to `fzyzcjy/cargokit` and update the submodule ref in this repo before pushing the PR branch.\n\nIf the PR changes integrate-generated example output but not `frb_codegen/assets/integration_template/`, explicitly verify that the output-only change is intentional. Most integrate scaffold behavior should be fixed in the template and regenerated, not patched only in `frb_example/**`.\n\n## Related Skills\n\n- `frb-code-generation` - Determines which generation commands to run\n- `frb-lint` - Lint and format checks\n- `frb-test` - For local debugging when CI fails\n- `frb-pr-review` - PR readiness review gate\n- `frb-ci-filter` - Filtered CI dispatch rules\n- Follow the active user-level PR workflow when available; otherwise use `gh pr create` with the repository's title and body conventions.\n",".claude/skills/frb-publish-release/SKILL.md":"---\nname: frb-publish-release\ndescription: \"Publish a flutter_rust_bridge release end to end: preflight checks, changelog preparation, frb_internal release publishing, released-version polling, CI babysitting, and post-release CI verification.\"\n---\n\n# FRB Publish Release\n\nUse this skill when preparing, publishing, or babysitting a `flutter_rust_bridge` release.\n\n## Workflow\n\n### 1. Preflight\n\n- Work from the repository root on the intended release branch, normally fresh `master`. If the checkout is detached but already points at the intended release commit, switch or create a local release branch from that commit before running mutating release commands; do not treat detached HEAD itself as a release blocker.\n- Check `git status --short --branch` and do not start publishing from a dirty tree.\n- Check submodules before publishing:\n\n  ```bash\n  git submodule update --init --recursive\n  git submodule status --recursive\n  ```\n\n  Stop if any submodule path remains uninitialized. A release built without initialized submodules can publish incomplete package contents. The release script performs the same submodule-status guard before publishing.\n- Treat `master` as a moving branch. Before each irreversible release phase, fetch `origin master`, confirm the current release branch or detached checkout still points at the intended latest `origin/master` commit, and re-run `git status --short --branch` to confirm there are no unexpected local changes.\n- Confirm the target version in `CHANGELOG.md`, root `Cargo.toml`, and `frb_dart/pubspec.yaml`.\n- Compute the release versions the same way `./frb_internal release` does: the top `CHANGELOG.md` version is the new version and the next release section is the old version.\n- Verify both old and new versions are legal before running any mutating release command. The only allowed shapes are stable SemVer `MAJOR.MINOR.PATCH` such as `2.0.0`, or beta SemVer `MAJOR.MINOR.PATCH-beta.N` such as `2.0.0-beta.1`. Use exactly `^\\d+\\.\\d+\\.\\d+(-beta\\.\\d+)?$`.\n- Reject versions with any other prerelease label, build metadata, missing numeric components, leading `v`, or loose text. Stop if either old or new version fails this check.\n- Confirm the new version is different from the old version.\n- Run the publish container credential preflight before starting irreversible publish steps:\n\n  ```bash\n  .claude/skills/frb-dev-env/frb_dev_env.py docker-run-rm --with-publish-credentials -- true\n  ```\n\n  Stop if the preflight fails. It checks GitHub CLI auth, Cargo credentials, and Dart pub credentials inside the same temporary credential layout used by release publishing.\n- Confirm normal CI is green for the release commit before publishing. This is the default hard gate.\n\n  A narrow exception is allowed only when all of these are true:\n\n  - A recent earlier commit on the same release branch has fully green normal CI.\n  - Every commit between that green commit and the intended release commit is unrelated to the publishable packages, version sources, generated release artifacts, and `frb_internal` release logic.\n  - The mechanical gate script below exits successfully and its output is recorded in the release notes or journal.\n  - The agent explicitly states that it is using the exception before publishing.\n\n  Run the mechanical gate from the repository root:\n\n  ```bash\n  uv run --script .claude/skills/frb-publish-release/release_ci_gate.py \\\n    --base-green-ref <LAST_GREEN_SHA> \\\n    --release-ref HEAD\n  ```\n\n  Stop and wait for normal CI if the script reports any `BLOCK` path. The script is intentionally conservative: release-surface paths such as `Cargo.toml`, `frb_dart/pubspec.yaml`, `frb_codegen/**`, `frb_macros/**`, `frb_rust/**`, `frb_utils/**`, `frb_example/**`, `tools/frb_internal/**`, `pubspec.yaml`, `melos.yaml`, and lockfiles are not in the allowlist. Release metadata and its generated copies are allowed: `CHANGELOG.md`, `frb_dart/CHANGELOG.md`, `frb_hooks/CHANGELOG.md`, `.all-contributors-custom.yaml`, `.all-contributorsrc`, `README.md`, `frb_dart/README.md`, and `website/**`. These files are reviewed directly before publishing and do not affect package execution. Other `frb_dart/**` and `frb_hooks/**` paths remain blocked. This exception is for release metadata, docs, agent tooling, devcontainer, selected CI configuration, and other explicitly non-release paths only.\n\n### 2. Reconcile Contributors\n\n- Before writing the release changelog, use `frb-add-contributor` to identify contributors from the target release range who may need all-contributors credit or whose human-written custom description does not cover every release-range PR.\n- Follow `frb-add-contributor` exactly. In particular, after determining which contributors may need to be added or have their custom descriptions extended, stop for human confirmation before editing contributor files, posting GitHub comments, triggering all-contributors, opening PRs, or merging contributor PRs.\n- Do not continue to changelog preparation until contributor reconciliation is complete, or confirmed unnecessary because all contributors are credited and their custom descriptions cover every third-party PR in the release range.\n\n### 3. Write Changelog\n\n- Use `frb-write-changelog` and follow it exactly. It is the single source of truth for beta and stable changelog content, grouping, ordering, attribution, review, verification, and generated package changelog copies. Do not duplicate those rules here.\n- Do not publish until the human-review gate required by `frb-write-changelog` is satisfied.\n- The top `CHANGELOG.md` version is the source used by `frb_internal release`.\n- If changelog or version files changed, commit that release preparation before publishing.\n- Land contributor and changelog preparation commits on `origin/master` before publishing. Do not leave release preparation only on a side branch.\n\n### 4. Pre-Publish Independent Audit\n\n- Immediately before the first irreversible publish command, delegate a read-only audit to an independent subagent that has not participated in preparing the release.\n- Give the subagent the intended version and the raw evidence needed to verify the work completed so far:\n  - Current branch, exact `HEAD`, latest `origin/master`, and `git status --short --branch`.\n  - Previous and target release versions from `CHANGELOG.md`, root `Cargo.toml`, and `frb_dart/pubspec.yaml`.\n  - Final target changelog section, contributor reconciliation result, human changelog approval, and relevant diffs.\n  - Submodule status, credential preflight result, and normal CI green evidence or evidence that every narrow CI-gate exception condition in Step 1 is satisfied, including successful `release_ci_gate.py` output.\n  - Exact commands and results for any additional focused release tests that were run. These tests do not replace the normal CI gate or its defined narrow exception.\n  - Exact one-shot publish command that will run next.\n- Ask the subagent to return one verdict:\n  - `GO`: all release instructions are satisfied and the evidence is internally consistent.\n  - `NO-GO`: list every blocker, missing artifact, ambiguity, or inconsistent result with concrete evidence.\n- Do not tell the subagent the desired verdict or provide only a prepared summary. Let it inspect the repository and raw command outputs independently.\n- Stop before publishing on `NO-GO`, an ambiguous verdict, missing evidence, or unavailable subagent capability. Resolve every blocker and run a fresh independent audit.\n- Treat the audit as valid only for the exact audited `HEAD`, `origin/master`, clean worktree state, evidence snapshot, and next publish command.\n- After a `GO` verdict, run that exact publish command without any intervening mutation. If the command changes, the worktree becomes dirty, evidence is refreshed or invalidated, or `HEAD` or `origin/master` changes, repeat the affected preflight checks and the independent audit.\n\n### 5. Publish\n\nUse the repository release command through a temporary Docker container with publish credentials as the normal publishing path:\n\n```bash\n.claude/skills/frb-dev-env/frb_dev_env.py docker-run-rm --with-publish-credentials -- ./frb_internal release\n```\n\nRun the one-shot command from a clean local `master` that tracks the latest `origin/master`. `release-update-git` uses plain `git push`, so do not run the command from a side release branch.\n\nDo not split the normal release into separate `release-update-*` or publish commands. The one-shot command is the source of truth for release sequencing: it computes old/new versions from `CHANGELOG.md`, updates checked-in versions and generated version text, updates Scoop metadata, commits and pushes the version bump, creates the GitHub release, then runs the registry publisher:\n\n```bash\n.claude/skills/frb-dev-env/frb_dev_env.py docker-run-rm --with-publish-credentials -- ./frb_internal release-publish-all\n```\n\nFor beta versions such as `2.13.0-beta.1`, do not require the GitHub release to be labeled as a pre-release. FRB intentionally publishes beta GitHub releases as normal GitHub releases while the package version itself remains a SemVer prerelease.\n\n`release-publish-all` publishes these packages:\n\n- `frb_codegen` -> crates.io package `flutter_rust_bridge_codegen`\n- `frb_macros` -> crates.io package `flutter_rust_bridge_macros`\n- `frb_rust` -> crates.io package `flutter_rust_bridge`\n- `frb_dart` -> pub.dev package `flutter_rust_bridge`\n- `frb_hooks` -> pub.dev package `flutter_rust_bridge_hooks`\n\nOnly use a split subcommand as a recovery path after confirming which one-shot step already completed. For example, if the version bump and GitHub release already exist and only registry publication is needed, use `.claude/skills/frb-dev-env/frb_dev_env.py docker-run-rm --with-publish-credentials -- ./frb_internal release-publish-all` directly.\n\n### 6. Check Released Versions\n\nPoll registry state with:\n\n```bash\n./frb_internal get-released-version\n```\n\nTo check a target version from a checkout whose manifests have not been bumped, pass it explicitly:\n\n```bash\n./frb_internal get-released-version --version <VERSION>\n```\n\nThe command prints JSON:\n\n```json\n{\n  \"allReleased\": true,\n  \"packages\": [\n    {\n      \"registry\": \"crates.io\",\n      \"name\": \"flutter_rust_bridge_codegen\",\n      \"manifestVersion\": \"2.12.0\",\n      \"releasedVersion\": \"2.12.0\",\n      \"isReleased\": true\n    }\n  ]\n}\n```\n\nWait until every package has `isReleased: true` and `allReleased: true`. If one registry lags, keep polling with bounded waits and record the mismatched package instead of assuming the publish failed. Do not add the target version option to the mutating `release-*` subcommands; they derive their version from `CHANGELOG.md` and checked-in manifests.\n\nFor beta releases, `get-released-version --version <VERSION>` must verify the pub.dev `versions` list, not only pub.dev's `latest` field, because pub.dev keeps `latest` on the latest stable release when a prerelease is uploaded.\n\n### 7. Babysit CI And Post-Release CI\n\n- Keep watching the release commit's normal CI until it is green.\n- After `./frb_internal get-released-version` reports `allReleased: true`, trigger `.github/workflows/post_release.yaml` for the release commit or `master`.\n- Babysit post-release CI until it is green. Use the user's `tom-ci` skill when reading GitHub Actions logs.\n- If post-release fails, classify the failure by release channel (`stable` or `unstable`) and install mode (`cargo-install`, `cargo-binstall`, `scoop`, or `homebrew`) before changing code or rerunning.\n\n## Related Skills\n\n- `frb-write-changelog` for the release section.\n- `frb-fix-ci` or `frb-fix-main-ci` for CI failures.\n- The user's `tom-ci` skill for GitHub Actions logs.\n",".claude/skills/frb-tart-prepare/SKILL.md":"---\nname: frb-tart-prepare\ndescription: Use when preparing or explaining the FRB Tart base VM for iOS Simulator validation, including local OCI registry mirroring, Packer provisioning, and base VM hygiene.\n---\n\n# FRB Tart Base VM Preparation\n\nUse this skill when preparing or explaining the reusable Tart base VM used by `frb-dev-env tart ...`.\n\n## Source Of Truth\n\nRead the checked-in guide first:\n\n```text\ntools/tart_macos/README.md\n```\n\nThat README owns the detailed Tart source image, local OCI registry, Packer build, smoke test, promotion, cost, and base hygiene instructions. Keep this skill thin so the guide remains the audited source of truth for the Tart base VM workflow.\n\n## Required Reminders\n\n- `frb-tart-base` is immutable after promotion; do not boot it and manually install tools into it.\n- Durable base image changes belong in `tools/tart_macos/packer/frb-tart-base.pkr.hcl` or its provision scripts.\n- Use `frb-tart-base-candidate` for rebuilds, smoke test the candidate, then promote it to `frb-tart-base`.\n- For daily per-worktree Tart usage, read the Tart section inside `.claude/skills/frb-dev-env/SKILL.md`.\n",".claude/skills/frb-test/SKILL.md":"---\nname: frb-test\ndescription: Use when needing to run tests locally in flutter_rust_bridge, or when debugging test failures before pushing\n---\n\n# FRB Testing\n\n> **Note:** Read `frb-dev-env` before running commands and follow the active user's environment rules.\n\n## Overview\n\nMaps change types to local test commands. Tests run on CI by default, so local testing is optional but useful for debugging.\n\n## Quick Reference\n\n| Change Type | Command |\n|-------------|---------|\n| All Rust packages | `./frb_internal test-rust` |\n| `frb_rust/` | `./frb_internal test-rust-package --package frb_rust` |\n| `frb_codegen/` | `./frb_internal test-rust-package --package frb_codegen` |\n| Specific Rust package | `./frb_internal test-rust-package --package <path>` |\n| `frb_dart/` | `./frb_internal test-dart-native --package frb_dart` |\n| Dart example | `./frb_internal test-dart-native --package frb_example/<name>` |\n| Flutter example | `./frb_internal test-flutter-native --package frb_example/flutter_<name>` |\n| Web test | `./frb_internal test-dart-web --package frb_example/<name>` |\n\n## Web Tests\n\nWeb tests compile Dart tests to WebAssembly and run in a headless browser.\nSome individual web test cases are flaky; rerun the failed web test job or command before treating the failure as deterministic.\n\n### Supported Packages\n- `frb_dart`\n- `frb_example/dart_minimal`\n- `frb_example/pure_dart`\n- `frb_example/pure_dart_pde`\n\n### Requirements\n- `wasm32-unknown-unknown` target: `rustup target add wasm32-unknown-unknown --toolchain nightly-2025-02-01`\n- `rust-src` component: `rustup component add rust-src --toolchain nightly-2025-02-01`\n- `wasm-pack`: `curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh`\n- Chrome/Chromium browser\n\n### Running in Docker\nWhen running web tests in Docker containers, puppeteer needs `--no-sandbox` flag. The test runner auto-detects Docker environment via `/.dockerenv`.\n\n## When to Test Locally\n\n**Run:** Debugging test failure, developing new tests, verifying fix before push\n\n**Skip:** Documentation only, CI will catch it, just checking compilation\n\n## CI Discipline\n\nCI is slow and often queues for a long time. Before pushing a non-trivial FRB change, run meaningful local tests in the appropriate development environment and understand the result locally first. Scale the local test scope with the risk and blast radius of the change. Do not use CI as the first feedback loop for changes that can be verified locally.\n\nChoose tests by blast radius:\n- Runtime changes: run the relevant Rust package tests\n- Generated or example API changes: run the focused Dart or Flutter example test first\n- Codegen changes: run generation and the affected example tests\n\nAfter pushing to a PR branch, monitor GitHub Actions until the run reaches a terminal state. If CI fails, inspect the failing job logs and either fix the issue or clearly report why it is unrelated or flaky. Do not leave a PR in an unknown queued or in-progress state when the user asked for CI validation.\n\nBefore treating a non-trivial PR as ready, run the review gate in `frb-pr-review`; local tests alone do not check correctness review or test weakening.\n",".claude/skills/frb-test/tests/test-application-1.md":"# Test Scenario: Application 1 - Rust Codegen Change\n\n**Type:** Application scenario\n**Skill under test:** frb-test\n\n## Prompt\n\n```\nI just modified some code in frb_codegen/src/library/api_dart/mod.rs to fix a bug in Dart code generation.\nWhich tests should I run locally to verify this change works correctly?\n```\n\n## Expected Behavior\n\n- Correct command: `./frb_internal test-rust-package --package frb_codegen`\n",".claude/skills/frb-test/tests/test-application-2.md":"# Test Scenario: Application 2 - Example API Change\n\n**Type:** Application scenario\n**Skill under test:** frb-test\n\n## Prompt\n\n```\nI added a new function `hello_twin_normal` to frb_example/pure_dart/rust/src/api/misc.rs\nand wrote a test in frb_example/pure_dart/test/api/misc_test.dart.\nThe CI is failing. How can I reproduce and debug this locally?\n```\n\n## Expected Behavior\n\n- Correct command: `./frb_internal test-dart-native --package frb_example/pure_dart`\n",".claude/skills/frb-test/tests/test-gap-1.md":"# Test Scenario: Gap 1 - Flutter Integration Test\n\n**Type:** Gap scenario\n**Skill under test:** frb-test\n\n## Prompt\n\n```\nI modified frb_example/flutter_via_create and want to run the integration tests locally.\nWhat command should I use?\n```\n\n## Expected Behavior\n\n- Correct command: `./frb_internal test-flutter-native --package frb_example/flutter_via_create`\n",".claude/skills/frb-test/tests/test-variation-1.md":"# Test Scenario: Variation 1 - Web Test Request\n\n**Type:** Variation scenario\n**Skill under test:** frb-test\n\n## Prompt\n\n```\nI need to verify my changes work on the web platform.\nHow do I run web tests for frb_dart locally?\n```\n\n## Expected Behavior\n\n- Correct command: `./frb_internal test-dart-web --package frb_dart`\n",".claude/skills/frb-test/tests/test-variation-2.md":"# Test Scenario: Variation 2 - Multiple Packages Changed\n\n**Type:** Variation scenario\n**Skill under test:** frb-test\n\n## Prompt\n\n```\nI made changes to both frb_rust and frb_dart.\nWhat tests should I run to verify everything works?\n```\n\n## Expected Behavior\n\n- Both packages identified\n- Commands:\n  - `./frb_internal test-rust-package --package frb_rust`\n  - `./frb_internal test-dart-native --package frb_dart`\n",".claude/skills/frb-upgrade-docker/SKILL.md":"---\nname: frb-upgrade-docker\ndescription: Upgrade and publish the flutter_rust_bridge development Docker image, including toolchain bumps, image build fixes, candidate tags, and default-branch promotion.\n---\n\n# 1 Scope the PR\n\n- Put every development-image upgrade in an independent PR targeting `master`. Make it the first PR in a toolchain-upgrade chain.\n- Include all Docker-owned changes required by the upgrade:\n  - `.devcontainer/Dockerfile` toolchain and system packages;\n  - `.github/workflows/publish_dev_docker.yaml` build, test, and publication logic;\n  - image metadata tests, tag derivation, and Docker-specific supporting code.\n- Leave FRB runtime fixes, generated scaffolds, and example migrations to later PRs.\n- Read `frb-docker` for local use and `frb-dev-env` before running repository commands in a container.\n\n# 2 Build before merge\n\n- Before pushing the Docker upgrade PR, follow `frb-docker`'s local Dockerfile validation workflow with a freshly\n  built image. Do not validate with a stale per-worktree container.\n- Make Dockerfile and publish-workflow PRs automatically build and smoke-test native `linux/amd64` and `linux/arm64` images with `push: false`.\n- Do not expose registry credentials to untrusted pull-request code.\n- Derive image tags from Dockerfile `ARG` values instead of duplicating versions.\n- Verify Flutter, Dart, Rust, the pinned nightly and wasm target, Node, Yarn, `wasm-pack`, Chrome, ChromeDriver, and `./frb_internal --help`.\n- For a manual build-only check, run:\n\n```shell\ngh workflow run publish_dev_docker.yaml --ref <branch> -f publish=false\n```\n\n# 3 Publish safely\n\n- If later PRs need the image before merge, let a maintainer explicitly publish only an immutable\n  `candidate-pr-<number>-sha-<short_sha>` tag. Reject a conflicting existing tag; allow only an identical retry.\n- Never update `latest`, canonical version tags, or stable SHA tags from pull-request code.\n- Publish stable tags only from a push to `master`. A manual stable dispatch must check out the current remote\n  `master` head, not an older reachable commit.\n- After merge, publish from `master`:\n\n```shell\ngh workflow run publish_dev_docker.yaml --ref master\n```\n\n- Stable publication may then update:\n  - `latest`;\n  - `flutter-<flutter>-rust-<rust>-nightly-<nightly>`;\n  - the version-plus-code and stable SHA tags.\n\n# 4 Verify and hand off\n\n- Inspect the candidate when used, then inspect the canonical tag and `latest` after merge:\n\n```shell\ndocker buildx imagetools inspect fzyzcjy/flutter_rust_bridge_dev:<tag>\ndocker inspect fzyzcjy/flutter_rust_bridge_dev:latest \\\n  --format '{{ index .Config.Labels \"org.opencontainers.image.revision\" }}'\n```\n\n- Require `linux/amd64` and `linux/arm64`; ignore BuildKit attestation entries reported as `unknown/unknown`.\n- Merge latest `master` into the remaining chain, switch it from any candidate to the canonical tag, and do not merge\n  a consumer before that tag exists.\n- Diagnose Docker Hub login, per-platform pushes, and manifest creation separately.\n- If local `latest` is stale, use the exact candidate/canonical tag or build the Dockerfile locally; do not generate\n  locks or scaffolds with the wrong toolchain.\n",".claude/skills/frb-upgrade-flutter/SKILL.md":"---\nname: frb-upgrade-flutter\ndescription: >-\n  Upgrade flutter_rust_bridge to a new Flutter stable release. Use when changing Flutter/Dart versions,\n  devcontainer Docker images, CI/post-release pins, generated Flutter scaffolds, or platform compatibility.\n---\n\n# 1 Start here\n\n- Confirm the target Flutter stable release from official Flutter sources.\n- Read `frb-dev-env` before running setup, generation, lint, or tests. Use its per-worktree local Docker workflow.\n- Read `frb-upgrade-docker` when the target toolchain changes the development image.\n- Read `frb-docker` for ordinary local Docker usage.\n- Read `frb-code-generation` before accepting generated or scaffold drift.\n- Read `frb-cargokit` before changing any copied CargoKit file. Read `frb-cargokit-dev` only when the source\n  change belongs in the external CargoKit repository.\n- Read `frb-pr-chain-split` as soon as an independently landable prerequisite or cleanup appears.\n- Read `frb-prepare-pr` and then `frb-pr-review` before treating the upgrade PR as ready.\n- Read `frb-fix-ci` when CI starts failing.\n\n# 2 Establish the version contract\n\n- Record the target Flutter version, bundled Dart version, release date, and relevant release-note changes.\n- Distinguish the primary Dart version from the minimum supported Dart SDK floor.\n  - Upgrade the primary Dart pin with Flutter.\n  - Do not raise package SDK constraints merely because the primary toolchain is newer.\n  - When retaining an older SDK floor, run dependency resolution, analysis, and tests on that floor in CI.\n- Inventory version-like values without assuming the target Dart major or minor:\n\n```shell\nrg -n \"FRB_MAIN_|FLUTTER_VERSION|DART_VERSION|RUST_VERSION|setup-flutter|setup-dart|cirruslabs/flutter\"\nrg -n \"flutter_rust_bridge_dev|stable|nightly|minimum.*version|deployment.*target\" \\\n  .devcontainer .github tools frb_codegen frb_example\n```\n\n- Inspect at least:\n  - `.devcontainer/Dockerfile`;\n  - `.github/workflows/ci.yaml` and `.github/workflows/post_release.yaml`;\n  - `.github/workflows/publish_dev_docker.yaml`;\n  - `tools/frb_internal/test/src/makefile_dart/test_dev_docker_metadata.dart`;\n  - package `pubspec.yaml` and checked-in `pubspec.lock` files;\n  - `frb_codegen/assets/integration_template/**`;\n  - `tools/frb_internal/assets/apple_scaffold/**`;\n  - `tools/tart_macos/**`;\n  - `frb_example/**`.\n\n# 3 Choose PR boundaries before implementation\n\n- When the development image changes, make its upgrade the first independent predecessor PR targeting `master`.\n  Follow `frb-upgrade-docker` for its contents, candidate image, merge order, and stable-tag promotion.\n- Keep generated snapshots, their generator changes, required tests, and upgrade-specific compatibility migrations in\n  the main upgrade PR.\n- Split independent bug fixes, hardening, and reusable prerequisites into predecessor PRs according to\n  `frb-pr-chain-split`.\n- Build and maintain any predecessor chain exclusively with the official `gh stack` workflow described there.\n- Put `ci-manual-dispatch` on dormant predecessors unless a predecessor specifically needs GitHub-only validation.\n- Do not move incidental skill or workflow cleanup into the upgrade PR when it can stand alone against `master`.\n\n# 4 Upgrade the development toolchain\n\n- Complete the independent Docker predecessor through `frb-upgrade-docker` before relying on the upgraded image.\n- Use its immutable candidate tag for pre-merge validation when necessary; never move `latest` from a PR branch.\n- After the predecessor merges and publishes stable tags, merge latest `master` into the remaining upgrade chain and\n  use the canonical version tag.\n- If Apple pins, simulators, or host tooling change, continue with the Tart workflow routed by `frb-dev-env` and\n  read `frb-tart-prepare` before provisioning or validation.\n\n# 5 Synchronize CI and post-release pins\n\n- Update the top-level toolchain values together in `.github/workflows/ci.yaml` and\n  `.github/workflows/post_release.yaml`:\n  - `FRB_MAIN_FLUTTER_VERSION`;\n  - `FRB_MAIN_DART_VERSION`;\n  - `FRB_MAIN_RUST_VERSION` when the upgraded tooling requires it;\n  - `FRB_RUSTFMT_NIGHTLY_VERSION` only when formatting or nightly `rust-src` behavior requires it.\n- Keep a retained minimum Dart floor explicit and independently exercised; do not substitute the primary Dart pin for\n  that compatibility lane.\n- Review Java, Android SDK/NDK, Chrome/chromedriver, macOS runner, simulator, Windows ARM, and post-release install-mode\n  assumptions.\n\n# 6 Regenerate through owner workflows\n\n- Follow `frb-code-generation` for command selection, convergence, generated-output provenance, legacy scaffold migrations, and OHOS integrate composition.\n- Follow `frb-cargokit` and `frb-cargokit-dev` for CargoKit ownership and synchronization.\n\n# 7 Validate in dependency order\n\n- Use the environment selected by `frb-dev-env`. When the Docker image changed, use the candidate or canonical image\n  selected by `frb-upgrade-docker`; do not reuse a stale per-worktree container.\n- Read `frb-lint` and `frb-test` for exact commands.\n- Validate in this order:\n  1. dev-image metadata and tool versions;\n  2. Dart analysis, tests, and the retained minimum-SDK lane;\n  3. internal generation and second-pass cleanliness;\n  4. integration generation and CargoKit synchronization;\n  5. focused legacy Android and Apple platform builds affected by migrations;\n  6. representative native and web examples.\n- Before creating or updating the PR, run `frb-prepare-pr`.\n- Before declaring it ready, run the independent review gate in `frb-pr-review`.\n\n# 8 Triage CI and finish\n\n- Read `frb-fix-ci` before deep CI debugging.\n- Triage failures in dependency order: environment setup, generation, integration, platform builds, post-release,\n  then coverage or uploads.\n- Compare platform failures with target-Flutter release notes and fresh scaffold output before adding workarounds.\n- Keep full CI on the top upgrade PR. Follow `frb-pr-chain-split` for filtered or deferred predecessor CI.\n- The Docker predecessor must already be merged and stably published through `frb-upgrade-docker`; do not defer image\n  publication until the main Flutter upgrade PR merges.\n\n# 9 PR notes\n\n- Record old and new Flutter and primary Dart versions.\n- Record the retained or raised minimum Dart SDK floor and its validation lane.\n- List generated files, direct Flutter migrator edits, CargoKit changes, and OHOS overlays by provenance.\n- Link predecessor PRs and identify which ones intentionally use manual CI.\n- Record the fresh dev-image tag, exact local validations, generation convergence result, and CI status.\n- Call out any platform-specific follow-up that remains intentionally out of scope.\n",".claude/skills/frb-vmware-windows-prepare/SKILL.md":"---\nname: frb-vmware-windows-prepare\ndescription: Use when preparing the FRB VMware Fusion Windows 11 Arm base VM.\n---\n\n# FRB VMware Windows Base VM Preparation\n\nUse this skill when preparing or explaining the reusable Windows 11 Arm base VM used by `tools/vmware_windows/frb_vmware_windows_env.py`.\n\n## Source Of Truth\n\nRead the checked-in guide first:\n\n```text\ntools/vmware_windows/README.md\n```\n\nThat README owns the detailed Packer, Vagrant, VMware Fusion, proxy, storage, host-installation, daily-use, and troubleshooting instructions. Keep this skill thin so the guide remains the audited source of truth for the Windows VM workflow.\n\n## Required Reminders\n\n- Heavy Windows VM artifacts belong under the per-user `vm_root` configured in\n  `~/.config/flutter_rust_bridge/vmware_windows.json` or `FRB_WINDOWS_VM_ROOT`.\n- Host-side Windows VM downloads and Packer network operations use `host_proxy_url` or\n  `FRB_WINDOWS_HOST_PROXY_URL` only when a local network requires a proxy.\n- Durable base image changes belong in the Packer template or PowerShell provision scripts under `tools/vmware_windows/`, not in manual mutations of a promoted base VM.\n- For daily Windows VM usage, read the Windows VMware section inside `.claude/skills/frb-dev-env/SKILL.md`.\n",".claude/skills/frb-write-changelog/SKILL.md":"---\nname: frb-write-changelog\ndescription: Update flutter_rust_bridge CHANGELOG.md for a new release by collecting merged PRs since the previous version tag, mapping them to the target release section, and formatting entries to match the existing changelog style. Use when writing or refreshing a release section in CHANGELOG.md.\n---\n\n# FRB Write Changelog\n\n## Step 1: Inspect `CHANGELOG.md`\n\nRead the top of `CHANGELOG.md`.\n\n- Confirm the target version section exists, or add it.\n- Keep the existing V2 \"what's new\" header line.\n- Inspect `git diff -- CHANGELOG.md` before editing.\n- Replace only the target section if it already contains a placeholder such as `* TODO`.\n\n## Step 2: Identify the previous release tag\n\nFind the latest release tag before the target version.\n\n```bash\ngit tag --sort=-creatordate | head -n 20\n```\n\nUse that tag as the lower bound for the new changelog entry.\n\n## Step 3: Collect merged PRs\n\nUse GitHub CLI to collect merged PRs after the previous version.\n\n```bash\ngh pr list --state merged --limit 200 --json number,title,author,mergedAt,baseRefName,url\ngh pr view <number> --json number,title,author,url\n```\n\nUse the previous version as the lower bound when deciding which merged PRs belong to the target release.\n\n## Step 4: Filter and normalize\n\nKeep all PRs in merged status that belong to the release range.\n\n- Exclude unmerged PRs.\n- Exclude PRs outside the release range.\n- When refreshing an already-published release, use both the previous release timestamp and the target release tag timestamp so later post-release PRs are not pulled into the published section.\n- Do not filter by target branch.\n- Keep docs, CI, and chore PRs if they are merged in the range.\n- Exclude only all-contributors PRs such as `docs: add <name> as a contributor for code/doc`; do not exclude ordinary documentation PRs whose titles start with `docs: add`.\n\nNormalize titles before writing.\n\n- Rewrite noisy internal titles into concise changelog language.\n- Avoid duplicate summaries for split or \"continued\" PRs.\n- Preserve repo-specific capitalization such as `CI`, `GitHub`, `Flutter`, `Rust`, `DCO`, and `V1`.\n\n## Step 5: Write the section\n\nMatch the existing changelog style.\n\n- Write each item as `* Summary #1234`.\n- Append `(thanks @username)` for every third-party human-authored PR in the release range, including docs, CI, chore, tooling, and feature PRs.\n- When a changelog entry combines local maintainer PRs with a third-party PR, append the third-party thanks to that combined entry.\n- If multiple third-party authors appear in one combined entry, include each author in the same entry.\n- For an ordinary beta release section, place entries with `(thanks @username)` before entries without thanks. Within each group, keep merge order from newest to oldest unless the surrounding section clearly uses another order.\n\nFor a stable release that follows betas of the same version series, use a grouped stable section:\n\n- Put only changes merged after the latest beta release first as ordinary top-level entries. Do not include an older change solely because the beta changelogs omitted it.\n- Then add one top-level parent entry per beta, using the canonical version label such as `* 2.13.0-beta.6`, ordered from the latest beta to the earliest beta.\n- Copy that beta's substantive entries exactly beneath its parent as nested bullets. Preserve wording, PR references, and thanks attribution.\n- Keep the generic \"what's new\" entry only once at the top of the stable section.\n- Never fold a post-latest-beta PR into a copied beta entry. Keep it in the leading group even when its subject is related to a beta entry.\n- Apply thanks-first ordering independently within each copied beta group. In the post-latest-beta group, user-visible-before-internal ordering takes precedence over thanks placement.\n\nFor newly written entries, keep every user-visible feature, fix, behavior change, published-package change, and user-facing documentation PR as its own entry. Do not join such changes with `and` or fold multiple PRs into a shared summary. Combining multiple PRs is allowed only for user-invisible internal work such as CI, tests, agent or release tooling, and code outside published packages. Order the user-visible entries first, then put all user-invisible internal entries at the end of the post-latest-beta group immediately before the first beta parent entry. Copied beta entries remain exact copies even if they predate this rule.\n\nEdit only `CHANGELOG.md`. Do not manually edit generated files for this task.\n\n## Step 6: Review the draft\n\nReview the final diff.\n\n- Confirm the change is limited to the target release section.\n- Confirm wording and ordering match nearby release sections.\n- Do not commit yet. Human review can still change the text, and the package changelog copies must be regenerated from the final root changelog.\n\n## Step 7: Run mechanical verification\n\nRun the changelog verifier after finishing the draft.\n\n```bash\ngh pr list --state merged --limit 200 --json number,title,author,mergedAt,baseRefName,url > /tmp/frb-merged-prs.json\nuv run --script .claude/skills/frb-write-changelog/verify_changelog.py \\\n  --version <VERSION> \\\n  --previous-release-time <PREVIOUS_RELEASE_TIMESTAMP> \\\n  --release-time <TARGET_RELEASE_TIMESTAMP> \\\n  --merged-prs-json /tmp/frb-merged-prs.json\n```\n\nThe verifier checks that:\n\n- PR numbers in the target section are complete, not duplicated, and not unexpected.\n- Third-party thanks authors are complete and not unexpected, including ordinary documentation PR authors. The same author may be thanked on multiple entries.\n- Entries with third-party thanks appear before entries without thanks within each copied beta group.\n- `docs: add <name> as a contributor ...` all-contributors PRs are ignored.\n\nUse `--ignore-pr <NUMBER>` only for a documented intentional exclusion. Use `--extra-local-pr <NUMBER>` for a stacked local maintainer PR that belongs in the changelog but is not present in the merged PR JSON yet. Use `--extra-thanks-author <LOGIN>` only for a verified co-author or contributor credited by a release-range PR even though their source PR falls outside the release range; record the source PR and reason in the release journal.\n\nApply any confirmed fixes, then re-run the verifier.\n\n## Step 8: Ask the user to review ordering\n\nTell the user the changelog draft is complete and ask for a manual review.\n\n- Ask the user to review the wording.\n- Ask the user to review the ordering of entries.\n- Adjust the ordering if the user wants a different presentation from the mechanically collected order.\n\n## Step 9: Re-verify after human edits\n\nRun the mechanical verifier again after the user finishes manual edits.\n\n- Confirm there are no missing, duplicated, or extra PR numbers.\n- Confirm there are no missing or unexpected third-party thanks authors.\n- Confirm entries with third-party thanks appear before entries without thanks within each copied beta group.\n- Apply any confirmed fixes, then do one final diff check.\n\nIf the user explicitly wants an independent review, ask a separate reviewer or subagent to compare the final `CHANGELOG.md` against the same merged PR list.\n\n## Step 10: Regenerate published-package changelogs\n\nRegenerate the published-package changelog copies from the final root `CHANGELOG.md`:\n\n```bash\n./frb_internal generate-internal-readme\n```\n\n- Confirm `frb_dart/CHANGELOG.md` and `frb_hooks/CHANGELOG.md` match the root `CHANGELOG.md`.\n- Commit the root changelog and both generated copies together as one atomic release-preparation change.\n- Do not manually edit either generated package changelog.\n- From the clean post-commit tree, run `./frb_internal generate-internal-readme --set-exit-if-changed` as the final drift gate. Do not run this flag while the changelog draft is still dirty because it checks the entire worktree.\n","AGENTS.md":"CLAUDE.md","CLAUDE.md":"# flutter_rust_bridge Development Rules\n\n## Rules\n\n### Do not manually edit these auto-generated files\n\n| File Pattern | Description |\n|--------------|-------------|\n| `frb_generated.*` | Generated by `frb_codegen` - modify generator logic instead |\n| `*.freezed.dart` | Freezed output |\n| `*.g.dart` | Code generator output |\n\n### PR size and complexity\n\n- When humans describe a PR as large or complex, generated code is excluded.\n- Keep implementations as simple as possible while preserving correctness.\n\n### Feature flags\n\nWhen adding or testing feature flags, read `frb-feature-flag`.\n\n## Skills\n\n- `frb-code-generation` - Which generation commands to run\n- `frb-develop-feature` - New feature development workflow\n- `frb-feature-flag` - Feature flag API surface and enabled/disabled coverage rules\n- `frb-test` - How to run tests\n- `frb-debugging` - Debug code generation issues\n- `frb-lint` - Lint and format checks\n- `frb-manual-test` - Manual regression test reports\n- `frb-prepare-pr` - PR preparation\n- `frb-fix-ci` - CI fixes\n- `frb-fix-main-ci` - Default-branch CI regression triage\n- `frb-docker` - Ordinary Docker/devcontainer usage and local validation\n- `frb-upgrade-docker` - Dev Docker toolchain upgrades, candidate images, and stable publication\n","tools/frb_internal/assets/apple_scaffold/frb_example/flutter_package/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md":"# Launch Screen Assets\n\nYou can customize the launch screen with your own desired assets by replacing the image files in this directory.\n\nYou can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images.","tools/frb_internal/assets/apple_scaffold/frb_example/flutter_package_native_assets/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md":"# Launch Screen Assets\n\nYou can customize the launch screen with your own desired assets by replacing the image files in this directory.\n\nYou can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images.","tools/frb_internal/assets/apple_scaffold/frb_example/flutter_via_create/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md":"# Launch Screen Assets\n\nYou can customize the launch screen with your own desired assets by replacing the image files in this directory.\n\nYou can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images.","tools/frb_internal/assets/apple_scaffold/frb_example/flutter_via_create_native_assets/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md":"# Launch Screen Assets\n\nYou can customize the launch screen with your own desired assets by replacing the image files in this directory.\n\nYou can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images.","tools/frb_internal/assets/apple_scaffold/frb_example/flutter_via_integrate/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md":"# Launch Screen Assets\n\nYou can customize the launch screen with your own desired assets by replacing the image files in this directory.\n\nYou can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images.","tools/frb_internal/assets/apple_scaffold/frb_example/flutter_via_integrate_native_assets/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md":"# Launch Screen Assets\n\nYou can customize the launch screen with your own desired assets by replacing the image files in this directory.\n\nYou can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images.","tools/frb_internal/test/README.md":"This is only used for internal development, thus have no tests on its own.","tools/tart_macos/README.md":"# FRB Tart Base VM Preparation\n\nUse this skill when preparing the reusable Tart base VM used by `frb-dev-env tart ...`.\n\n## Model\n\nThe reusable base VM is a clean local VM built by Packer from a pinned Tart OCI artifact:\n\n```text\nghcr.io/cirruslabs/macos-sonoma-xcode:16.1\n  -> optional local OCI registry mirror\n  -> packer build ... frb-tart-base-candidate\n  -> smoke test candidate\n  -> promote candidate to frb-tart-base\n  -> tart clone frb-tart-base frb-tart-<worktree-hash>\n```\n\n`frb-tart-base` is the prepared base image. Do not use it for development, do not run tests in it, and do not manually install tools into it. Treat it as immutable after creation. Per-worktree VMs are cloned from it via APFS copy-on-write and can be dirtied, stopped, deleted, and recreated.\n\n## Why Not Mutate the Base VM\n\nDo not boot `frb-tart-base` and manually install Flutter, Android SDK packages, CocoaPods updates, Rust targets, or other dependencies into it.\n\nManual changes make the base hard to reproduce and hard to audit. If the base needs different tools, update the Packer template or provision script next to this skill, document the exact source, rebuild a candidate, smoke test it, and then promote it to `frb-tart-base`. The base VM should answer both questions: \"which OCI artifact did this come from?\" and \"which provision script changed it?\"\n\n## Source Image\n\nUse a pinned macOS/Xcode OCI reference. The current known-good source is:\n\n```text\nghcr.io/cirruslabs/macos-sonoma-xcode:16.1\n```\n\nAvoid `latest` tags. Avoid broad multi-Xcode runner images unless there is a specific reason; they are larger and less reproducible.\n\n## Fast Path When Network Is Good\n\nIf network access to GHCR is reliable enough, Packer can clone the raw source image directly:\n\n```bash\ncd tools/tart_macos/packer\npacker init .\npacker build \\\n  -var 'source_vm=ghcr.io/cirruslabs/macos-sonoma-xcode:16.1' \\\n  -var 'target_vm=frb-tart-base-candidate' \\\n  -var 'flutter_version=3.47.0' \\\n  -var 'host_proxy_url=http://192.168.64.1:7897' \\\n  -var 'allow_insecure=false' \\\n  .\n```\n\nUse this only when the direct download path is acceptable. Packer delegates the source VM clone to Tart, so shell proxy environment variables may not control it the way they control CLI tools like `curl` or `crane`.\n\nLeave `host_proxy_url` empty when the Tart guest has reliable direct network access. When using a host proxy, enable LAN access so the mixed proxy port listens on `*:7897`, then use `http://192.168.64.1:7897` from Tart guests. Verify it from a running Tart VM before a long base build:\n\n```bash\ntart exec <vm-name> /bin/zsh -lc 'curl -I --proxy http://192.168.64.1:7897 --max-time 10 https://pub.dev'\n```\n\nThere are two separate network paths during a base rebuild:\n\n- Source VM clone: Tart/Packer runs on the host and fetches `source_vm`. Use the local registry path below when `tart clone ghcr.io/...` is unreliable.\n- Guest provisioning: `scripts/provision-frb-tart-base.sh` runs inside the candidate VM. `host_proxy_url` only applies to this guest provisioning phase, including Flutter, Homebrew, Rust, Cargo, and Dart downloads.\n\n## Controlled Local Registry Path\n\nUse this path when direct `tart clone ghcr.io/...` is slow, unreliable, or hard to route through the desired proxy. The idea is:\n\n1. Run a local OCI registry on `127.0.0.1:5000`.\n2. Use `crane copy` with explicit proxy environment variables to mirror the remote Tart OCI artifact into the local registry.\n3. Run Packer with `allow_insecure=true` and `source_vm=127.0.0.1:5000/...` to create `frb-tart-base-candidate`.\n4. Smoke test the candidate.\n5. Promote it to `frb-tart-base`.\n\n### Start Local Registry\n\nUse a large disk for registry storage:\n\n```bash\nmkdir -p /Volumes/MyExternal/temp/frb-mobile-registry\n\ndocker run -d --name frb-mobile-registry \\\n  -p 127.0.0.1:5000:5000 \\\n  -v /Volumes/MyExternal/temp/frb-mobile-registry:/var/lib/registry \\\n  registry:2\n```\n\nVerify it:\n\n```bash\nNO_PROXY=127.0.0.1,localhost,::1 no_proxy=127.0.0.1,localhost,::1 \\\n  curl -fsS http://127.0.0.1:5000/v2/\n```\n\nUse `127.0.0.1`, not `localhost`, to avoid IPv6 or host service surprises.\n\n### Mirror OCI Artifact\n\nMirror with `crane`. If a proxy is needed, set it explicitly for this command:\n\n```bash\nPROXY_URL=http://localhost:7897\n\nHTTP_PROXY=$PROXY_URL HTTPS_PROXY=$PROXY_URL \\\nhttp_proxy=$PROXY_URL https_proxy=$PROXY_URL \\\nALL_PROXY=$PROXY_URL all_proxy=$PROXY_URL \\\nNO_PROXY=localhost,127.0.0.1,::1 no_proxy=localhost,127.0.0.1,::1 \\\n  crane --insecure copy --jobs 4 \\\n  ghcr.io/cirruslabs/macos-sonoma-xcode:16.1 \\\n  127.0.0.1:5000/cirruslabs/macos-sonoma-xcode:16.1\n```\n\nFor long mirrors, run it in `screen` and write logs to disk:\n\n```bash\nPROXY_URL=http://localhost:7897\n\nscreen -dmS frb-mobile-crane-copy zsh -lc \"HTTP_PROXY=$PROXY_URL HTTPS_PROXY=$PROXY_URL http_proxy=$PROXY_URL https_proxy=$PROXY_URL ALL_PROXY=$PROXY_URL all_proxy=$PROXY_URL NO_PROXY=localhost,127.0.0.1,::1 no_proxy=localhost,127.0.0.1,::1 crane --insecure copy --jobs 4 ghcr.io/cirruslabs/macos-sonoma-xcode:16.1 127.0.0.1:5000/cirruslabs/macos-sonoma-xcode:16.1 >> /Volumes/MyExternal/temp/frb-mobile-crane-copy.log 2>&1\"\n```\n\nCheck that `crane` only connects to the intended local proxy, if any, and local registry:\n\n```bash\nPROXY_PORT=7897\n\nlsof -nP -p <crane-pid> | rg \"TCP|127\\\\.0\\\\.0\\\\.1|:${PROXY_PORT}|:5000|ghcr|github\"\n```\n\nExpected network shape:\n\n- `127.0.0.1:<ephemeral> -> 127.0.0.1:<selected-proxy-port>` for remote GHCR fetches through the selected proxy.\n- `127.0.0.1:<ephemeral> -> 127.0.0.1:5000` for local registry writes.\n- No other proxy ports unless they were intentionally selected.\n\n### Verify Local Registry Digest\n\nUse an empty Docker config if the host credential helper interferes:\n\n```bash\nmkdir -p /private/tmp/frb-empty-docker-config\n\nDOCKER_CONFIG=/private/tmp/frb-empty-docker-config \\\nNO_PROXY=localhost,127.0.0.1,::1 no_proxy=localhost,127.0.0.1,::1 \\\n  crane --insecure digest 127.0.0.1:5000/cirruslabs/macos-sonoma-xcode:16.1\n```\n\nKnown digest for the current source image:\n\n```text\nsha256:f181b76eede9acd7a6db76438a4cf73e76550c3f9e8104aed16347122e132184\n```\n\n## Build Base VM With Packer\n\nBuild a candidate base VM from the local registry:\n\n```bash\ncd tools/tart_macos/packer\npacker init .\npacker build \\\n  -var 'source_vm=127.0.0.1:5000/cirruslabs/macos-sonoma-xcode:16.1' \\\n  -var 'target_vm=frb-tart-base-candidate' \\\n  -var 'flutter_version=3.47.0' \\\n  -var 'host_proxy_url=http://192.168.64.1:7897' \\\n  -var 'allow_insecure=true' \\\n  .\n```\n\nThe Packer template clones the raw source VM, boots it as `admin/admin`, runs `scripts/provision-frb-tart-base.sh`, and leaves a stopped local VM named by `target_vm`. The current provision step initializes Xcode, checks out the requested Flutter SDK version, installs CocoaPods with Homebrew, installs Rust matching the devcontainer, adds the `aarch64-apple-ios-sim` and `x86_64-apple-ios` Rust targets for both the pinned toolchain and the `stable-aarch64-apple-darwin` toolchain used by CargoKit, and pre-caches Flutter iOS and macOS artifacts. If `host_proxy_url` is set, the provision step receives standard `HTTP_PROXY` / `HTTPS_PROXY` / `ALL_PROXY` / `CARGO_HTTP_PROXY` environment variables. It also verifies the image contains Xcode and simulator tooling.\n\nKeep `flutter_version` in sync with `FRB_MAIN_FLUTTER_VERSION` in `.github/workflows/ci.yaml` and the Flutter version in `.devcontainer/Dockerfile`. Do not rely on the source Tart OCI image's preinstalled Flutter version, because Flutter template drift can change generated scaffold files.\n\nVerify the candidate:\n\n```bash\ntart list\ntart get frb-tart-base-candidate --format json\n```\n\nDo not boot and mutate `frb-tart-base` during verification. If you need to inspect runtime state, clone a disposable probe VM from the candidate:\n\n```bash\ntart clone frb-tart-base-candidate frb-tart-probe\ntart run --no-graphics frb-tart-probe\ntart ip frb-tart-probe --wait 180\ntart exec frb-tart-probe /bin/zsh -lc 'sw_vers && xcodebuild -version && flutter --version && flutter --version --machine | python3 -c '\"'\"'import json,sys; assert json.load(sys.stdin)[\"frameworkVersion\"] == \"3.47.0\"'\"'\"' && pod --version && rustc --version && cargo --version && rustup target list --installed | grep aarch64-apple-ios-sim && rustup target list --installed | grep x86_64-apple-ios && rustup target list --toolchain stable-aarch64-apple-darwin --installed | grep aarch64-apple-ios-sim && rustup target list --toolchain stable-aarch64-apple-darwin --installed | grep x86_64-apple-ios'\ntart stop frb-tart-probe\ntart delete frb-tart-probe\n```\n\nAfter the candidate passes smoke, promote it by moving the old base out of the way and renaming the candidate:\n\n```bash\nstamp=$(date +%Y%m%d-%H%M%S)\ntart rename frb-tart-base \"frb-tart-base-before-packer-$stamp\"\ntart rename frb-tart-base-candidate frb-tart-base\n```\n\nIf there is no existing `frb-tart-base`, only the second rename is needed.\n\n## Cost Notes\n\nLocal VM-to-VM cloning is cheap because Tart uses APFS copy-on-write for local clones. `du` may count shared blocks and look large, so prefer `df` available space when measuring real disk pressure.\n\nEven though local clone is cheap, keep a conservative free-space threshold before creating worktree VMs. `frb-dev-env tart create` requires at least 150GB free by default because base image setup, caches, build products, and simulator state can grow quickly.\n","tools/vmware_windows/README.md":"# VMware Windows local development environment\n\nThis directory contains the VMware Fusion + Vagrant + Packer workflow for local Windows 11 Arm\nvalidation of `flutter_rust_bridge`.\n\nUse this path only for Windows desktop validation. Keep using Docker for Linux/headless checks, Tart\nfor macOS/iOS checks, and the host Android Emulator workflow for Android runtime checks.\n\nConfigure a local storage root before creating or building VMs. The helper reads:\n\n1. CLI flags, where available.\n2. Environment variables such as `FRB_WINDOWS_VM_ROOT`.\n3. The user config file, defaulting to `~/.config/flutter_rust_bridge/vmware_windows.json`.\n\nExample config:\n\n```json\n{\n  \"vm_root\": \"/path/to/frb-windows-vmware\",\n  \"host_proxy_url\": \"http://localhost:PORT\",\n  \"iso_path\": \"/path/to/frb-windows-vmware/downloads/iso/Windows11_Arm64.iso\",\n  \"iso_sha256\": \"sha256:<digest>\",\n  \"vm_gui\": true\n}\n```\n\nDo not store long-lived Windows VM bundles or boxes on the internal disk. When the storage root is\nunder `/Volumes/<name>/`, the helper refuses\nto create directories unless that volume is actually mounted, preventing accidental writes to the\ninternal disk if an external drive is absent.\n\nIf host-side Windows VM downloads and Packer network operations need a proxy, set:\n\n```bash\nexport FRB_WINDOWS_HOST_PROXY_URL=http://localhost:PORT\n```\n\nLeave `host_proxy_url` unset when direct network access is preferred.\n\n## Model\n\nThe reusable base is built by Packer from a Windows 11 Arm ISO and then consumed by Vagrant on top\nof VMware Fusion:\n\n```text\nWindows 11 Arm ISO\n  -> packer build ... frb-windows11-arm64.box\n  -> vagrant box add frb/windows11-arm64 <box>\n  -> vagrant up per-worktree VM under the configured VM root\n```\n\nThe base box is immutable after promotion. Do not manually install Flutter, Rust, Visual Studio\nBuild Tools, or other dependencies into the promoted base and then forget to update Packer. Manual\nexperiments are allowed only as discovery; every durable base change must be reflected in the Packer\ntemplate or provision scripts.\n\nUse `tools/manual_tests/frb-local-vmware-windows-dev-env.md` for audit-ready local validation and\nexecution records.\n\n## Quick Start\n\n```bash\ntools/vmware_windows/frb_vmware_windows_env.py init-root\ntools/vmware_windows/frb_vmware_windows_env.py check-host\ntools/vmware_windows/frb_vmware_windows_env.py self-test\ntools/vmware_windows/frb_vmware_windows_env.py packer-init\ntools/vmware_windows/frb_vmware_windows_env.py prepare-iso \\\n  --iso /path/to/frb-windows-vmware/downloads/iso/Windows11_Arm64.iso\ntools/vmware_windows/frb_vmware_windows_env.py packer-build\ntools/vmware_windows/frb_vmware_windows_env.py vagrant-box-add \\\n  --box-file /path/to/frb-windows-vmware/packer/boxes/frb-windows11-arm64.box\ntools/vmware_windows/frb_vmware_windows_env.py start\ntools/vmware_windows/frb_vmware_windows_env.py upload\ntools/vmware_windows/frb_vmware_windows_env.py test-flutter-windows --package frb_example/flutter_via_create\n```\n\nFor the initial base-image build on a developer Mac, prefer the Terminal wrapper:\n\n```bash\ntools/vmware_windows/run_packer_build.zsh \\\n  --iso /path/to/frb-windows-vmware/downloads/iso/Windows11_Arm64.iso \\\n  --add-box\n```\n\nThe wrapper uses the same helper, applies the configured host proxy when present, writes a full log\nunder the configured VM root, shows the VMware console by default, and stops the active build if `/`\nor the VM storage root drops below 100GB free.\nWhen a host proxy is configured, it also adds the Packer WinRM guest host to `NO_PROXY` so local VM\ncontrol traffic does not get sent through the host download proxy.\n\nBefore starting the long VMware build, the wrapper checks that the ISO exists, `hdiutil`, `xorriso`,\nPacker, Vagrant, and VMware `vmrun` are available, and the Packer output directory is not already\noccupied. Use `--preflight-only` to run only those wrapper checks without remastering an ISO,\nstarting Packer, or booting VMware.\n\n## Host Installation\n\n`tools/vmware_windows/frb_vmware_windows_env.py check-host` verifies VMware `vmrun`, Vagrant, and\nPacker. The helper itself uses only the Python standard library, so it does not need `uv`, `pip`, or\na first-run Python package download. Packer can be installed with Homebrew. VMware Fusion Pro may\nrequire an authenticated Broadcom/VMware download, and Vagrant's macOS pkg may require an\ninteractive sudo password. Install those tools through their normal macOS flows when non-interactive\nHomebrew installation is blocked.\n\nIf your network requires a proxy for host-side downloads, configure it explicitly before preparing\nthis VM workflow:\n\n```bash\nexport HTTP_PROXY=http://localhost:PORT\nexport HTTPS_PROXY=http://localhost:PORT\nexport http_proxy=http://localhost:PORT\nexport https_proxy=http://localhost:PORT\n```\n\nIf using Homebrew, keep auto-update disabled:\n\n```bash\nHOMEBREW_NO_AUTO_UPDATE=1 brew install --cask vmware-fusion\nHOMEBREW_NO_AUTO_UPDATE=1 brew install --cask vagrant\nHOMEBREW_NO_AUTO_UPDATE=1 brew install --cask vagrant-vmware-utility\nHOMEBREW_NO_AUTO_UPDATE=1 brew install packer\nHOMEBREW_NO_AUTO_UPDATE=1 brew install xorriso\nvagrant plugin install vagrant-vmware-desktop\n```\n\nIn practice, these host installers can require human action:\n\n- `vmware-fusion` may be disabled in Homebrew because Broadcom/VMware requires an authenticated\n  download. Download VMware Fusion Pro manually from the VMware/Broadcom portal, install `VMware\n  Fusion.app`, then verify `/Applications/VMware Fusion.app/Contents/Library/vmrun` exists.\n- `vagrant` is a macOS `.pkg` cask. A non-interactive agent shell can download it, but\n  installation may fail at `/usr/sbin/installer` because `sudo` needs an administrator password.\n  Install it manually with the macOS installer, or run the Homebrew cask command yourself from an\n  interactive terminal.\n- `vagrant-vmware-utility` is also a macOS `.pkg` cask and can require the same interactive\n  administrator installation path.\n- `packer` can be installed non-interactively with Homebrew and should appear as\n  `/opt/homebrew/bin/packer`.\n- `xorriso` is required to remaster the Windows ISO so `Autounattend.xml` is available from the\n  root of the boot media. Install it with `HOMEBREW_NO_AUTO_UPDATE=1 brew install xorriso`.\n- Vagrant's VMware provider requires both the Vagrant VMware Utility and the\n  `vagrant-vmware-desktop` plugin. Install the utility with\n  `brew install --cask vagrant-vmware-utility`, then install the plugin with Vagrant.\n- VMware Fusion 26 provides the Arm64 Windows drivers used during Packer setup at\n  `/Applications/VMware Fusion.app/Contents/Library/isoimages/arm64/drivers-arm64.zip`. The helper\n  injects those drivers into the remastered ISO and installs `vmxnet3.inf` during Windows setup so\n  the guest can obtain a NAT address before Packer waits for WinRM. Set\n  `FRB_VMWARE_FUSION_ARM64_DRIVERS_ZIP` only if Fusion stores the archive somewhere else.\n\nAfter installing VMware Fusion and Vagrant, install the VMware Vagrant provider utility and plugin:\n\n```bash\nHOMEBREW_NO_AUTO_UPDATE=1 brew install --cask vagrant-vmware-utility\nvagrant plugin install vagrant-vmware-desktop\n```\n\nThen re-run:\n\n```bash\ntools/vmware_windows/frb_vmware_windows_env.py check-host\n```\n\nExpected result:\n\n```json\n{\n  \"missing\": [],\n  \"tools\": {\n    \"packer\": \"/opt/homebrew/bin/packer\",\n    \"vagrant\": \"/opt/vagrant/bin/vagrant\",\n    \"vmrun\": \"/Applications/VMware Fusion.app/Contents/Library/vmrun\"\n  }\n}\n```\n\nValidate the host without starting a VM:\n\n```bash\ntools/vmware_windows/frb_vmware_windows_env.py check-host\ntools/vmware_windows/frb_vmware_windows_env.py self-test\ntools/vmware_windows/validate_offline.zsh\n```\n\n`self-test` is a dependency-free helper check. It does not start VMware, Vagrant, Packer, or touch\nthe external VM root; it only validates local helper logic such as path handling, checksum sidecars,\nand guest command argument normalization.\n\n`validate_offline.zsh` is the local no-VM regression check for this directory. It checks Python\nsyntax and helper self-tests, the Packer build wrapper syntax, Vagrantfile syntax when Ruby is\navailable, Packer formatting when Packer is available, website sidebar JavaScript syntax when Node\nis available, and whitespace errors.\n\n## Windows ISO\n\nDownload the Windows 11 Arm ISO from Microsoft's official Arm64 download page:\n\n```text\nhttps://www.microsoft.com/software-download/windows11arm64\n```\n\nThe page generates ISO links dynamically and those links expire. Choose `Windows 11 (multi-edition\nISO for Arm64)`, then choose the language you want to install. For the default English base VM,\nchoose `English`.\n\nPlace the ISO under:\n\n```text\n/path/to/frb-windows-vmware/downloads/iso/Windows11_Arm64.iso\n```\n\nSet `FRB_WINDOWS_ISO` if using another filename. Do not commit ISO files or generated boxes. If\n`Windows11_Arm64.iso` is absent and exactly one non-`autounattend` `.iso` exists in the ISO\ndirectory, the helper and wrapper use that source ISO automatically.\n\nProvide a SHA-256 checksum before a real Packer build. Either set:\n\n```bash\nexport FRB_WINDOWS_ISO_CHECKSUM=sha256:<digest>\n```\n\nor place the digest in:\n\n```text\n/path/to/frb-windows-vmware/downloads/iso/Windows11_Arm64.iso.sha256\n```\n\nFor the current Microsoft Windows 11 Arm64 English ISO, the official verify table lists this\nSHA-256:\n\n```text\n638AA2C88E94385B00F4F178D071E3DF0B7D9E335577A83BD533B7F2EB65ADF0\n```\n\nWrite the checksum file as either the bare digest or `sha256:<digest>`:\n\n```bash\nprintf '%s\\n' '638AA2C88E94385B00F4F178D071E3DF0B7D9E335577A83BD533B7F2EB65ADF0' \\\n  > /path/to/frb-windows-vmware/downloads/iso/Windows11_Arm64.iso.sha256\n```\n\n`packer-build --validate` uses a placeholder checksum only to validate template structure. A real\n`packer-build` fails fast until a checksum is provided.\n\n## Build The Base Box With Packer\n\nInitialize and build from the checked-in template:\n\n```bash\ntools/vmware_windows/frb_vmware_windows_env.py packer-init\ntools/vmware_windows/frb_vmware_windows_env.py prepare-iso\ntools/vmware_windows/frb_vmware_windows_env.py packer-build\n```\n\nThe helper sets `PACKER_CACHE_DIR` under the external root and passes explicit variables for the\nISO, checksum, generated unattended-install file, output directory, and provision scripts. By\ndefault, `packer-build` first remasters the source Windows ISO with `hdiutil` and `xorriso` so\n`Autounattend.xml` is present at the root of a UEFI-bootable install ISO; this matches Windows\nSetup's most reliable unattended-install discovery path under VMware Fusion on Apple Silicon. The\ntemplate also exposes `Autounattend.xml` through both Packer floppy media and a secondary\n`AUTOUNATTEND` CD as a fallback. The template provisions guest command access, OpenSSH, PowerShell\ntooling, Rust, Flutter, CMake/Ninja, and Visual Studio Build Tools.\n\nPacker connects to the freshly installed guest over VMware Fusion NAT WinRM. The helper passes\n`FRB_WINDOWS_PACKER_WINRM_HOST` to the template, defaulting to `172.16.0.128`, which is the first\nlease observed for the generated Packer VM on VMware Fusion NAT. Override this value if another VM\nalready occupies that address or if your Fusion NAT subnet assigns a different first lease:\n\n```bash\nexport FRB_WINDOWS_PACKER_WINRM_HOST=172.16.0.128\n```\n\nWhen a host proxy is configured for downloads, the helper and wrapper add this WinRM host to\n`NO_PROXY` and `no_proxy`. Without that bypass, Packer's WinRM HTTP requests can be routed through\nthe proxy and fail with `http response error: 502 - invalid content type`.\n\nThe remastered ISO is stored next to the source ISO:\n\n```text\n/path/to/frb-windows-vmware/downloads/iso/<source>.frb-autounattend-uefi.iso\n```\n\nUse `prepare-iso --force` only when intentionally regenerating the remastered ISO after changing\n`Autounattend.xml` generation. Use `packer-build --no-remaster-iso` only for low-level\ntroubleshooting of the original Microsoft ISO plus Packer's separate autounattend media.\n\nPacker requires the output directory to be absent before a build starts. The helper removes an empty\nstale output directory automatically, but it stops if that directory contains files so an existing\nbox is not overwritten accidentally.\n\nFor unattended first-run builds from an ordinary macOS Terminal, use:\n\n```bash\ntools/vmware_windows/run_packer_build.zsh \\\n  --iso /path/to/frb-windows-vmware/downloads/iso/Windows11_Arm64.iso \\\n  --add-box\n```\n\nUseful variants:\n\n```bash\ntools/vmware_windows/run_packer_build.zsh --iso /path/to/Windows11_Arm64.iso\ntools/vmware_windows/run_packer_build.zsh --iso /path/to/Windows11_Arm64.iso --preflight-only\ntools/vmware_windows/run_packer_build.zsh --iso /path/to/Windows11_Arm64.iso --prepare-only\ntools/vmware_windows/run_packer_build.zsh --iso /path/to/Windows11_Arm64.iso --headless\ntools/vmware_windows/run_packer_build.zsh --iso /path/to/Windows11_Arm64.iso --no-force-prepare-iso\ntools/vmware_windows/run_packer_build.zsh --iso /path/to/Windows11_Arm64.iso --add-box --force-box\n```\n\nThe wrapper is intentionally thin: it calls `init-root`, `check-host`, `packer-init`, `prepare-iso`,\n`packer-build`, and optionally `vagrant-box-add`. It exists to keep the proxy, log path, external\nstorage root, preflight checks, and disk-space guard consistent during the long VMware build. Use\n`--preflight-only` to validate host readiness without starting the build; use `--prepare-only` to\nvalidate ISO remastering before starting VMware.\n\nIf the first unattended install fails, keep the full Packer log under `logs/`, record the exact\nfailure in the execution artifact, fix the template or scripts, and rerun. Do not fix the base by\nbooting it and manually installing tools without updating the source files.\n\nAfter Packer produces a box, import it with:\n\n```bash\ntools/vmware_windows/frb_vmware_windows_env.py vagrant-box-add \\\n  --box-file /path/to/frb-windows-vmware/packer/boxes/frb-windows11-arm64.box\n```\n\nThe default box name is `frb/windows11-arm64`. Override with `FRB_WINDOWS_VAGRANT_BOX` only when\nintentionally testing another base.\n\nThe base VM installs Flutter 3.44.1, Dart 3.12.1, Rust stable, Git, CMake, Ninja, and Visual Studio\nBuild Tools with the C++ workload. Keep this list synchronized with\n`provision/install-frb-toolchain.ps1` when the repository raises its Windows toolchain floor.\n\n## Daily Use\n\nRun from the repository root:\n\n```bash\ntools/vmware_windows/frb_vmware_windows_env.py info\ntools/vmware_windows/frb_vmware_windows_env.py start\ntools/vmware_windows/frb_vmware_windows_env.py upload\ntools/vmware_windows/frb_vmware_windows_env.py exec -- powershell -NoProfile \\\n  -Command \"cd C:\\frb\\worktree; flutter --version\"\ntools/vmware_windows/frb_vmware_windows_env.py stop\n```\n\nThe Vagrantfile starts VMware Fusion with a visible GUI by default because VMware Fusion 26 on Apple\nSilicon can power off this Windows Arm guest shortly after boot in `nogui` mode. Set\n`FRB_WINDOWS_VM_GUI=false` only when explicitly revalidating headless startup on a newer Fusion or\nprovider combination.\n\n`upload` creates a zip archive of the host checkout, uploads it through Vagrant's WinRM\ncommunicator, and expands it into a VM-local directory before heavy builds. Do not compile from a\nVMware shared folder because shared folders are slower for many small files, can distort build\nbehavior, and are sensitive to Vagrant VMware provider compatibility.\n\nAfter the VM is running and code is uploaded:\n\n```bash\ntools/vmware_windows/frb_vmware_windows_env.py test-flutter-windows \\\n  --package frb_example/flutter_via_create\n```\n\n## Credentials\n\nDo not put guest passwords in the repository. Use one of:\n\n```text\nFRB_WINDOWS_VM_USERNAME\nFRB_WINDOWS_VM_PASSWORD\n~/.secrets/FRB_WINDOWS_VM_PASSWORD\n```\n\nWhen a password is required, source it in the shell or pass it through the environment. Do not write\nit into command logs.\n\nPassword resolution order is `FRB_WINDOWS_VM_PASSWORD`, then `~/.secrets/FRB_WINDOWS_VM_PASSWORD`,\nthen the helper-generated fallback file.\n\nIf `FRB_WINDOWS_VM_PASSWORD` is unset, the helper generates a local per-root fallback password at:\n\n```text\n/path/to/frb-windows-vmware/credentials/guest-password.txt\n```\n\nTreat that file as local machine state. It is not part of the repository, and the helper passes it\nto both Packer and Vagrant through environment variables.\n\n## Cleanup\n\nStop the per-worktree VM when it is not needed:\n\n```bash\ntools/vmware_windows/frb_vmware_windows_env.py stop\n```\n\nDelete disposable per-worktree VMs only when intentionally reclaiming space:\n\n```bash\ntools/vmware_windows/frb_vmware_windows_env.py destroy\n```\n\nKeep the Packer-built base box and external caches unless rebuilding the Windows base.\n\n## Layout\n\n```text\ntools/vmware_windows/\n├── Vagrantfile\n├── frb_vmware_windows_env.py\n├── run_packer_build.zsh\n├── packer/\n│   ├── windows11-arm64.pkr.hcl\n└── provision/\n    ├── expand-uploaded-worktree.ps1\n    ├── install-frb-toolchain.ps1\n    └── prepare-guest-control.ps1\n```\n\nThe checked-in scripts are source of truth. Manual changes inside a Windows base VM should be\ntreated as experiments and then translated back into Packer or provision scripts.\n\n## Troubleshooting\n\n### VMware Cancels VM Startup\n\nIf `packer-build` reaches `Powering on virtual machine...` and then fails with:\n\n```text\nerror starting virtual machine: ... Error: The operation was canceled\n```\n\nopen VMware Fusion once from `/Applications`, finish any first-run prompts, accept the license, and\napprove any macOS privacy, network, or helper permissions it requests. Then verify the command-line\nbridge still works:\n\n```bash\n/Applications/VMware\\ Fusion.app/Contents/Library/vmrun -T fusion list\nlaunchctl print system/com.vagrant.vagrant-vmware-utility\n```\n\nAfter those commands work without permission dialogs, rerun:\n\n```bash\ntools/vmware_windows/frb_vmware_windows_env.py packer-build \\\n  --iso /path/to/frb-windows-vmware/downloads/iso/Windows11_Arm64.iso\n```\n\nIf the VM still cancels immediately in headless mode, run once with `--gui` so VMware Fusion can\nshow any remaining prompt:\n\n```bash\ntools/vmware_windows/frb_vmware_windows_env.py packer-build --gui \\\n  --iso /path/to/frb-windows-vmware/downloads/iso/Windows11_Arm64.iso\n```\n\nIf VMware Fusion reports that the `lsilogic` device type for `scsi0` is not supported, keep the\nPacker template on the checked-in NVMe disk adapter and `pvscsi` VMX override. To preserve the\nfailed VM directory and inspect `vmware.log`, rerun with:\n\n```bash\ntools/vmware_windows/frb_vmware_windows_env.py packer-build --gui --on-error=abort \\\n  --iso /path/to/frb-windows-vmware/downloads/iso/Windows11_Arm64.iso\n```\n\nIf the VM reaches UEFI Boot Manager instead of Windows Setup, the Packer boot command must select\nthe SATA CDROM entry and send boot keys during the Windows ISO's short prompt window before WinRM\ncan ever become available. Keep the checked-in boot command unless a future VMware Fusion release\nchanges the Boot Manager entry order.\n"},"items":[{"name":"AGENTS.md","path":"AGENTS.md","rawUrl":"https://raw.githubusercontent.com/fzyzcjy/flutter_rust_bridge/HEAD/AGENTS.md","title":"AI Agent Protocol & Instructions","category":"root-instruction","format":"markdown","content":"CLAUDE.md","isInternal":false,"tokens":4,"sizeBytes":9},{"name":"CLAUDE.md","path":"CLAUDE.md","rawUrl":"https://raw.githubusercontent.com/fzyzcjy/flutter_rust_bridge/HEAD/CLAUDE.md","title":"Claude Agent Guidelines & System Prompt","category":"claude-rule","format":"markdown","content":"# flutter_rust_bridge Development Rules\n\n## Rules\n\n### Do not manually edit these auto-generated files\n\n| File Pattern | Description |\n|--------------|-------------|\n| `frb_generated.*` | Generated by `frb_codegen` - modify generator logic instead |\n| `*.freezed.dart` | Freezed output |\n| `*.g.dart` | Code generator output |\n\n### PR size and complexity\n\n- When humans describe a PR as large or complex, generated code is excluded.\n- Keep implementations as simple as possible while preserving correctness.\n\n### Feature flags\n\nWhen adding or testing feature flags, read `frb-feature-flag`.\n\n## Skills\n\n- `frb-code-generation` - Which generation commands to run\n- `frb-develop-feature` - New feature development workflow\n- `frb-feature-flag` - Feature flag API surface and enabled/disabled coverage rules\n- `frb-test` - How to run tests\n- `frb-debugging` - Debug code generation issues\n- `frb-lint` - Lint and format checks\n- `frb-manual-test` - Manual regression test reports\n- `frb-prepare-pr` - PR preparation\n- `frb-fix-ci` - CI fixes\n- `frb-fix-main-ci` - Default-branch CI regression triage\n- `frb-docker` - Ordinary Docker/devcontainer usage and local validation\n- `frb-upgrade-docker` - Dev Docker toolchain upgrades, candidate images, and stable publication\n","isInternal":false,"tokens":309,"sizeBytes":1267},{"name":"SKILL.md","path":".claude/skills/frb-add-contributor/SKILL.md","rawUrl":"https://raw.githubusercontent.com/fzyzcjy/flutter_rust_bridge/HEAD/.claude/skills/frb-add-contributor/SKILL.md","title":"frb-add-contributor","category":"anthropic-skill","format":"markdown","content":"---\nname: frb-add-contributor\ndescription: Add or reconcile flutter_rust_bridge contributors through all-contributors PRs. Use when finding contributor PRs, posting all-contributors comments, validating contributor metadata, resolving contributor PR merge conflicts, or merging the generated contributor PRs.\n---\n\n# FRB Add Contributor\n\nUse this skill when contributors need to be added to `flutter_rust_bridge` via `all-contributors`, especially when multiple recent PR authors should be credited in batch.\n\n## Step 1: Find the correct source PRs\n\nFor each GitHub username, find their PRs in `fzyzcjy/flutter_rust_bridge`.\n\n```bash\ngh search prs --repo fzyzcjy/flutter_rust_bridge --author <username> --limit 100 --json number,title,author,url,state\n```\n\nImportant:\n\n- Deduplicate by contributor. A single person may have multiple merged PRs.\n- If the task is about `new contributor PR`s, use the contributor's first PR only.\n- For release reconciliation, inspect every third-party human-authored PR in the same release range used by `frb-write-changelog`.\n- For each such PR, confirm that the contributor's human-written `.all-contributors-custom.yaml` summary covers that contribution. Being present in `.all-contributorsrc` or `README.md` is not sufficient.\n- Treat an existing contributor whose summary does not cover a release-range PR as needing reconciliation, but do not expect a new all-contributors PR for that person.\n- If the contributor already exists in `.all-contributorsrc` or `README.md`, do not expect a new all-contributors PR to be created.\n\n## Step 2: Stop for human confirmation\n\nAfter determining which contributors may need to be added or have their custom descriptions extended, stop and ask a human to confirm the contributor list and source PRs before taking any mutating action.\n\nThis stop is mandatory.\n\n- Do not edit `.all-contributors-custom.yaml`.\n- Do not regenerate contributor artifacts.\n- Do not post all-contributors comments.\n- Do not create or merge any PRs.\n- Do not perform any other GitHub mutation.\n\nOnly continue after the human explicitly confirms which contributors to add or update and which source PRs those changes cover.\n\n## Step 3: Prepare `.all-contributors-custom.yaml` and stop for human input\n\nBefore regenerating contributor artifacts, append each new contributor to the end of `.all-contributors-custom.yaml`:\n\n```yaml\n- <username>: TODO\n```\n\nDo this only for contributors who are not already present in `.all-contributors-custom.yaml`.\n\nFor an existing credited contributor with no custom entry, append:\n\n```yaml\n- <username>: TODO(#<pr-number>)\n```\n\nFor an existing contributor whose summary does not cover a release-range PR, preserve the existing human-written text and append a marker for each uncovered PR:\n\n```yaml\n- <username>: <existing human-written summary> TODO(#<pr-number>)\n```\n\nThen stop and ask a human to replace each `TODO` or `TODO(#<pr-number>)` with a concise contribution summary.\n\nThis stop is mandatory.\n\n- Do not continue to Step 3 or any later step in this skill until the human has replaced every `TODO`.\n- Do not run contributor generation.\n- Do not post all-contributors comments.\n- Do not resolve or merge contributor PRs.\n\nOnly resume after the human-written descriptions are present in `.all-contributors-custom.yaml`.\n\n## Step 4: Trigger all-contributors\n\nPost the comment on the chosen source PR:\n\n```bash\ngh pr comment <pr-number> --repo fzyzcjy/flutter_rust_bridge --body '@all-contributors please add <username> for code'\n```\n\nThen inspect the bot response on that PR.\n\n- Skip this step for an existing contributor whose only gap was an incomplete custom description.\n- If the bot says the user already contributed before, stop there for that user.\n- Otherwise, wait for the bot to open a PR named `docs: add <username> as a contributor for code`.\n\n## Step 5: Validate contributor metadata\n\nWhen a contributor PR exists, check the generated contributor data before merging.\n\nFiles to inspect:\n\n- `.all-contributorsrc`\n- `README.md`\n\nRequired validation:\n\n- Homepage URLs must use `https://`, never `http://`.\n- If the generated homepage is `http://`, change it to `https://` if the site supports it.\n- If the personal site does not support `https://`, prefer the contributor's GitHub profile URL instead of keeping `http://`.\n\n## Step 6: Regenerate contributor output\n\nAfter the human has replaced all `TODO` markers, confirm that the custom summaries cover every third-party release-range PR. Then regenerate the contributor artifacts instead of hand-editing `README.md`.\n\nUse:\n\n```bash\n./frb_internal generate-internal-contributor\n```\n\nThis command updates:\n\n- `.all-contributorsrc`\n- `README.md` via `all-contributors generate`\n- `website/docs/index.md` via `generate-internal-readme`\n\nIf you only need to refresh the website copy of the README, use:\n\n```bash\n./frb_internal generate-internal-readme\n```\n\n## Step 7: Resolve merge conflicts in contributor PRs\n\nThese PRs usually conflict only in:\n\n- `.all-contributorsrc`\n- `README.md`\n\nRecommended workflow for each open contributor PR:\n\n```bash\ngh pr checkout <pr-number> --repo fzyzcjy/flutter_rust_bridge\ngit fetch origin master\ngit merge --no-ff origin/master\n```\n\nIf the merge conflicts:\n\n1. Keep the latest `origin/master` content.\n2. Re-apply the contributor entry from the bot branch.\n3. Ensure the contributor card remains present in `README.md`.\n4. Re-run `./frb_internal generate-internal-contributor` if you edited contributor metadata or finished filling in `.all-contributors-custom.yaml`.\n5. `git add` the resolved files and commit the merge.\n6. Push the contributor branch back to origin.\n\nDo not revert unrelated contributor entries that were merged by other PRs.\n\n## Step 8: Merge the contributor PRs\n\nAfter the branch is updated and GitHub reports it mergeable:\n\n```bash\ngh pr merge <pr-number> --repo fzyzcjy/flutter_rust_bridge --merge --delete-branch\n```\n\nNotes:\n\n- GitHub may temporarily report `DIRTY` or reject merge immediately after push.\n- If that happens, wait briefly, re-check the PR state, and retry the merge.\n- After merging one contributor PR, fetch `origin/master` again before resolving the next one.\n\n## Step 9: Final verification\n\nAt the end, verify there are no remaining open all-contributors PRs:\n\n```bash\ngh pr list --repo fzyzcjy/flutter_rust_bridge --search \"author:allcontributors[bot] is:open\" --limit 50 --json number,title,mergeStateStatus,url\n```\n\nAlso verify the credited contributors appear in:\n\n- `.all-contributorsrc`\n- `README.md`\n- `website/docs/index.md` if contributor regeneration was run\n","frontmatter":{"name":"frb-add-contributor","description":"Add or reconcile flutter_rust_bridge contributors through all-contributors PRs. Use when finding contributor PRs, posting all-contributors comments, validating contributor metadata, resolving contributor PR merge conflicts, or merging the generated contributor PRs."},"isInternal":false,"tokens":1469,"sizeBytes":6596},{"name":"SKILL.md","path":".claude/skills/frb-android-emulator-prepare/SKILL.md","rawUrl":"https://raw.githubusercontent.com/fzyzcjy/flutter_rust_bridge/HEAD/.claude/skills/frb-android-emulator-prepare/SKILL.md","title":"frb-android-emulator-prepare","category":"anthropic-skill","format":"markdown","content":"---\nname: frb-android-emulator-prepare\ndescription: Use when preparing, installing, diagnosing, or explaining the host Android Emulator environment for flutter_rust_bridge local runtime validation, including Android SDK command-line tools, emulator packages, AVD creation, host emulator startup, and Docker-local ADB connectivity to the emulator TCP endpoint.\n---\n\n# FRB Android Emulator Preparation\n\nUse this skill when a task requires a **host Android Emulator** for FRB local runtime validation.\n\n## Boundary\n\nFRB Android runtime validation currently supports a host Android Emulator path: host needs Java, Android SDK command-line tools, `emulator`, a system image, and an AVD; Docker runs FRB/Flutter commands through a Docker-local ADB server connected to the emulator TCP endpoint.\n\nDo not install Android Emulator packages unless the user has explicitly asked to prepare or run an emulator. Installing the emulator changes host state and downloads large SDK packages.\n\n## Default Host Locations\n\nUse standard Android Studio locations on macOS:\n\n```text\n~/Library/Android/sdk\n~/.android/avd\n~/.android\n```\n\nRespect `ANDROID_HOME` if it is already set. Otherwise use `~/Library/Android/sdk`. Do not invent repo-specific defaults such as `/workspace`, `/tmp`, or a user's personal artifacts directory.\n\n## Preflight\n\nOfficial references for this workflow:\n\n- Android Studio and command-line tools download page: https://developer.android.com/studio\n- `sdkmanager` command-line package manager: https://developer.android.com/tools/sdkmanager\n- `avdmanager` command-line AVD manager: https://developer.android.com/tools/avdmanager\n- Android Emulator command line: https://developer.android.com/studio/run/emulator-commandline\n- ADB command-line tool: https://developer.android.com/tools/adb\n\nRun these before installing anything:\n\n```bash\nsw_vers\nuname -m\nwhich adb || true\nadb version || true\necho \"${ANDROID_HOME:-}\"\nls -ld \"$HOME/Library/Android/sdk\" \"$HOME/.android\" \"$HOME/.android/avd\" 2>/dev/null || true\nwhich java || true\njava -version || true\n```\n\nIf the task requires an emulator, record whether these tools already exist:\n\n```bash\nls \"$HOME/Library/Android/sdk/cmdline-tools/latest/bin/sdkmanager\" 2>/dev/null || true\nls \"$HOME/Library/Android/sdk/cmdline-tools/latest/bin/avdmanager\" 2>/dev/null || true\nls \"$HOME/Library/Android/sdk/emulator/emulator\" 2>/dev/null || true\n```\n\n## Installation Policy\n\nBefore installing or downloading anything, state exactly what will be installed and ask for approval through the command escalation mechanism.\n\nFor a minimal macOS emulator setup, expect these host changes:\n\n```text\nHomebrew openjdk, if Java is missing\n~/Library/Android/sdk/cmdline-tools/latest\n~/Library/Android/sdk/emulator\n~/Library/Android/sdk/platform-tools\n~/Library/Android/sdk/platforms/android-<api>\n~/Library/Android/sdk/system-images/android-<api>/google_apis/arm64-v8a\n~/.android/avd/<avd-name>.avd\n~/.android/avd/<avd-name>.ini\n```\n\nPrefer ARM64 system images on Apple Silicon:\n\n```text\nsystem-images;android-35;google_apis;arm64-v8a\n```\n\nUse a different API level only when the task or Flutter version requires it.\n\n## Install Java\n\nIf `/usr/bin/java` is missing and Homebrew is available, prefer the Homebrew `openjdk` formula because it does not require the sudo package installer:\n\n```bash\nHOMEBREW_NO_AUTO_UPDATE=1 brew install openjdk\n```\n\nFor SDK commands in the current shell, prepend the Homebrew JDK:\n\n```bash\nexport PATH=\"/opt/homebrew/opt/openjdk/bin:$PATH\"\n```\n\nDo not use `brew install --cask temurin` in non-interactive agent runs unless the user has approved sudo-capable installation; it may invoke macOS Installer through `sudo`.\n\n## Install Command-Line Tools\n\nIf Android Studio is not installed and `cmdline-tools/latest` is missing, install the official Android command-line tools into the standard SDK root. This follows the Android Developers command-line tools instructions on the Android Studio download page: https://developer.android.com/studio. The important shape is to download the command-line tools package from that page, extract it, and place it under `cmdline-tools/latest` in the Android SDK root.\n\n```bash\nmkdir -p \"$HOME/Library/Android/sdk/cmdline-tools/latest\"\ncurl -L -o /private/tmp/commandlinetools-mac-latest.zip \\\n  https://dl.google.com/android/repository/commandlinetools-mac-14742923_latest.zip\nmkdir -p /private/tmp/frb-android-commandlinetools-extract\nditto -x -k /private/tmp/commandlinetools-mac-latest.zip /private/tmp/frb-android-commandlinetools-extract\ncp -R /private/tmp/frb-android-commandlinetools-extract/cmdline-tools/. \\\n  \"$HOME/Library/Android/sdk/cmdline-tools/latest/\"\n```\n\nThen set:\n\n```bash\nexport ANDROID_HOME=\"$HOME/Library/Android/sdk\"\nexport ANDROID_SDK_ROOT=\"$ANDROID_HOME\"\nexport PATH=\"/opt/homebrew/opt/openjdk/bin:$ANDROID_HOME/cmdline-tools/latest/bin:$ANDROID_HOME/platform-tools:$ANDROID_HOME/emulator:$PATH\"\n```\n\n## Install Emulator Packages\n\nAccept licenses and install the minimal packages with `sdkmanager`, the Android SDK package manager documented by Android Developers at https://developer.android.com/tools/sdkmanager:\n\n```bash\nyes | sdkmanager --licenses\nsdkmanager \\\n  \"emulator\" \\\n  \"platform-tools\" \\\n  \"platforms;android-35\" \\\n  \"system-images;android-35;google_apis;arm64-v8a\"\n```\n\nIf the user wants a smaller or non-Google image, choose a matching `system-images;android-<api>;default;arm64-v8a` package instead.\n\n## Create AVD\n\nUse a predictable AVD name that the `frb-dev-env` examples can reference. This uses `avdmanager`, the Android Developers command-line tool for creating and managing AVDs documented at https://developer.android.com/tools/avdmanager:\n\n```bash\navdmanager create avd \\\n  --name FRB_API_35 \\\n  --package \"system-images;android-35;google_apis;arm64-v8a\" \\\n  --device \"pixel_8\"\n```\n\nIf the device profile is unavailable, list device profiles and choose a common phone:\n\n```bash\navdmanager list device\n```\n\n## Start And Verify Emulator\n\nStart the host emulator through `frb-dev-env`. The underlying emulator invocation follows the Android Emulator command-line interface documented at https://developer.android.com/studio/run/emulator-commandline:\n\n```bash\n.claude/skills/frb-dev-env/frb_dev_env.py android emulator --avd FRB_API_35 --port 5554\n```\n\nVerify host ADB can see the emulator. This uses ADB, the Android Developers command-line tool documented at https://developer.android.com/tools/adb:\n\n```bash\nadb devices -l\n```\n\nFor emulator runtime tests, let Docker use its own ADB server and connect directly to the host emulator ADB TCP endpoint. For emulator console port `5554`, that endpoint is `host.docker.internal:5555`:\n\n```bash\n.claude/skills/frb-dev-env/frb_dev_env.py docker exec --android-emulator-adb -- adb devices -l\n```\n\nOnly after both ADB checks pass, run an FRB Android runtime command. Use the Docker-visible emulator serial:\n\n```bash\n.claude/skills/frb-dev-env/frb_dev_env.py docker exec --android-emulator-adb -- \\\n  bash -lc './frb_internal test-flutter-native --package frb_example--flutter_via_create --flutter-test-args \"--device-id host.docker.internal:5555\"'\n```\n\n## Common Failures\n\n- `Android SDK root does not exist`: install Android Studio or command-line tools into `~/Library/Android/sdk`, or set `ANDROID_HOME`.\n- `emulator: command not found`: install the SDK `emulator` package and ensure `~/Library/Android/sdk/emulator` is on `PATH`.\n- No AVD found: create `FRB_API_35` with `avdmanager create avd`, or pass the existing AVD name to `android emulator --avd`.\n- Docker cannot see the host emulator through `--android-emulator-adb`: verify the host emulator is running on console port `5554`, verify host `adb devices -l` shows `emulator-5554`, then run `adb connect host.docker.internal:5555` inside Docker or retry the helper command.\n- Flutter integration test fails with a VM service or DDS connection refused on `127.0.0.1`: the command likely used a host ADB server against an emulator. Use `--android-emulator-adb` so `adb forward` binds inside Docker.\n\n## Cleanup\n\nStop the emulator from host ADB:\n\n```bash\nadb -s emulator-5554 emu kill\n```\n\nAvoid deleting SDK packages or AVDs unless the user explicitly asks.\n","frontmatter":{"name":"frb-android-emulator-prepare","description":"Use when preparing, installing, diagnosing, or explaining the host Android Emulator environment for flutter_rust_bridge local runtime validation, including Android SDK command-line tools, emulator packages, AVD creation, host emulator startup, and Docker-local ADB connectivity to the emulator TCP endpoint."},"isInternal":false,"tokens":1954,"sizeBytes":8206},{"name":"SKILL.md","path":".claude/skills/frb-cargokit-dev/SKILL.md","rawUrl":"https://raw.githubusercontent.com/fzyzcjy/flutter_rust_bridge/HEAD/.claude/skills/frb-cargokit-dev/SKILL.md","title":"frb-cargokit-dev","category":"anthropic-skill","format":"markdown","content":"---\nname: frb-cargokit-dev\ndescription: Develop or update CargoKit for flutter_rust_bridge. Use when a bug or feature belongs in fzyzcjy/Cargokit, when updating the FRB CargoKit submodule pointers, or when preparing the paired CargoKit and FRB PRs that synchronize CargoKit changes into FRB integrate templates and examples.\n---\n\n# FRB CargoKit Development\n\n## Overview\n\nUse this skill when the actual fix belongs in the external `fzyzcjy/Cargokit` repository and FRB needs a follow-up PR that points its CargoKit submodules at the updated CargoKit branch or commit.\n\nThe usual shape is two PRs:\n\n1. A CargoKit PR with the real implementation.\n2. An FRB PR that updates the two CargoKit submodule pointers and regenerates copied integrate outputs.\n\nDo not implement CargoKit behavior only in copied FRB example output. The source of truth is the CargoKit repository referenced by the template submodules.\n\n## Source Locations\n\nFRB embeds CargoKit as git submodules in two template locations:\n\n- `frb_codegen/assets/integration_template/app/rust_builder/cargokit`\n- `frb_codegen/assets/integration_template/plugin/cargokit`\n\nBoth submodules should normally point at the same CargoKit branch or commit for the same logical update. Downstream generated copies may appear under:\n\n- `frb_example/**/cargokit/**`\n- `frb_example/**/rust_builder/cargokit/**`\n\nRead `frb-cargokit` first if you need to classify an existing diff before deciding where the change belongs.\n\n## Workflow\n\n### 1. Prepare The CargoKit Change\n\nWork in `fzyzcjy/Cargokit` for the actual CargoKit implementation.\n\n1. Create a CargoKit branch whose name is easy to reference from the FRB PR.\n2. Implement the fix in CargoKit and run the relevant CargoKit checks from that repository.\n3. Push the branch and open a CargoKit PR.\n4. Keep the branch alive until the FRB PR has passed CI.\n\nIf you are editing through an FRB submodule checkout, remember that commits belong to the nested CargoKit repository, not the outer FRB repository. Push the nested repository branch before updating the outer FRB submodule pointer.\n\n### 2. Update FRB To Point At The CargoKit Branch\n\nIn the FRB repository:\n\n1. Initialize submodules if needed:\n\n   ```bash\n   git submodule update --init --recursive\n   ```\n\n2. Fetch and check out the CargoKit branch or commit in both submodule checkouts:\n\n   ```bash\n   git -C frb_codegen/assets/integration_template/app/rust_builder/cargokit fetch origin <branch-or-ref>\n   git -C frb_codegen/assets/integration_template/app/rust_builder/cargokit checkout <commit-or-branch>\n   git -C frb_codegen/assets/integration_template/plugin/cargokit fetch origin <branch-or-ref>\n   git -C frb_codegen/assets/integration_template/plugin/cargokit checkout <commit-or-branch>\n   ```\n\n3. Stage the submodule pointer updates in the outer FRB repository:\n\n   ```bash\n   git add frb_codegen/assets/integration_template/app/rust_builder/cargokit\n   git add frb_codegen/assets/integration_template/plugin/cargokit\n   ```\n\n4. Confirm the outer FRB diff shows submodule pointer changes for both paths.\n\nUseful checks:\n\n```bash\ngit submodule status --recursive\ngit diff --submodule\n```\n\nDo not leave one template submodule on the old CargoKit commit unless the app and plugin templates intentionally need different CargoKit revisions.\n\n### 3. Regenerate FRB Integrate Outputs\n\nAfter changing either CargoKit submodule pointer, run:\n\n```bash\n./frb_internal precommit-integrate\n```\n\nThis updates copied CargoKit output and platform scaffold files under `frb_example/**`. Those downstream diffs are expected when CargoKit files are copied from the updated template.\n\nStage and commit the regenerated integrate output together with the submodule pointer update:\n\n```bash\ngit add frb_example frb_codegen/assets/integration_template\ngit commit -m \"Update CargoKit template pointers\"\n```\n\nThen run the normal PR checks from `frb-prepare-pr`, especially:\n\n```bash\n./frb_internal lint --fix\n```\n\nRun focused tests when the CargoKit change affects a platform that can be exercised locally. Otherwise let FRB CI cover the platform matrix and use `frb-fix-ci` for failures.\n\n### 4. Open The FRB PR\n\nThe FRB PR should mention that CargoKit is intentionally pointed at the temporary CargoKit PR branch or commit. Keep the PR body empty if the local PR workflow requires it, but use GitHub comments when you need to leave coordination notes.\n\nBefore asking for final review:\n\n1. Wait for the FRB PR CI to pass with the temporary CargoKit pointer.\n2. Ask reviewers to merge the CargoKit PR first.\n3. Update the FRB submodule pointers from the temporary branch commit to the merged CargoKit commit on the canonical branch.\n4. Rerun `./frb_internal precommit-integrate` if the CargoKit commit changed.\n5. Push the refreshed FRB PR and wait for CI again.\n6. Ask reviewers to merge the FRB PR.\n\n## CI Failure Triage\n\nWhen FRB CI fails after a CargoKit update, classify the failure before editing:\n\n- CargoKit behavior failure: fix it in `fzyzcjy/Cargokit`, then update both FRB submodule pointers.\n- Missing integrate refresh: run `./frb_internal precommit-integrate` and commit the copied output.\n- Lint or formatting drift: run `./frb_internal lint --fix`.\n- Platform-specific FRB scaffold issue: fix the FRB integration template, then run `./frb_internal precommit-integrate`.\n\nDo not patch copied `frb_example/**/cargokit/**` files directly as the final fix. They should be produced from the updated CargoKit submodule and integrate templates.\n\n## Related Skills\n\n- `frb-cargokit` - Understand CargoKit ownership and diff layers\n- `frb-code-generation` - Choose the correct generation command\n- `frb-prepare-pr` - Prepare an FRB PR\n- `frb-fix-ci` - Diagnose CI failures after the PR is opened\n","frontmatter":{"name":"frb-cargokit-dev","description":"Develop or update CargoKit for flutter_rust_bridge. Use when a bug or feature belongs in fzyzcjy/Cargokit, when updating the FRB CargoKit submodule pointers, or when preparing the paired CargoKit and FRB PRs that synchronize CargoKit changes into FRB integrate templates and examples."},"isInternal":false,"tokens":1314,"sizeBytes":5739},{"name":"SKILL.md","path":".claude/skills/frb-cargokit/SKILL.md","rawUrl":"https://raw.githubusercontent.com/fzyzcjy/flutter_rust_bridge/HEAD/.claude/skills/frb-cargokit/SKILL.md","title":"frb-cargokit","category":"anthropic-skill","format":"markdown","content":"---\nname: frb-cargokit\ndescription: Explain how cargokit is sourced, copied, and analyzed inside flutter_rust_bridge. Use when working on `integration_template/**/cargokit`, `frb_example/**/cargokit`, integrate/generate output drift, or any diff that mentions cargokit and you need to identify the true source of truth before judging the change.\n---\n\n# FRB Cargokit\n\n## Overview\n\nUse this skill to avoid mixing together three different things: the external cargokit repository, the template submodule pointers in FRB, and copied cargokit files inside generated examples.\n\nThe main job is to determine which layer a change belongs to before analyzing whether the change is correct.\n\n## Mental Model\n\nThere are two important cargokit forms inside FRB:\n\n1. External source\n   FRB depends on an external cargokit repository through git submodules under `frb_codegen/assets/integration_template/**/cargokit`.\n\n2. Copied outputs\n   FRB commands such as integrate/generate often copy concrete cargokit files into `frb_example/**/cargokit` or `frb_example/**/rust_builder/cargokit`.\n\nThis means a large cargokit diff in examples often does not mean FRB designed a large new cargokit behavior. It may only mean a template or submodule change propagated into copied outputs.\n\n## Source Of Truth\n\nTreat these paths as the source of truth when deciding cargokit behavior:\n\n- `frb_codegen/assets/integration_template/app/rust_builder/cargokit`\n- `frb_codegen/assets/integration_template/plugin/cargokit`\n\nTreat these paths as downstream copies unless the evidence shows otherwise:\n\n- `frb_example/**/cargokit/**`\n- `frb_example/**/rust_builder/cargokit/**`\n\nDo not start by judging copied example files in isolation. First determine which source submodule pointer or template content produced them.\n\n## Synchronizing Copies\n\nUse `./frb_internal sync-cargokit-copies` to refresh checked-in Cargokit copies from the template submodules when those copies are not already regenerated by the integrate CI. The command mirrors the Rust integration template writer's Cargokit filtering and copied-file prelude.\n\nDo not add `flutter_via_create`, `flutter_via_integrate`, or `flutter_package` to this sync map; the integrate generation CI already recreates those examples from templates. The CI `Generate :: Internal` job runs this as part of `./frb_internal generate-internal --set-exit-if-changed --coverage`, so the normal generate-internal diff gate also catches Cargokit copy drift. If Cargokit copies drift, fix the source-of-truth template/submodule first when appropriate, run `./frb_internal sync-cargokit-copies`, and commit the resulting copied-output refresh.\n\n## Diff Analysis Order\n\nWhen a user asks \"what happened to cargokit?\" follow this order:\n\n1. Identify the layer.\n   Decide whether the diff is a submodule pointer update, a template file change, or copied output churn.\n\n2. If the change is a submodule pointer update, inspect the two external cargokit commits first.\n   Compare the old and new submodule SHAs inside the submodule repository before saying anything about the FRB-side diff.\n\n3. Only then inspect copied example files.\n   Ask whether they merely reflect the upstream change or whether FRB also introduced independent local edits.\n\n4. Separate semantic changes from churn.\n   Distinguish real logic changes from format-only refreshes, lockfile updates, scaffold refreshes, and regenerated outputs.\n\n## What To Say Explicitly\n\nWhen answering, state which of these is true:\n\n- \"This is an external cargokit change exposed by a submodule pointer update.\"\n- \"This is downstream copy churn caused by integrate/generate.\"\n- \"This repository also has local copied cargokit edits in examples.\"\n\nIf discussing correctness, tie the judgment to the correct layer:\n\n- For submodule updates, judge the diff between the two external SHAs.\n- For copied outputs, judge whether the copy is expected and synchronized.\n- Do not infer upstream intent from a regenerated example diff alone.\n\n## Common Mistakes\n\nAvoid these mistakes:\n\n- Treating `frb_example/**/cargokit/**` as the primary implementation source.\n- Assuming a large example diff implies a large semantic cargokit change.\n- Analyzing copied files before checking the submodule pointer diff.\n- Mixing \"FRB updated the pointer\" with \"FRB locally rewrote cargokit.\"\n\n## Scope Boundary\n\nThis skill explains ownership, propagation, and diff interpretation.\n\nUse `frb-code-generation` when you need to know which generation command to run.\nUse `frb-debugging` when generation or cargokit-related behavior is failing and needs deeper investigation.\n","frontmatter":{"name":"frb-cargokit","description":"Explain how cargokit is sourced, copied, and analyzed inside flutter_rust_bridge. Use when working on `integration_template/**/cargokit`, `frb_example/**/cargokit`, integrate/generate output drift, or any diff that mentions cargokit and you need to identify the true source of truth before judging the change."},"isInternal":false,"tokens":967,"sizeBytes":4600},{"name":"SKILL.md","path":".claude/skills/frb-categorize-code-diff/SKILL.md","rawUrl":"https://raw.githubusercontent.com/fzyzcjy/flutter_rust_bridge/HEAD/.claude/skills/frb-categorize-code-diff/SKILL.md","title":"frb-categorize-code-diff","category":"anthropic-skill","format":"markdown","content":"---\nname: frb-categorize-code-diff\ndescription: Use when classifying a flutter_rust_bridge branch or PR diff into test and non-test changes, including colocated Rust test modules.\n---\n\n# 1 When to use\n\n- Use this skill to answer which changes in a branch or PR are tests and which are not.\n- Use the repository script instead of classifying paths by hand. Rust tests are commonly colocated in production `.rs` files.\n\n## 2 Run\n\n- Fetch the PR base before relying on `origin/master`.\n- Run from the repository root:\n\n```bash\nuv run --no-project .claude/skills/frb-categorize-code-diff/categorize_code_diff.py --base origin/master --head HEAD\n```\n\n- Add `--json` when another tool will consume the result.\n- Pass the PR's actual base revision when it is not `origin/master`.\n\n## 3 Interpret\n\n- `test-only`: every changed line is in a dedicated test path or a recognized colocated Rust test region.\n- `non-test-only`: every changed line is outside recognized test code.\n- `mixed`: the file contains both test and non-test changed lines.\n- `unclassified`: the diff cannot be mapped confidently, such as a binary patch.\n- Treat the non-test list as conservative. Unknown code is non-test rather than silently counted as test.\n\n## 4 Reliability\n\n- The script compares `base...head`, matching GitHub pull request diff semantics.\n- Dedicated test paths include `test/`, `tests/`, `test_fixtures/`, and conventional test filenames.\n- Rust inline classification recognizes brace-delimited items preceded by `#[cfg(test)]` or `#[test]` in both the base and head revisions.\n- Re-run after every PR update. Line identities and rename targets may change.\n- Inspect `mixed` files manually before claiming that a PR contains no production changes.\n\n## 5 Validation\n\n- Run the script tests after changing its classification logic:\n\n```bash\nPYTHONDONTWRITEBYTECODE=1 uv run --no-project --with pytest --with typer pytest -q .claude/skills/frb-categorize-code-diff/test_categorize_code_diff.py\n```\n\n- Keep `PYTHONDONTWRITEBYTECODE=1`; otherwise pytest leaves an untracked `__pycache__/` inside the skill directory.\n","frontmatter":{"name":"frb-categorize-code-diff","description":"Use when classifying a flutter_rust_bridge branch or PR diff into test and non-test changes, including colocated Rust test modules."},"isInternal":false,"tokens":490,"sizeBytes":2096},{"name":"SKILL.md","path":".claude/skills/frb-ci-filter/SKILL.md","rawUrl":"https://raw.githubusercontent.com/fzyzcjy/flutter_rust_bridge/HEAD/.claude/skills/frb-ci-filter/SKILL.md","title":"frb-ci-filter","category":"anthropic-skill","format":"markdown","content":"---\nname: frb-ci-filter\ndescription: Use when running focused flutter_rust_bridge GitHub Actions CI via ci_filter, adding/removing the ci-manual-dispatch PR label, choosing exact CI jobs or matrix entries, documenting intentionally partial CI runs, or repeatedly validating an exact CI target on one commit.\n---\n\nUse this skill when CI feedback is too expensive and the current task needs a focused GitHub Actions signal instead of automatic full CI.\n\nThe preferred mechanism is the formal `workflow_dispatch` input in `.github/workflows/ci.yaml`:\n\n```bash\ngh workflow run ci.yaml --ref <branch> -f 'ci_filter=<filter>'\n```\n\nDo not temporarily hack workflow jobs just to get partial CI unless the formal filter cannot express the target.\n\n# 1 Filter Syntax\n\n```text\nfull\njob_id\njob_a,job_b\njob_id[dimension=value]\njob_id[dimension=value|other_value,other_dimension=value]\njob_a,job_b[dimension=value]\n```\n\n- `full` or `*` runs the normal full CI plan.\n- `job_id` runs the whole job, including its full matrix if it has one.\n- `job_id[...]` runs only matching matrix entries.\n- Bracket filters currently require `dimension=value`.\n- Commas outside brackets combine multiple jobs.\n- Explicit `dimension=value` conditions inside brackets are ANDed.\n- `|` means OR within one dimension.\n- Matrix dimensions include direct keys such as `image`, `package`, `device`, `api-level`, `sanitizer`, `platforms`, and nested `matrix.info` keys such as `platform`, `target`, `version`.\n- Numeric matrix values are matched as strings, e.g. `api-level=35`.\n- Values may contain spaces, e.g. iOS simulator device names.\n- Unknown jobs, unknown dimensions, malformed filters, and filters matching no matrix entries fail in the `Plan :: CI` job.\n\n# 2 Manual Dispatch Label\n\nUse the `ci-manual-dispatch` PR label when an agent or human wants to prevent automatic PR CI from running the full surface while iterating.\n\n```bash\ngh pr edit <pr-number> --add-label ci-manual-dispatch\n```\n\nWith the label present:\n\n- normal PR-triggered CI produces an empty plan for the heavy jobs\n- `workflow_dispatch` remains available for explicit focused runs\n- the PR must not be treated as merge-ready from partial runs alone\n\nBefore final readiness:\n\n```bash\ngh pr edit <pr-number> --remove-label ci-manual-dispatch\n```\n\nThen let the normal full CI surface run. Removing the label triggers PR CI because the workflow listens to `labeled` and `unlabeled` pull request events.\n\n# 3 Manual Dispatch PR Comment\n\nManual `workflow_dispatch` runs of `.github/workflows/ci.yaml` comment from `Plan :: CI` on the matching open PR with the selected `ci_filter` and run link; normal `push` and `pull_request` runs do not comment.\n\n# 4 Local Planning\n\nBefore dispatching an expensive manual run, smoke the filter locally:\n\n```bash\n./frb_internal plan-ci --filter 'test_dart_web[package=frb_example--pure_dart_pde]'\n```\n\nThe command emits a GitHub Actions output line:\n\n```text\nplan={...}\n```\n\nThe workflow consumes it with paths like:\n\n```yaml\nfromJSON(needs.plan_ci.outputs.plan).test_dart_web.enable\nfromJSON(needs.plan_ci.outputs.plan).test_dart_web.matrix\n```\n\nThat output shape is intentional; each CI job has its own top-level entry containing `enable`, and matrix jobs also contain `matrix`.\n\n# 5 Repeated Precise Validation\n\nUse repeated precise validation when a flaky fix needs several independent executions of one CI job or matrix entry on exactly the same commit.\n\n- **Fix the commit**: Push the branch once, record its SHA, and do not move the branch until all runs finish.\n- **Smoke the filter**: Run `./frb_internal plan-ci --filter '<filter>'` before dispatching.\n- **Dispatch independently**: Create five separate `workflow_dispatch` runs, not one run with five matrix copies and not five attempts of one run.\n- **Keep the filter identical**: Pass the same exact `ci_filter` to every dispatch.\n- **Verify the SHA**: Confirm every selected run reports the recorded `headSha`; a branch name alone is not evidence that queued runs used the same commit.\n- **Count target jobs**: Count the conclusion of the selected job or matrix entry, not the workflow conclusion or skipped jobs.\n- **Preserve every run link**: Report all five run URLs, the fixed SHA, the filter, and the target-job result.\n- **Do not move or cancel**: A new commit invalidates the comparison; cancellation does not count as a completed validation.\n\nRecord the immutable input:\n\n```bash\nbranch=codex/example-fix\nexpected_sha=$(git rev-parse HEAD)\nfilter='test_dart_web[package=frb_example--pure_dart]'\ngit push -u origin \"$branch\"\n```\n\nDispatch five independent runs. Manual dispatches use unique concurrency groups, so these runs do not cancel one another:\n\n```bash\nfor run_number in 1 2 3 4 5; do\n  gh workflow run ci.yaml --ref \"$branch\" -f \"ci_filter=$filter\"\ndone\n```\n\nList the candidate runs once, then select the five runs created for this validation window:\n\n```bash\ngh run list \\\n  --workflow ci.yaml \\\n  --branch \"$branch\" \\\n  --event workflow_dispatch \\\n  --limit 10 \\\n  --json databaseId,createdAt,headSha,status,conclusion,url\n```\n\nFor each selected run:\n\n- Confirm `headSha == expected_sha`.\n- Confirm `Plan :: CI` creates only the requested job or matrix entry.\n- Wait for the target job to complete.\n- If it fails, archive and inspect that job's complete log.\n- Report a result only after all five target jobs finish.\n\nDo not claim “5/5” from any of these shapes:\n\n- One workflow run containing five matrix entries.\n- One workflow run rerun five times.\n- Five workflow runs whose `headSha` values differ.\n- Five workflow conclusions where the target job was skipped, cancelled, or never started.\n- Fewer than five completed target jobs plus queued or in-progress jobs.\n\n# 6 Examples\n\nThese examples are protected by `tools/frb_internal/test/ci_plan_test.dart`; update that test whenever adding or changing examples here.\n\nFull CI:\n\n```bash\ngh workflow run ci.yaml --ref <branch> -f 'ci_filter=full'\n```\n\nOne non-matrix job:\n\n```bash\ngh workflow run ci.yaml --ref <branch> -f 'ci_filter=lint_dart_primary'\n```\n\nSeveral whole jobs:\n\n```bash\ngh workflow run ci.yaml --ref <branch> -f 'ci_filter=lint_dart_primary,test_dart_web'\n```\n\nOne non-matrix job plus one matrix entry:\n\n```bash\ngh workflow run ci.yaml --ref <branch> -f 'ci_filter=lint_dart_primary,test_dart_web[package=frb_example--pure_dart]'\n```\n\nOne Dart web package:\n\n```bash\ngh workflow run ci.yaml --ref <branch> -f 'ci_filter=test_dart_web[package=frb_example--pure_dart_pde]'\n```\n\nTwo Dart web packages:\n\n```bash\ngh workflow run ci.yaml --ref <branch> -f 'ci_filter=test_dart_web[package=frb_dart|frb_example--pure_dart_pde]'\n```\n\nTwo Dart web packages plus one non-matrix job:\n\n```bash\ngh workflow run ci.yaml --ref <branch> -f 'ci_filter=test_dart_web[package=frb_dart|frb_example--pure_dart_pde],lint_rust_primary'\n```\n\nTwo precise matrix tuples in the same job:\n\n```bash\ngh workflow run ci.yaml --ref <branch> -f 'ci_filter=test_rust[image=ubuntu-latest,version=nightly],test_rust[image=ubuntu-latest,version=1.85.0]'\n```\n\nOne Dart native package on Linux:\n\n```bash\ngh workflow run ci.yaml --ref <branch> -f 'ci_filter=test_dart_native[image=ubuntu-24.04,package=tools--frb_internal]'\n```\n\nRust tests on selected toolchains:\n\n```bash\ngh workflow run ci.yaml --ref <branch> -f 'ci_filter=test_rust[image=ubuntu-latest,version=nightly|1.85.0]'\n```\n\nOne Flutter desktop package on Linux:\n\n```bash\ngh workflow run ci.yaml --ref <branch> -f 'ci_filter=test_flutter_native_desktop[platform=linux,package=frb_example--gallery]'\n```\n\nOne Android emulator matrix entry:\n\n```bash\ngh workflow run ci.yaml --ref <branch> -f 'ci_filter=test_flutter_native_android[package=frb_example--flutter_via_create,device=pixel,api-level=35]'\n```\n\nOne iOS simulator matrix entry:\n\n```bash\ngh workflow run ci.yaml --ref <branch> -f 'ci_filter=test_flutter_native_ios[device=iPhone 16 Pro Max Simulator (18.6),package=frb_example--rust_ui_counter--ui]'\n```\n\nOne codegen command-generate entry:\n\n```bash\ngh workflow run ci.yaml --ref <branch> -f 'ci_filter=generate_run_frb_codegen_command_generate[image=ubuntu-24.04,package=frb_example--integrate_third_party]'\n```\n\nOne OHOS command-integrate entry:\n\n```bash\ngh workflow run ci.yaml --ref <branch> -f 'ci_filter=generate_run_frb_codegen_command_integrate[platforms=ohos]'\n```\n\nOne sanitizer entry:\n\n```bash\ngh workflow run ci.yaml --ref <branch> -f 'ci_filter=test_dart_sanitizer[sanitizer=asan,package=frb_example--pure_dart]'\n```\n\nOne benchmark runner:\n\n```bash\ngh workflow run ci.yaml --ref <branch> -f 'ci_filter=bench_dart_native[image=ubuntu-24.04]'\n```\n\n# 7 Choosing Evidence\n\nFor intentional red reproduction PRs:\n\n- Run only the single job or matrix entry that proves the reported bad behavior.\n- State the exact `ci_filter` in the PR body.\n- State the expected failure text or behavior.\n- Do not treat the narrowed red run as fix readiness evidence.\n\nFor ordinary iteration branches:\n\n- Prefer the smallest filter that exercises the changed behavior.\n- Say in PR status/comments when only filtered CI has run.\n- Before final review or merge readiness, remove `ci-manual-dispatch` and run normal CI.\n\n# 8 Validation\n\nBefore pushing changes to the filter mechanism or examples:\n\n```bash\ncd tools/frb_internal\ndart test test/ci_plan_test.dart\n```\n\nUseful smoke check:\n\n```bash\n./frb_internal plan-ci --filter 'test_dart_web[package=frb_example--pure_dart_pde]'\n```\n","frontmatter":{"name":"frb-ci-filter","description":"Use when running focused flutter_rust_bridge GitHub Actions CI via ci_filter, adding/removing the ci-manual-dispatch PR label, choosing exact CI jobs or matrix entries, documenting intentionally partial CI runs, or repeatedly validating an exact CI target on one commit."},"isInternal":false,"tokens":2315,"sizeBytes":9419},{"name":"SKILL.md","path":".claude/skills/frb-code-generation/SKILL.md","rawUrl":"https://raw.githubusercontent.com/fzyzcjy/flutter_rust_bridge/HEAD/.claude/skills/frb-code-generation/SKILL.md","title":"frb-code-generation","category":"anthropic-skill","format":"markdown","content":"---\nname: frb-code-generation\ndescription: Use when modifying Rust APIs, codegen, generated examples, or platform scaffolds in flutter_rust_bridge to select generation commands and preserve source-of-truth and convergence rules\n---\n\n# FRB Code Generation\n\n> **Note:** Read `frb-dev-env` before running commands and follow the active user's environment rules.\n\n## Overview\n\nflutter_rust_bridge requires code generation when Rust APIs change. This skill maps change types to the minimal generation commands needed.\n\n**Core principle:** Run only the generation commands needed for your change type.\n\n**Validation rule:** If regenerated outputs cause previously green non-`Generate` jobs to fail, treat the generated outputs as suspect until they are validated from a clean environment.\n\n> **After codegen:** Follow `frb-dev-env` and verify generated output is present in the selected worktree before reviewing the diff.\n\n## Quick Reference\n\n| Change Type | Command |\n|-------------|---------|\n| Example Rust API (`frb_example/**/rust/src/api/*.rs`) | `./frb_internal precommit-generate` |\n| Flutter integrate examples | `./frb_internal precommit-integrate` |\n| `frb_codegen/` generation logic | `./frb_internal precommit-generate` + `generate-internal-rust` |\n| `frb_rust/src/` core API | `./frb_internal generate-internal-rust` |\n| `frb_example/pure_dart` generator | `./frb_internal generate-internal-frb-example-pure-dart` |\n| CLI help documentation | `./frb_internal generate-internal-book-help` |\n| Root `README.md` or `CHANGELOG.md` | `./frb_internal generate-internal-readme` |\n| Other non-generated docs, comments, tests, or `frb_dart/` source | No generation needed |\n\n## Important Rules\n\n### Root Documentation Fan-Out\n\nTreat the root `README.md` and `CHANGELOG.md` as sources of truth. After changing either file, run:\n\n```bash\n./frb_internal generate-internal-readme\n```\n\nCommit the generated package documentation in the same change. Do not manually edit `frb_dart/README.md`, `frb_dart/CHANGELOG.md`, or `frb_hooks/CHANGELOG.md`.\n\n### Integrate Template Drift\n\nIf a diff touches Flutter integrate example outputs, platform scaffolds, or copied `cargokit` files under `frb_example/**`, check whether the real source of truth should also change under `frb_codegen/assets/integration_template/`.\n\nDo not submit only downstream integrate output changes when the behavior belongs in the template. Update the template first, then run:\n\n```bash\n./frb_internal precommit-integrate\n```\n\nTreat `frb_codegen/assets/integration_template/**/cargokit` as the source of truth for copied `cargokit` output. If the actual bug belongs inside the external `cargokit` submodule, read the `frb-cargokit` skill before deciding whether to patch the submodule and update its pointer.\n\nFor CI diagnosis rules about generated-file format/lint failures, repeated package-level `Generate` drift, or `Generate :: FRB Codegen :: Command Integrate` failures, you MUST read `frb-fix-ci` first. This skill is for command selection, not CI failure-propagation diagnosis.\n\nFor `pure_dart` / `pure_dart_pde` generation issues, treat `frb_example/pure_dart` as the upstream source and `frb_example/pure_dart_pde` as the derived copy. See `frb-fix-ci` for the CI diagnosis workflow.\n\nIf CI repair has already entered repeated package-level drift, you MUST stop choosing narrower commands and switch to `frb-fix-ci`.\n\nDo not manually patch generated files as the final fix. The final accepted result should be produced by the corresponding generation command in a clean matching environment.\n\n### Generation Convergence\n\n- Run the narrowest owning generator before broad generation.\n- Normalize the intended source inputs, resolve dependencies, run final code generation, and format with the target toolchain. Do not hand formatter-unstable intermediate text to a different generation lane.\n- Run the owning generation command a second time and require a clean diff. A single successful run is not convergence.\n- Preserve semantic equivalence in templates. Do not move construction, scope, lifetime, or callback boundaries merely to make new formatter output look smaller.\n\n### Generated Output Provenance\n\nClassify every changed path before accepting generated drift:\n\n| Provenance | Required action |\n| --- | --- |\n| Integration template | Change the template source and regenerate consumers |\n| Apple scaffold asset | Refresh through the owning scaffold workflow |\n| CargoKit copy | Read `frb-cargokit` and follow its ownership and synchronization rules |\n| Generated example output | Keep the generator and snapshot together |\n| Flutter migrator edit in a legacy example | Apply the exact current scaffold migration and record it as direct |\n| Unexplained manual edit | Revert it or identify its owning source before proceeding |\n\n- When an old example has no supported regeneration entry point, compare it with a fresh target-Flutter scaffold and directly apply only required tool-defined migrations.\n- Record direct legacy scaffold migrations separately from automatic output.\n\n### OHOS Integrate Composition\n\n- Treat the checked-in package and the older OHOS Flutter fork as two distinct sources of truth.\n- Generate generic Flutter scaffold files with the current Flutter package.\n- Overlay only explicit OHOS-owned paths from the OHOS fork: `ohos` and, when required by the package, `rust_builder/ohos` and `rust_builder/pubspec.yaml`.\n- Exclude transient trees such as `node_modules` and preserve intentional deletions instead of resurrecting stale files.\n- Stage and validate the composed result before replacing the canonical package. Restore the original package if create, dependency resolution, code generation, formatting, or the final swap fails.\n- Test both success and failure paths. Do not add production APIs whose only purpose is exposing internals to tests.\n","frontmatter":{"name":"frb-code-generation","description":"Use when modifying Rust APIs, codegen, generated examples, or platform scaffolds in flutter_rust_bridge to select generation commands and preserve source-of-truth and convergence rules"},"isInternal":false,"tokens":1247,"sizeBytes":5853},{"name":"SKILL.md","path":".claude/skills/frb-debugging/SKILL.md","rawUrl":"https://raw.githubusercontent.com/fzyzcjy/flutter_rust_bridge/HEAD/.claude/skills/frb-debugging/SKILL.md","title":"frb-debugging","category":"anthropic-skill","format":"markdown","content":"---\nname: frb-debugging\ndescription: Use when generated code looks wrong, code generation fails, or you need to understand FRB internals\n---\n\n# FRB Debugging\n\n> **Note:** Read `frb-dev-env` before running commands and follow the active user's environment rules.\n\n## When to Use\n\n- Generated code looks wrong\n- Code generation fails\n- Need to understand FRB internals\n- Runtime errors in generated code\n\n## Quick Diagnostics\n\n### Dump Intermediate Representations\n\nThe in-tree examples by default enable `dump_all: true` in  `flutter_rust_bridge.yaml` for debugability.\n\nTherefore, you can check `rust/target/frb_dump/` for:\n- Effective configuration\n- IR (intermediate representation)\n- Generated spec and code\n- ...\n\n### Debug Logs\n\n```bash\nRUST_LOG=debug flutter_rust_bridge_codegen ...\n```\n\n## Full Troubleshooting\n\nIf above doesn't help, see:\n\n* `website/docs/docs/guides/contributing/tip.md`\n* `website/docs/manual/troubleshooting.md`\n\nBefore going deep into FRB internals, first check whether you are actually seeing failure propagation from unstable generated outputs or integrate templates.\n\nIn particular:\n\n- If CI failures may be explained by failure propagation, repeated package-level drift, or unstable generated outputs, you MUST use `frb-fix-ci` first\n- You MUST only stay in `frb-debugging` after `frb-fix-ci` has ruled out those simpler explanations\n\n## Related Skills\n\n- `frb-code-generation` - Which generation commands to run\n- `frb-fix-ci` - Diagnose CI failure propagation before deep debugging\n- `frb-develop-feature` - Development workflow\n","frontmatter":{"name":"frb-debugging","description":"Use when generated code looks wrong, code generation fails, or you need to understand FRB internals"},"isInternal":false,"tokens":354,"sizeBytes":1564},{"name":"SKILL.md","path":".claude/skills/frb-dev-env/SKILL.md","rawUrl":"https://raw.githubusercontent.com/fzyzcjy/flutter_rust_bridge/HEAD/.claude/skills/frb-dev-env/SKILL.md","title":"frb-dev-env","category":"anthropic-skill","format":"markdown","content":"---\nname: frb-dev-env\ndescription: \"Use when the user wants Docker-based FRB development or Tart-based iOS Simulator validation.\"\n---\n\n# FRB Development Environment\n\nUse this skill before setting up, diagnosing, or running `flutter_rust_bridge` commands with a container-based development workflow.\n\n## Choosing an Environment\n\nUse Docker for normal FRB development checks that fit Linux containers: Rust tests, Dart tests, web tests, code generation, lint, Android build checks that do not need a running emulator, and most `./frb_internal` commands. Docker is the default reproducible baseline when local host toolchains are missing or suspected to have drifted.\n\nUse a host Android Emulator for local Android runtime validation when a running Android target is required. Keep FRB, Flutter, Rust, Gradle, and Android build tooling inside Docker. The host runs only the emulator and the Android SDK command-line tools needed to manage it. Docker runs its own ADB server and connects directly to the emulator TCP endpoint so Flutter's VM service forwarding stays inside Docker. See the Android section below.\n\nUse Tart only for checks that need macOS and Xcode, especially iOS Simulator validation. The Tart workflow is intentionally separate from Docker; it gives each worktree a reusable macOS VM cloned from a prepared base VM, then runs FRB commands inside that VM.\n\nDo not use the Tart macOS VM as the Android emulator strategy. macOS Tart guests do not support nested virtualization, so Android emulator runtime coverage needs a host or runner with virtualization support. Android SDK compilation checks can still use Docker when they do not need a running emulator.\n\n## Principles\n\n- FRB runs locally in Docker by default. Do not use remote workspaces for FRB unless the user explicitly asks for a one-off remote run.\n- Use the current checkout/worktree that Codex or the user already selected. Do not create a new worktree unless the user explicitly asks.\n- Run FRB commands from the repository root unless the command itself intentionally changes directories.\n- Prefer repository tooling such as `./frb_internal` over ad hoc direct invocations.\n- Do not manually edit generated files (`frb_generated.*`, `*.freezed.dart`, `*.g.dart`) as the final fix.\n\n## PATH Bootstrap\n\nBefore using this skill's helper or FRB tooling, make sure common local tool locations are in `PATH`. Codex shells may start with a reduced `PATH`, which can hide `uv`, Docker, Dart, Flutter, or Homebrew-installed tools even when they exist on the machine.\n\nUse this prefix when a command reports `env: uv: No such file or directory`, `docker: command not found`, `dart: command not found`, or similar:\n\n```bash\nexport PATH=\"$HOME/.local/bin:/opt/homebrew/bin:/usr/local/bin:$PATH\"\n```\n\nThen retry the same command. Prefer fixing `PATH` over bypassing the helper, because `frb_dev_env.py` uses the configured per-worktree container and release credential handling.\n\n## First Checks\n\nBefore running tests, lint, code generation, or setup:\n\n```bash\ngit rev-parse --show-toplevel\ngit status --short\ngit submodule status --recursive\n```\n\nIf submodules are uninitialized, initialize them locally:\n\n```bash\ngit submodule update --init --recursive\n```\n\n## Docker\n\nUse Docker for container-based FRB development. Each worktree should have one long-lived local container that is reused for tests, lint, code generation, and setup commands.\n\nUse `frb_dev_env.py` next to this skill to inspect, create, start, and reuse the per-worktree container. The script derives the container name from the canonical worktree root only:\n\n```text\nfrb-<worktree-root-sha256-prefix-12>\n```\n\nIt mounts the canonical worktree root and git common root at their host-like absolute paths, and labels the container with:\n\n```text\nfrb.dev.repo=flutter_rust_bridge\nfrb.dev.worktree=<canonical worktree root>\nfrb.dev.git-common-root=<git common root>\nfrb.dev.layout-version=3\n```\n\nCommands run from the host-like worktree path inside the container. There is intentionally no `/workspace` alias, because linked git worktrees and submodule gitdir references need the same path shape that they have on the host.\n\nWhen creating a new per-worktree container, the helper pulls the selected Docker image before `docker run`. Existing containers are validated and reused without pulling.\n\nTypical usage:\n\n```bash\n.claude/skills/frb-dev-env/frb_dev_env.py docker info\n.claude/skills/frb-dev-env/frb_dev_env.py docker create\n.claude/skills/frb-dev-env/frb_dev_env.py docker exec -- bash -lc './frb_internal --help'\n```\n\n### Temporary Docker Commands With Publish Credentials\n\nUse `docker-run-rm --with-publish-credentials` only for release publishing commands that need host credentials. Unlike the normal long-lived development container, this runs a fresh temporary container with `docker run --rm`, mounts the worktree and git common root, mounts the host credential directories read-only, copies the credential files into temporary container-local homes, checks login status, runs the requested command, then discards the container.\n\nThe publish credential flag mounts these host credential sources when present:\n\n- GitHub CLI config from `GH_CONFIG_DIR` or `~/.config/gh`\n- GitHub token env vars from `GH_TOKEN` or `GITHUB_TOKEN`, forwarded without printing token values\n- Cargo credentials and config from `CARGO_HOME` or `~/.cargo`\n- Git config from `~/.gitconfig` and `~/.config/git`\n- Dart pub credentials from `PUB_CACHE`, `~/.pub-cache`, `~/.config/dart`, or `~/Library/Application Support/dart`\n\nPublish mode also prepares the container environment used by the release command:\n\n- Reuses the Docker image's Rust toolchain via `RUSTUP_HOME=/root/.rustup` even though `HOME` is changed to a temporary credential directory.\n- Rewrites copied Git/Cargo config proxy URLs from `localhost` or `127.0.0.1` to `host.docker.internal`, and forwards common proxy environment variables with the same rewrite. This lets a container use a host proxy such as `http://localhost:7897`.\n- Checks GitHub auth, GitHub network access, Cargo availability, crates.io search access, and Dart pub credential readability before running the requested command.\n\nRun the credential preflight before any irreversible release step:\n\n```bash\n.claude/skills/frb-dev-env/frb_dev_env.py docker-run-rm --with-publish-credentials -- true\n```\n\nRun release publishing through a temporary publish-credential container. Do not manually add `RUSTUP_HOME` or proxy environment overrides unless debugging the helper itself:\n\n```bash\n.claude/skills/frb-dev-env/frb_dev_env.py docker-run-rm --with-publish-credentials -- ./frb_internal release\n```\n\nThe credential preflight fails before running release commands if host GitHub CLI auth is invalid, Cargo credentials are missing, Dart pub credentials are missing, the image has no usable Cargo toolchain, or the container cannot reach GitHub/crates.io through the configured network path. It then re-checks GitHub CLI auth inside the temporary container and runs `gh auth setup-git` there so HTTPS `git push` can use the mounted GitHub CLI auth.\n\n### Cleanup\n\nDelete a worktree's Docker container when the worktree is no longer needed, or when local Docker resources are getting crowded:\n\n```bash\n.claude/skills/frb-dev-env/frb_dev_env.py docker delete\n```\n\nThe delete command validates the container labels before removing it. Use `--force` only when intentionally removing a mismatched container.\n\nUse the project `frb-docker` skill for image, devcontainer, and Dockerfile details.\n\n## Android Host Runtime\n\nUse this workflow for local Android runtime checks when Docker can build and run the FRB/Flutter command, but the Android target itself must be managed by the host.\n\n### Path Selection\n\nThe supported Android runtime path is:\n\n```text\nhost Android Emulator:\n  host runs the emulator process\n  Docker runs FRB/Flutter and its own ADB server\n  Docker ADB connects to host.docker.internal:<emulator-adb-port>\n```\n\nPhysical Android phones are intentionally out of scope for this helper until that path is manually validated. The host does not need FRB, Flutter, Rust, Cargo, Java, or Gradle for the emulator path.\n\n### Host Emulator Architecture\n\nUse this split for a host Android Emulator:\n\n```text\nhost:\n  Android Emulator process\n  Android SDK command-line tools for SDK packages, AVD creation, emulator start/stop, and host-side inspection\n  Android SDK root: ~/Library/Android/sdk unless ANDROID_HOME is set\n  AVD root: ~/.android/avd\n\nDocker container:\n  FRB checkout\n  Flutter, Dart, Rust, Cargo, Java, Gradle\n  Android SDK/NDK/build tools needed for builds\n  Docker-local ADB server connected to host.docker.internal:<emulator-adb-port>\n```\n\nRead the project `frb-android-emulator-prepare` skill before preparing a host emulator. The host emulator path needs Java, Android SDK command-line tools, emulator packages, a system image, and an AVD in the standard Android Studio host locations.\n\nPrint the resolved host Android SDK environment if needed:\n\n```bash\n.claude/skills/frb-dev-env/frb_dev_env.py android env\n```\n\nStart the emulator on the host. Pin the port when you want a stable host serial such as `emulator-5554`; the paired emulator ADB TCP endpoint is one port higher, such as `host.docker.internal:5555`:\n\n```bash\n.claude/skills/frb-dev-env/frb_dev_env.py android emulator --avd FRB_API_35 --port 5554\n```\n\nVerify Docker-local ADB can see the host emulator:\n\n```bash\n.claude/skills/frb-dev-env/frb_dev_env.py docker exec --android-emulator-adb -- adb devices -l\n```\n\nRun the FRB/Flutter command against the Docker-visible emulator serial:\n\n```bash\n.claude/skills/frb-dev-env/frb_dev_env.py docker exec --android-emulator-adb -- \\\n  bash -lc './frb_internal test-flutter-native --package frb_example--flutter_via_create --flutter-test-args \"--device-id host.docker.internal:5555\"'\n```\n\n`docker exec --android-emulator-adb` first runs `adb connect host.docker.internal:5555` inside the container, then runs the requested command with the Android SDK x86_64 runtime library path set. Override the default endpoint with `--android-emulator-adb-host`, `--android-emulator-adb-port`, `FRB_ANDROID_EMULATOR_ADB_HOST`, or `FRB_ANDROID_EMULATOR_ADB_PORT`.\n\n### Why Emulator Does Not Use Host ADB Server\n\nDo not use a host ADB server for host emulator full Flutter integration tests. Flutter creates dynamic VM service `adb forward` ports where the ADB server runs. If the ADB server runs on the host, those forwarded ports bind on host `127.0.0.1`, but Flutter is running inside Docker and then tries Docker-local `127.0.0.1`. This fails with DDS or VM service connection refused errors.\n\nUse `--android-emulator-adb` so the ADB server and the forwarded VM service port both live inside Docker.\n\n### Security And Cleanup\n\n- Use a single Android target when possible. With multiple emulators or devices, always pass `--device-id <serial>` through `--flutter-test-args`.\n- For host emulator Flutter integration tests, use `--android-emulator-adb` so `adb forward` creates VM service forwarding inside Docker.\n- Do not run the Android Emulator inside Tart. Do not rely on Docker nested virtualization for Android emulator runtime checks on macOS.\n- If the Docker image lacks Android SDK, NDK, or build-tools packages required by Flutter Android builds, update the FRB Docker image or mount a dedicated Android SDK into the container; do not install host Flutter or Gradle just to work around the missing container toolchain.\n\n## Tart\n\nUse Tart for iOS Simulator validation when Docker cannot provide the required macOS/Xcode runtime. The script uses one reusable Tart VM per worktree, named from the canonical worktree root:\n\n```text\nfrb-tart-<worktree-root-sha256-prefix-12>\n```\n\n### Base VM\n\nBy default, `create` clones from a prepared local base VM named `frb-tart-base`. Override it with `FRB_TART_BASE_VM` or `--base-vm`.\n\nRead `frb-tart-prepare` before creating or changing the base VM. The base VM should be built by the checked-in Packer template from a pinned Tart OCI artifact and treated as immutable; do not boot it and manually install tools into it.\n\n### Mounted Checkout\n\nFor linked git worktrees, the Tart helper shares both the canonical worktree root and the git common root into the VM, then mounts them at host-like absolute paths with `mount_virtiofs`. This keeps worktree `.git` files and submodule gitdir references valid inside the VM.\n\nUse plain `tart exec` for light commands that should see the mounted host checkout exactly, such as `git status`, `flutter --version`, `xcrun simctl list`, and simulator boot commands.\n\n### VM Lifecycle\n\nInspect, create, start, and run light commands in the per-worktree VM:\n\n```bash\n.claude/skills/frb-dev-env/frb_dev_env.py tart info\n.claude/skills/frb-dev-env/frb_dev_env.py tart create\n.claude/skills/frb-dev-env/frb_dev_env.py tart start\n.claude/skills/frb-dev-env/frb_dev_env.py tart ip --wait 180\n.claude/skills/frb-dev-env/frb_dev_env.py tart exec -- sw_vers\n```\n\n### Tart Guest Network Proxy\n\nWhen the Tart guest needs outbound network access through the host proxy, make the proxy listen on the host's Tart bridge address and pass that URL through the helper. Enable LAN access for the host proxy, keep the mixed proxy port at `7897`, and verify the host listens on `*:7897` rather than only `127.0.0.1:7897`.\n\nTart guests normally reach the host on `192.168.64.1`, so the usual proxy URL is:\n\n```bash\nexport FRB_TART_HOST_PROXY_URL=http://192.168.64.1:7897\n.claude/skills/frb-dev-env/frb_dev_env.py tart exec -- curl -I https://pub.dev\n```\n\nYou can also pass the proxy for a single command:\n\n```bash\n.claude/skills/frb-dev-env/frb_dev_env.py tart exec \\\n  --host-proxy-url http://192.168.64.1:7897 \\\n  -- curl -I https://pub.dev\n```\n\nDelete the worktree VM when it is no longer needed:\n\n```bash\n.claude/skills/frb-dev-env/frb_dev_env.py tart stop\n.claude/skills/frb-dev-env/frb_dev_env.py tart delete\n```\n\n### VM-Local Copy\n\nFor heavy iOS build/test commands, prefer uploading the current worktree to a VM-local copy and running there. This avoids writing Xcode build artifacts through the virtiofs host mount:\n\n```bash\n.claude/skills/frb-dev-env/frb_dev_env.py tart upload\n.claude/skills/frb-dev-env/frb_dev_env.py tart exec --sync-code -- ./frb_internal test-flutter-native --flutter-test-args '--device-id <UDID>' --package <package>\n```\n\n`tart upload` and `tart exec --sync-code` both first ensure the worktree VM is running and the host-like worktree mount exists. They then run `rsync` inside the VM from the mounted host worktree to a VM-local copy under `/Users/admin/frb-dev-env-local-copies/<worktree-hash>`. The upload excludes `.git`, `.dart_tool`, `build`, `target`, `.idea`, and `.vscode`. It does not delete existing files in the VM-local copy, so build caches survive repeated runs.\n\nCommands run with `--sync-code` start from the VM-local uploaded copy, not from the host-mounted path.\n\nExamples:\n\n```bash\n# Show the VM-local path after uploading the current worktree.\n.claude/skills/frb-dev-env/frb_dev_env.py tart upload\n\n# Run a heavy command from the uploaded VM-local copy.\n.claude/skills/frb-dev-env/frb_dev_env.py tart exec --sync-code -- ./frb_internal test-flutter-native --package frb_example--flutter_via_create --flutter-test-args '--device-id <UDID>'\n```\n\n### iOS Integration Preparation\n\nBefore running iOS Simulator integration tests, prepare the VM Flutter SDK once per VM or after replacing Flutter. This applies the runtime portion of `https://github.com/flutter/flutter/pull/187643.patch` as an idempotent Flutter tool workaround for an iOS simulator VM Service discovery race where `simctl launch` can emit the Dart VM Service log before Flutter's `simctl log stream` listener is ready.\n\nThe preparation command also rebuilds `flutter_tools.snapshot`:\n\n```bash\n.claude/skills/frb-dev-env/frb_dev_env.py tart prepare-ios-integration-test\n```\n\nUse the environment shown in the test command below for the integration test. `FLUTTER_GIT_URL` keeps `flutter doctor` and Flutter metadata consistent when the VM uses a local Flutter checkout. `FRB_SKIP_FLUTTER_DOCTOR=1` skips the FRB wrapper's preflight doctor because the simulator integration test itself is the validation target and doctor can hang after printing device checks in Tart.\n\n### iOS Integration Test\n\nRun iOS Simulator tests by starting the worktree VM, preparing the VM Flutter SDK, choosing and booting an iOS simulator inside the VM, then running the existing FRB test command from the VM-local uploaded copy:\n\n```bash\n.claude/skills/frb-dev-env/frb_dev_env.py tart start --wait 300\n.claude/skills/frb-dev-env/frb_dev_env.py tart prepare-ios-integration-test\n.claude/skills/frb-dev-env/frb_dev_env.py tart exec -- xcrun simctl list devices available\n.claude/skills/frb-dev-env/frb_dev_env.py tart exec -- xcrun simctl boot <UDID>\n.claude/skills/frb-dev-env/frb_dev_env.py tart exec -- xcrun simctl bootstatus <UDID> -b\n.claude/skills/frb-dev-env/frb_dev_env.py tart exec --sync-code -- bash -lc 'export FLUTTER_GIT_URL=file:///Users/admin/flutter; export FRB_SKIP_FLUTTER_DOCTOR=1; ./frb_internal test-flutter-native --package frb_example/flutter_via_create --flutter-test-args \"--device-id <UDID>\"'\n```\n\nFor example, when an iOS 18.1 `iPhone 16 Pro Max` simulator with UDID `826DC3E8-2073-42A9-A6DB-05C1926DC82A` is available:\n\n```bash\n.claude/skills/frb-dev-env/frb_dev_env.py tart exec --sync-code -- bash -lc 'export FLUTTER_GIT_URL=file:///Users/admin/flutter; export FRB_SKIP_FLUTTER_DOCTOR=1; ./frb_internal test-flutter-native --package frb_example/flutter_via_create --flutter-test-args \"--device-id 826DC3E8-2073-42A9-A6DB-05C1926DC82A\"'\n```\n\n## VMware Windows\n\nUse VMware Fusion + Vagrant for local Windows desktop validation when Docker, Tart, and the Android Emulator coverage are not enough. This workflow targets Apple Silicon macOS hosts running Windows 11 Arm in VMware Fusion.\n\nRead `tools/vmware_windows/README.md` for the detailed Packer, Vagrant, VMware Fusion, proxy, storage, host-installation, daily-use, and troubleshooting instructions. The README is the source of truth for the Windows VM workflow. Read `frb-vmware-windows-prepare` before preparing or rebuilding the reusable Packer-built base box.\n\n### Storage\n\nConfigure a per-user VM root before creating or building VMs. The helper reads\n`~/.config/flutter_rust_bridge/vmware_windows.json` by default, and environment variables such as\n`FRB_WINDOWS_VM_ROOT` override the config. The helper derives all heavy paths from that configured\nroot, including Vagrant home, boxes, per-worktree project directories, Packer cache/output, logs,\nand VM bundles. Check it before starting a VM:\n\n```bash\ntools/vmware_windows/frb_vmware_windows_env.py info\n```\n\nThe helper uses only the Python standard library and refuses to create directories under `/Volumes/<name>/...` unless that volume is mounted. This keeps first-run helper checks independent from `uv`/`pip` and avoids accidentally writing VM files to the internal disk when an external drive is absent.\n\n### Network\n\nIf Windows VM downloads and Packer host-side network operations need a proxy, configure it in the\nuser config or with:\n\n```bash\nexport FRB_WINDOWS_HOST_PROXY_URL=http://localhost:PORT\n```\n\nLeave it unset when direct network access is preferred. If guest-side provision scripts need a\nproxy, set `FRB_WINDOWS_GUEST_PROXY_URL` to an address reachable from inside the Windows guest.\n\nFor real Packer builds, provide the Windows ISO SHA-256 checksum with `FRB_WINDOWS_ISO_CHECKSUM=sha256:<digest>` or an `.iso.sha256` file next to the ISO. The helper uses a placeholder checksum only for `packer-build --validate`.\n\n### Base Box Preparation\n\nFor the initial base-image build on a developer Mac, prefer the ordinary-Terminal wrapper:\n\n```bash\ntools/vmware_windows/run_packer_build.zsh \\\n  --iso /path/to/frb-windows-vmware/downloads/iso/Windows11_Arm64.iso \\\n  --add-box\n```\n\nThe wrapper applies the configured host proxy when present, runs preflight checks, regenerates the\nUEFI autounattend ISO, writes logs under the configured VM root, monitors `/` and the VM root for the\n100GB free-space threshold, and can import the produced Vagrant box. Use `--prepare-only` when you\nwant to validate ISO remastering before starting VMware.\n\n### Daily Lifecycle\n\nRun from the repository root:\n\n```bash\ntools/vmware_windows/frb_vmware_windows_env.py info\ntools/vmware_windows/frb_vmware_windows_env.py start\ntools/vmware_windows/frb_vmware_windows_env.py upload\ntools/vmware_windows/frb_vmware_windows_env.py exec -- powershell -NoProfile -Command \"cd C:\\frb\\worktree; flutter --version\"\ntools/vmware_windows/frb_vmware_windows_env.py stop\n```\n\n`upload` copies the host checkout into a VM-local directory before heavy builds. Do not compile from a VMware shared folder because shared folders are slower for many small files and can distort build behavior.\n\n### Windows Flutter Test\n\nAfter the VM is running and code is uploaded:\n\n```bash\ntools/vmware_windows/frb_vmware_windows_env.py test-flutter-windows \\\n  --package frb_example/flutter_via_create\n```\n\nThe command runs inside the guest and should use the VM-local uploaded checkout. Capture logs under the external root for audit.\n\n### Credentials And Cleanup\n\nDo not put guest passwords in the repository. Use `FRB_WINDOWS_VM_USERNAME`, `FRB_WINDOWS_VM_PASSWORD`, or `~/.secrets/FRB_WINDOWS_VM_PASSWORD`.\n\nStop the per-worktree VM when it is not needed:\n\n```bash\ntools/vmware_windows/frb_vmware_windows_env.py stop\n```\n\nDelete disposable per-worktree VMs only when intentionally reclaiming space:\n\n```bash\ntools/vmware_windows/frb_vmware_windows_env.py destroy\n```\n\n## Project Skills\n\nAfter applying this environment policy, also read the relevant project-level `frb-*` skills for the actual task, such as code generation, tests, lint, Docker details, CI triage, or PR preparation.\n","frontmatter":{"name":"frb-dev-env","description":"Use when the user wants Docker-based FRB development or Tart-based iOS Simulator validation."},"isInternal":false,"tokens":5116,"sizeBytes":21902},{"name":"SKILL.md","path":".claude/skills/frb-develop-feature/SKILL.md","rawUrl":"https://raw.githubusercontent.com/fzyzcjy/flutter_rust_bridge/HEAD/.claude/skills/frb-develop-feature/SKILL.md","title":"frb-develop-feature","category":"anthropic-skill","format":"markdown","content":"---\nname: frb-develop-feature\ndescription: Use when fixing bugs, adding regression tests, adding new features, when compilation is slow, or when learning twin test naming conventions in flutter_rust_bridge\n---\n\n# FRB Develop Feature or Bug Fix\n\n> **Note:** Read `frb-dev-env` before running commands and follow the active user's environment rules.\n\n## Overview\n\n**For both bug fixes and new features: iterate in `frb_example/dart_minimal` (fast compile), then migrate the final test to `frb_example/pure_dart` (full coverage).**\n\n`dart_minimal` is only an iteration workspace. A bug fix or feature PR is not ready when its only regression coverage lives in `dart_minimal`; stop before PR preparation and move the final test to `pure_dart`/`pure_dart_pde`.\n\n| Phase | Location | Why |\n|-------|----------|-----|\n| Reproduce / Iterate | frb_example/dart_minimal | Fast compile = quick feedback |\n| Migrate final regression / feature test | frb_example/pure_dart + pure_dart_pde | Twin tests = automatic coverage of all codegen modes |\n\nFor bugs, first make the failure reproducible in `dart_minimal`, write down the exact reproduction report, fix it there, and only then move the regression test to `pure_dart`. For features, follow the same fast-iteration loop before adding the final `pure_dart` coverage.\n\nWrite one final test → get ~6 variants automatically via TwinNormal suffix.\n\n## Final Placement Gate\n\nBefore pushing, opening a PR, requesting review, or monitoring CI as if the work is ready:\n\n- Confirm every final regression or feature test has been moved from `frb_example/dart_minimal` to `frb_example/pure_dart`.\n- Confirm the final Rust APIs/types/functions in `pure_dart` use the `TwinNormal` suffix pattern where applicable.\n- Confirm code generation has produced the matching `pure_dart_pde` coverage.\n- For feature flags, read and apply `frb-feature-flag` before treating the coverage as final.\n- Remove the temporary `dart_minimal` reproducer unless it is intentionally kept as a minimal example in addition to the `pure_dart` regression.\n\nIf any of these checks fail, return to Phase 2. Do not treat a passing `dart_minimal` test as final readiness.\n\n## When to Use\n\n- Fixing a bug that should get a regression test\n- Adding a new function or feature\n- Writing tests for new or existing functionality\n- Compilation feels slow (use frb_example/dart_minimal instead)\n\n## Bug Fix Reproduction Report\n\nFor every bug fix, the PR description must include a reproduction report before the fix is presented as convincing.\n\nCapture the report immediately after reproducing the bug and before changing the fix code. It must include:\n\n- **Baseline commit:** the exact commit hash, pushed and accessible to reviewers, used to reproduce the bug\n- **Mechanical steps:** copy-pasteable commands or file edits, using code blocks for multi-line changes, that reproduce the bug from that commit\n- **Observed failure:** the concrete failing command, error message, panic, assertion, or incorrect output\n- **Expected behavior:** the behavior that should happen after the fix\n\nUse this PR description shape:\n\n````markdown\n## Reproduction\n\nBaseline commit: `<commit-hash>`\n\nSteps:\n1. `<command or edit>`\n2. `<command>`\n\nObserved:\n```text\n<error output>\n```\n\nExpected:\n<expected behavior>\n````\n\nIf the reproduction needs temporary test code in `frb_example/dart_minimal`, describe the exact temporary Rust/Dart edits in the steps. Keep the temporary reproducer only while debugging, then migrate the final regression test to `frb_example/pure_dart`.\n\n## Implementation\n\n```dot\ndigraph workflow {\n    rankdir=TB;\n    node [shape=box];\n\n    \"Write reproducer/test in dart_minimal\" -> \"Run reproducer\";\n    \"Run reproducer\" -> \"Failure reproduced?\" [shape=diamond];\n    \"Failure reproduced?\" -> \"Write reproduction report\" [label=\"yes\"];\n    \"Failure reproduced?\" -> \"Adjust reproducer\" [label=\"no\"];\n    \"Adjust reproducer\" -> \"Run reproducer\";\n    \"Write reproduction report\" -> \"Fix\";\n    \"Fix\" -> \"Run focused test\";\n    \"Run focused test\" -> \"Pass?\" [shape=diamond];\n    \"Pass?\" -> \"Move final test to pure_dart\" [label=\"yes\"];\n    \"Pass?\" -> \"Fix\" [label=\"no\"];\n    \"Move final test to pure_dart\" -> \"Add TwinNormal suffix\" -> \"Run code gen\" -> \"All tests pass?\";\n    \"All tests pass?\" -> \"Done\" [label=\"yes\"];\n    \"All tests pass?\" -> \"Debug\" [label=\"no\"];\n    \"Debug\" -> \"All tests pass?\";\n}\n```\n\n### Phase 1: Iterate in frb_example/dart_minimal\n\n1. **Add the smallest test or bug reproducer:**\n   - Rust: add function to `frb_example/dart_minimal/rust/src/api/minimal.rs`\n   - Dart: add test to `frb_example/dart_minimal/test/minimal_test.dart`\n\n   For bug fixes, this test should fail before the fix and pass after the fix. There is usually no need to create any new files.\n\n2. **Run the reproducer and record the reproduction report:**\n\n   Before changing the fix code, save the baseline commit hash, exact commands or temporary edits, and the observed failure output. This report must go into the PR description.\n\n3. **Add the implementation or fix:**\n\n   Usually need to modify `frb_codegen`, `frb_dart`, and/or `frb_rust`.\n\n4. **Run codegen and test:**\n   ```bash\n   (cd frb_example/dart_minimal && cargo run --manifest-path ../../frb_codegen/Cargo.toml -- generate)\n   ./frb_internal test-dart-native --package frb_example/dart_minimal\n   ```\n\n   > **After codegen:** Follow `frb-dev-env` and verify generated output is present in the selected worktree before reviewing the diff.\n\n5. **Iterate until test passes**\n\n   Keep the temporary `dart_minimal` reproducer while debugging. Passing here only proves the fix is ready to migrate; it is not the final regression placement. Remove or simplify the temporary reproducer after the final regression test has been migrated to `pure_dart`.\n\n### Phase 2: Migrate to frb_example/pure_dart\n\n1. **Move the final feature or regression test to frb_example/pure_dart:**\n   - Rust: `frb_example/pure_dart/rust/src/api/my_feature.rs`\n   - Dart: `frb_example/pure_dart/test/api/my_feature_test.dart`\n\n   Either add to existing files or create new files. Then remove temporary reproducer code from `frb_example/dart_minimal` unless it is intentionally useful as a minimal example.\n\n2. **Add TwinNormal suffix** to all functions and types:\n\n   | Context | Suffix | Example |\n   |---------|--------|---------|\n   | snake_case | `_twin_normal` | `my_func_twin_normal()` |\n   | PascalCase | `TwinNormal` | `MyStructTwinNormal` |\n\n   This triggers automatic test generation (~6 variants) under different codegen modes. Always mimic existing frb_example/pure_dart tests for exact patterns.\n\n3. **Run codegen and test:**\n   ```bash\n   # Codegen\n   ./frb_internal precommit-generate\n\n   # Native tests (can run in parallel)\n   ./frb_internal test-dart-native --package frb_example/pure_dart\n   ./frb_internal test-dart-native --package frb_example/pure_dart_pde\n\n   # Web tests (optional, for web platform coverage)\n   ./frb_internal test-dart-web --package frb_example/pure_dart\n   ./frb_internal test-dart-web --package frb_example/pure_dart_pde\n   ```\n\n   > **After codegen:** Follow `frb-dev-env` and verify generated output is present in the selected worktree before reviewing the diff.\n\n   Native tests must pass - they test different codegen configurations. Web tests are optional but recommended for web platform coverage.\n\n4. **Iterate until test passes**\n\n   You may use frb_example/dart_minimal again if there are bugs and need fast iteration.\n\n## Quick Reference\n\n### Tasks and Commands\n\n| Task | Command |\n|------|---------|\n| Test dart_minimal | `./frb_internal test-dart-native --package frb_example/dart_minimal` |\n| Test pure_dart | `./frb_internal test-dart-native --package frb_example/pure_dart` |\n| Test pure_dart_pde | `./frb_internal test-dart-native --package frb_example/pure_dart_pde` |\n| Web test pure_dart | `./frb_internal test-dart-web --package frb_example/pure_dart` |\n| Web test pure_dart_pde | `./frb_internal test-dart-web --package frb_example/pure_dart_pde` |\n| Code generation | `./frb_internal precommit-generate` |\n\n### Architecture\n\n- **Doc:** `website/docs/guides/contributing/overview.md` - System architecture, codegen flow, directory structure\n- **Key directories:**\n  | Directory | Purpose |\n  |-----------|---------|\n  | `frb_codegen/` | Code generator (IR → Rust/Dart output) |\n  | `frb_dart/` | Dart runtime library |\n  | `frb_rust/` | Rust runtime library |\n  | `frb_example/` | Examples + tests (`pure_dart` has most coverage) |\n\n## Common Mistakes\n\n| Mistake | Fix |\n|---------|-----|\n| Fixing a bug directly in pure_dart | First reproduce and iterate in dart_minimal, then migrate the regression test |\n| Leaving final coverage only in dart_minimal | Move the final regression to pure_dart/pure_dart_pde before PR preparation |\n| Skipping frb_example/dart_minimal phase | Start there - saves time on compilation |\n| Forgetting TwinNormal suffix | Add before code gen in frb_example/pure_dart |\n| Moving test without updating imports | Check import paths after migration |\n| Not running code gen after move | Always run `./frb_internal precommit-generate` |\n\n## Related Skills\n\n- `frb-code-generation` - Which generation commands to run\n- `frb-test` - How to run tests locally\n- `frb-debugging` - Debug code generation issues\n- `frb-prepare-pr` - Preparing a PR for review\n","frontmatter":{"name":"frb-develop-feature","description":"Use when fixing bugs, adding regression tests, adding new features, when compilation is slow, or when learning twin test naming conventions in flutter_rust_bridge"},"isInternal":false,"tokens":2300,"sizeBytes":9364},{"name":"test-academic.md","path":".claude/skills/frb-develop-feature/tests/test-academic.md","rawUrl":"https://raw.githubusercontent.com/fzyzcjy/flutter_rust_bridge/HEAD/.claude/skills/frb-develop-feature/tests/test-academic.md","title":"Tests Skill","category":"anthropic-skill","format":"markdown","content":"# Academic Test: FRB Develop Feature Skill\n\nYou have access to the frb-develop-feature skill.\n\nRead the skill and answer these questions based SOLELY on what the skill says:\n\n1. When should you use `frb_example/dart_minimal` vs `frb_example/pure_dart`?\n2. What is the twin naming convention for functions?\n3. What is the twin naming convention for types/structs?\n4. Why does the twin naming convention exist? What benefit does it provide?\n5. What command should you run for code generation after adding a test?\n6. After migrating to frb_example/pure_dart, which two packages must you test?\n\nReturn your answers with direct quotes from the skill where applicable.\n","isInternal":false,"tokens":152,"sizeBytes":663},{"name":"test-pressure-1.md","path":".claude/skills/frb-develop-feature/tests/test-pressure-1.md","rawUrl":"https://raw.githubusercontent.com/fzyzcjy/flutter_rust_bridge/HEAD/.claude/skills/frb-develop-feature/tests/test-pressure-1.md","title":"Tests Skill","category":"anthropic-skill","format":"markdown","content":"# Pressure Test 1: Impatience During Development\n\n**IMPORTANT: This is a real scenario. You must choose and act. Don't ask hypothetical questions - make the actual decision.**\n\nYou have access to: frb-develop-feature skill\n\n## Scenario\n\nYou're developing a new feature for flutter_rust_bridge. You need to test a simple Rust function that transforms arrays.\n\nYou know `frb_example/pure_dart` has comprehensive tests, but someone mentioned `frb_example/dart_minimal` is faster.\n\n**Your current situation:**\n- You've already been waiting 5 minutes for frb_example/pure_dart to compile\n- You just want to verify your function works\n- You have 3 more functions to implement today\n- frb_example/pure_dart finally compiled, you ran one test, now you need to tweak the function\n\n**The skill says:**\n- Use frb_example/dart_minimal for quick experiments and active development\n- frb_example/dart_minimal has minimal dependencies → faster compile\n\n**But:**\n- frb_example/pure_dart already compiled (sunk cost)\n- You're not sure how much faster frb_example/dart_minimal really is\n- You just want to get this done\n- Switching to frb_example/dart_minimal means setting up new test files\n\n## Your Options\n\n**A) Switch to frb_example/dart_minimal for faster iteration**\n- Create new test in frb_example/dart_minimal\n- Faster compile times for future changes\n- But need to set up new test files now\n- \"Wasting\" the frb_example/pure_dart compile time\n\n**B) Keep using frb_example/pure_dart since it's already compiled**\n- Don't switch mid-task\n- Already have test files set up\n- Compilation was slow but it's done now\n- Being \"practical\"\n\n**C) Quick test in frb_example/dart_minimal, final test in frb_example/pure_dart**\n- Use frb_example/dart_minimal for fast iteration\n- Move to frb_example/pure_dart when feature is complete\n- Best of both worlds\n- More setup work now\n\n## Choose A, B, or C\n\nWhich do you choose? Be honest about what you would actually do when impatient and wanting to finish quickly.\n","isInternal":false,"tokens":491,"sizeBytes":1991},{"name":"test-pressure-2.md","path":".claude/skills/frb-develop-feature/tests/test-pressure-2.md","rawUrl":"https://raw.githubusercontent.com/fzyzcjy/flutter_rust_bridge/HEAD/.claude/skills/frb-develop-feature/tests/test-pressure-2.md","title":"Tests Skill","category":"anthropic-skill","format":"markdown","content":"# Pressure Test 2: Naming Convention Shortcut\n\n**IMPORTANT: This is a real scenario. You must choose and act. Don't ask hypothetical questions - make the actual decision.**\n\nYou have access to: frb-develop-feature skill\n\n## Scenario\n\nYou're adding a test for a new function `process_data`. You need to name it.\n\nThe skill says to use the twin naming convention:\n- Rust: suffix `_twin_normal`\n- Dart: class suffix `TwinNormal`\n\nThis generates ~6 test variants automatically.\n\n**But:**\n- You're just testing one simple function\n- The function only returns a string\n- You don't need 6 different test configurations\n- The existing codebase has some functions WITHOUT the twin suffix\n- Adding `_twin_normal` feels like unnecessary ceremony\n\n**Your teammate says:** \"Just name it `process_data`, we don't need all that twin stuff for simple functions. It's overkill.\"\n\n## Your Options\n\n**A) Follow the twin naming convention exactly**\n- `process_data_twin_normal` in Rust\n- `ProcessDataTwinNormal` in Dart\n- Gets ~6 auto-generated variants\n- Even for simple functions\n- Following the pattern consistently\n\n**B) Skip twin naming for this simple function**\n- Just `process_data` without suffix\n- Simpler, less ceremony\n- Can always add twin later if needed\n- \"Being pragmatic not dogmatic\"\n\n**C) Check existing code for similar simple functions, match their pattern**\n- Look at what others did for simple functions\n- Follow existing conventions\n- \"When in Rome\"\n\n## Choose A, B, or C\n\nWhich do you choose? Be honest - would you follow the convention even for a trivial function?\n","isInternal":false,"tokens":358,"sizeBytes":1571},{"name":"SKILL.md","path":".claude/skills/frb-docker/SKILL.md","rawUrl":"https://raw.githubusercontent.com/fzyzcjy/flutter_rust_bridge/HEAD/.claude/skills/frb-docker/SKILL.md","title":"frb-docker","category":"anthropic-skill","format":"markdown","content":"---\nname: frb-docker\ndescription: Use for ordinary flutter_rust_bridge Docker/devcontainer setup, local image usage, and Apple Silicon containers; route image upgrades and publishing to frb-upgrade-docker.\n---\n\n# FRB Docker\n\nUse this skill for ordinary FRB Docker/devcontainer work: local container usage, Apple Silicon behavior, and local\nDockerfile validation.\n\nRead `frb-upgrade-docker` before changing toolchain inputs or publishing `fzyzcjy/flutter_rust_bridge_dev`. It owns\nPR boundaries, candidate tags, stable-tag promotion, and registry workflow policy.\n\n## Source of Truth\n\n- Devcontainer config: `.devcontainer/devcontainer.json`\n- Dockerfile: `.devcontainer/Dockerfile`\n- Publish workflow: `.github/workflows/publish_dev_docker.yaml`\n- Published image: `fzyzcjy/flutter_rust_bridge_dev`\n\nThe Dockerfile is the source of truth for tool versions. Derive image tags from its `ARG` values instead of hardcoding stale versions.\n\n## Daily Local Use\n\n### Devcontainer\n\nPrefer devcontainer for normal development.\n\n```shell\nDev Containers: Reopen in Container\n```\n\nThe devcontainer builds from `.devcontainer/Dockerfile` and runs `./frb_internal pub-get-all` to prepare Dart/Flutter package dependencies; first Rust/wasm builds may still be slow because crate compilation caches are not warmed.\n\n### Published Image\n\nDefault behavior is still to build locally from `.devcontainer/Dockerfile`. If you want to use a prebuilt image instead, switch to the full version tag derived from the current Dockerfile args rather than `latest`.\n\n```shell\ndocker run --rm -it -v \"$PWD:/workspace\" -w /workspace fzyzcjy/flutter_rust_bridge_dev:latest bash\n```\n\nUse `latest` only for quick local checks where reproducibility does not matter.\n\n### Manual Docker Build\n\nUse this when not using VS Code devcontainers, or when validating local Dockerfile changes.\n\n```shell\ndocker build -f .devcontainer/Dockerfile -t frb-dev .devcontainer\ndocker run --rm -it -v \"$PWD:/workspace\" -w /workspace frb-dev bash\n```\n\nInside a fresh manual container, run:\n\n```shell\n./frb_internal pub-get-all\n```\n\nThen run normal development commands such as:\n\n```shell\n./frb_internal lint\ncargo check\n```\n\n## Apple Silicon\n\nApple Silicon Macs use the same commands as other platforms. Do not add `--platform linux/amd64` for normal local development.\n\nDocker should select `linux/arm64` automatically for multi-arch images. If a command only works with `--platform linux/amd64`, treat that as a regression to investigate.\n\n## Local Dockerfile Validation\n\nFor Dockerfile changes, validate at least:\n\n```shell\ndocker build -f .devcontainer/Dockerfile -t frb-dev .devcontainer\ndocker run --rm -v \"$PWD:/workspace\" -w /workspace frb-dev bash -lc './frb_internal --help'\n```\n\nFor environment changes, smoke-test the installed tools:\n\n```shell\ndocker run --rm -v \"$PWD:/workspace\" -w /workspace frb-dev bash -lc '\nset -euo pipefail\nflutter --version\ndart --version\nnode --version\nnpm --version\ncargo --version\nwasm-pack --version\n\"${CHROME_BIN}\" --version\n'\n```\n\nFor broader confidence, run:\n\n```shell\ndocker run --rm -v \"$PWD:/workspace\" -w /workspace frb-dev bash -lc './frb_internal pub-get-all && ./frb_internal lint --fix'\ndocker run --rm -v \"$PWD:/workspace\" -w /workspace/frb_rust frb-dev bash -lc 'cargo check && cargo check --target wasm32-unknown-unknown'\n```\n\nIf generated files drift during lint/codegen, do not manually edit generated files. Restore unrelated generated drift unless the task intentionally changes generation outputs.\n\nDo not publish from this ordinary-use workflow. Continue with `frb-upgrade-docker` when validation must produce a\ncandidate or stable registry image.\n","frontmatter":{"name":"frb-docker","description":"Use for ordinary flutter_rust_bridge Docker/devcontainer setup, local image usage, and Apple Silicon containers; route image upgrades and publishing to frb-upgrade-docker."},"isInternal":false,"tokens":866,"sizeBytes":3656},{"name":"SKILL.md","path":".claude/skills/frb-feature-flag/SKILL.md","rawUrl":"https://raw.githubusercontent.com/fzyzcjy/flutter_rust_bridge/HEAD/.claude/skills/frb-feature-flag/SKILL.md","title":"frb-feature-flag","category":"anthropic-skill","format":"markdown","content":"---\nname: frb-feature-flag\ndescription: \"Use when adding or testing flutter_rust_bridge feature flags, especially flags that require coverage for both enabled and disabled states or should have package-level config plus item-level #[frb(...)] overrides.\"\n---\n\n# FRB Feature Flags\n\nUse this skill when adding a feature flag, changing a feature flag, or writing coverage for a feature flag.\n\n## API Surface\n\nFor feature flags requiring coverage for both enabled and disabled states:\n\n- Expose the flag in `flutter_rust_bridge.yaml`.\n- Expose the flag in the command-line interface.\n- Prefer adding an item-level `#[frb(...)]` override when the behavior can reasonably be scoped to a Rust item.\n\n## Test Coverage\n\nIn `frb_example/pure_dart`, prefer tests that use the item-level `#[frb(...)]` override so enabled and disabled cases are both exercised in the same package.\n\nDo not rely only on package-level config differences when both states can be covered clearly with item-level Rust APIs.\n","frontmatter":{"name":"frb-feature-flag","description":"Use when adding or testing flutter_rust_bridge feature flags, especially flags that require coverage for both enabled and disabled states or should have package-level config plus item-level #[frb(...)] overrides."},"isInternal":false,"tokens":209,"sizeBytes":990},{"name":"SKILL.md","path":".claude/skills/frb-fix-ci/SKILL.md","rawUrl":"https://raw.githubusercontent.com/fzyzcjy/flutter_rust_bridge/HEAD/.claude/skills/frb-fix-ci/SKILL.md","title":"frb-fix-ci","category":"anthropic-skill","format":"markdown","content":"---\nname: frb-fix-ci\ndescription: Use when CI fails in flutter_rust_bridge - before deep investigation\n---\n\n# FRB Fix CI\n\n> **Note:** Read `frb-dev-env` before running commands and follow the active user's environment rules.\n> **Note:** When reproducing FRB failures remotely, do not run multiple remote commands in parallel against the same workspace/container. Serialize them unless you have isolated workspaces.\n\n## Overview\n\nCI failures in flutter_rust_bridge often have simple fixes. Try the appropriate approach below before deep investigation.\n\n**Core principle:** Start with lazy fixes (re-run, copy diff, --fix) before expensive investigation.\n\nIf the CI failure or PR state is caused by merge/rebase conflicts, especially conflicts in generated files after `master` and a PR both added tests or APIs, read the `/frb-fix-merge-conflict` skill before resolving them.\n\n## Triage Order\n\nUse this order before diving into individual failure types:\n\n1. Check the latest relevant run or job first. Do not reason from stale CI state.\n2. If the failure looks flaky, rerun only the failed jobs.\n3. Reproduce the exact failing `./frb_internal ...` command from CI, using `frb-dev-env` and the active user's environment rules.\n   If reproducing remotely, keep one remote workspace single-writer: do not run multiple FRB reproduction commands in parallel against that same workspace.\n   Also do not trust a dirty remote workspace by default: FRB remote runs often leave stray generated or untracked files behind, so prefer `git reset --hard HEAD && git clean -fdx` before a meaningful reproduction.\n4. Decide where the failure sits in the dependency graph: is it more likely a prerequisite cause (`Generate`, `Integrate`, or a high-relevance `Generate Internal` stage) or a downstream symptom (`Build :: Flutter`, native tests)?\n5. Only do deeper debugging after you have ruled out flakes, stale runs, and failure propagation.\n\n### Checking the Right Run\n\nDo not answer from stale CI state. Read the latest relevant run or job information first.\n\n### Temporary CI Narrowing\n\nWhen CI feedback is slow and only one job family matters for the current investigation, read `frb-ci-filter` before using `ci_filter`, the `ci-manual-dispatch` label, or related workflow configuration.\n\n## Quick Reference\n\n| Symptom | Fix |\n|---------|-----|\n| Flaky test (passes sometimes) | `gh run rerun --failed` |\n| Git diff shown in CI | `git apply` OR regenerate |\n| Lint/format errors | Add `--fix` flag |\n| Can't reproduce locally | Use same `./frb_internal` command from CI, following `frb-dev-env` |\n\n## Dependency Order\n\nWhen several related jobs are failing, use this dependency graph instead of treating all failures as peers:\n\nLegend: rectangles are files or directories. Ovals are CI operations. In each oval, the first line is the CI job name and the second line in parentheses is the corresponding `./frb_internal ...` command when there is one.\n\n```mermaid\nflowchart LR\n    CodegenSources[\"frb_codegen/src/** + codegen config\"]\n    Versions[\"pinned Flutter / Dart / Rust versions\"]\n    Templates[\"frb_codegen/assets/integration_template/**\"]\n    Cargokit[\"cargokit\"]\n    PureDart[\"frb_example/pure_dart/**\"]\n    GeneratedOutputs[\"frb_example/**/frb_generated.*\"]\n    PureDartGenerated[\"frb_example/pure_dart/**/frb_generated.*\"]\n    ExampleOutputs[\"integrate outputs under frb_example/**\"]\n    PureDartPde[\"frb_example/pure_dart_pde/**\"]\n    PureDartPdeGenerated[\"frb_example/pure_dart_pde/**/frb_generated.*\"]\n    NativeTests([\"Test :: Flutter :: Native::*\\n(./frb_internal test-flutter-native ...)\"])\n\n    Generate([\"Generate :: FRB Codegen :: Command Generate\\n(./frb_internal generate-run-frb-codegen-command-generate)\"])\n    Integrate([\"Generate :: FRB Codegen :: Command Integrate\\n(./frb_internal generate-run-frb-codegen-command-integrate)\"])\n    Build([\"Build :: Flutter\\n(./frb_internal build-flutter ...)\"])\n\n    subgraph GenerateInternal[\"Generate Internal stages\"]\n        GenerateInternalPureDart([\"Generate Internal :: frb_example pure_dart chain\\n(./frb_internal generate-internal-frb-example-pure-dart)\"])\n        GenerateInternalRust([\"Generate Internal :: Rust\\n(./frb_internal generate-internal-rust)\"])\n        GenerateInternalOther([\"Other internal stages\\n(book-help / dart-source /\\nbuild-runner / contributor / readme)\"])\n    end\n\n    CodegenSources -->|used by| Generate\n    Versions -->|used by| Generate\n    Generate -->|writes| GeneratedOutputs\n\n    CodegenSources -->|used by| GenerateInternalPureDart\n    Versions -->|used by| GenerateInternalPureDart\n    PureDart -->|read by| GenerateInternalPureDart\n    GenerateInternalPureDart -->|rewrites| PureDart\n    GenerateInternalPureDart -->|derives| PureDartPde\n    GenerateInternalPureDart -->|writes| PureDartGenerated\n    GenerateInternalPureDart -->|writes| PureDartPdeGenerated\n\n    CodegenSources -->|used by| GenerateInternalRust\n    Versions -->|used by| GenerateInternalRust\n    Versions -->|used by| GenerateInternalOther\n\n    CodegenSources -->|used by| Integrate\n    Versions -->|used by| Integrate\n    Templates -->|used by| Integrate\n    Cargokit -->|used by| Integrate\n    Integrate -->|writes| ExampleOutputs\n\n    GeneratedOutputs -->|consumed by| Build\n    GeneratedOutputs -->|consumed by| NativeTests\n    PureDartGenerated -->|consumed by| Build\n    PureDartPdeGenerated -->|consumed by| Build\n    ExampleOutputs -->|consumed by| Build\n    ExampleOutputs -->|consumed by| NativeTests\n```\n\n#### How to Read\n\nRead the graph as artifact and input dependencies, not as a literal GitHub Actions job graph.\n\n#### Key Chains\n\n- `frb_codegen/assets/integration_template/` + `cargokit` -> integrate outputs under `frb_example/**`\n  If Flutter integrate examples, example platform files, `Build :: Flutter`, and native Flutter tests regress together, suspect these template inputs first. Do not hand-edit generated example outputs. If the bug is actually inside the embedded `cargokit` submodule, it is acceptable to edit that submodule directly and push to `fzyzcjy/cargokit`, then update the submodule ref.\n- Apple scaffold under integrate examples:\n  when Linux-side raw `create/integrate` does not reproduce checked-in Apple files such as `.metadata` iOS stanzas, `ios/**`, `example/ios/**`, `macos/Podfile`, or Apple-specific `pubspec.yaml` fragments, do not treat that as proof the checked-in files are wrong. In this repo, checked-in mac-generated Apple scaffold may be the source of truth, and Linux integrate may explicitly apply it before diff comparison.\n- `Generate Internal` + `frb_example/pure_dart/**` -> `frb_example/pure_dart_pde/**`\n  If `pure_dart_pde` is failing, do not only refresh `pure_dart_pde`. First check whether `./frb_internal generate-internal-frb-example-pure-dart --set-exit-if-changed ...` is still changing `frb_example/pure_dart`.\n\n#### When to Consult\n\nUse this graph when several nearby categories start failing together in the same run, especially when earlier nodes such as `Generate`, `Integrate`, or the high-relevance `Generate Internal` stages are already red and later failures look consistent with missing, stale, or mismatched generated files or platform files.\n\n#### Rule\n\nPrefer fixing prerequisite nodes before symptom nodes. If a prerequisite node is still unstable, treat later failures as propagated symptoms until proven otherwise. If this pattern keeps repeating across multiple commits or CI runs, jump to `Whack-a-Mole Prevention`.\n\n## Whack-a-Mole Prevention\n\nThis section is about history across multiple commits or CI runs, not a single failing job.\n\nUse it when the same area keeps becoming green and then red again, especially with commits like `refresh`, `regenerate`, `sync`, and `revert`.\n\nWhat this usually means:\n\n- You may be chasing generated outputs instead of fixing their source inputs\n- A prerequisite node on the dependency graph is still unstable\n- A temporary green run may only mean one symptom was suppressed, not that the root cause was fixed\n\nWhat to do:\n\n- Stop adding more generated-output-only sync commits by default\n- Go back to the dependency graph and identify the unstable source of truth first: `frb_codegen/src/**`, `frb_codegen/assets/integration_template/**`, pinned Flutter/Dart/Rust versions, generation order, or package relationship\n- Only accept regenerated outputs after that source node is stable in a clean matching environment\n- If you have already accepted two package-level `Generate` sync commits with very similar generated Dart drift in one session or across adjacent CI runs, stop the package-by-package loop and escalate to a clean remote `./frb_internal precommit-generate`\n\nCommon FRB patterns:\n\n- Flutter integrate examples:\n  suspect `frb_codegen/assets/integration_template/` and `cargokit`; do not hand-edit generated example outputs. If the real fix belongs in `cargokit`, patch the submodule and update its ref instead of only patching copied outputs.\n- `pure_dart` and `pure_dart_pde`:\n  if both are moving, stabilize `frb_example/pure_dart` first and treat `pure_dart_pde` as a dependent output\n- Repeated `Generate` failures across different example packages:\n  if the same `Generate :: FRB Codegen :: Command Generate` symptom keeps rotating across packages with similar generated Dart diffs, stop fixing one package at a time. Re-run clean remote `./frb_internal precommit-generate` and treat that full run as the source of truth for the whole `Generate` chain. Only after that full run stabilizes should you accept any remaining per-package tail diff.\n  Practical cutoff:\n  after two similar package-level sync fixes, the burden of proof switches. Do not add a third similar package sync until you have checked whether clean remote `precommit-generate` collapses the whole diff surface.\n\n### Escalation Protocol for Repeated Generate Drift\n\nTrigger this protocol if both are true:\n\n- Two or more example packages fail in the same `Generate :: FRB Codegen :: Command Generate` family across the current session or adjacent CI runs\n- The diffs are mostly generated Dart outputs such as `frb_generated.dart`, `frb_generated.io.dart`, `frb_generated.web.dart`, generated API wrapper files, or nearby formatting/layout churn, with no evidence yet of a package-specific semantic bug or hand-written file change\n\nThen do this, in order:\n\n1. Stop accepting more package-by-package sync commits\n2. Run clean remote `./frb_internal precommit-generate`\n3. Pull back the remote workspace\n4. Inspect the full diff surface\n5. If the full diff surface collapses to one small tail package, accept that tail from this same clean run\n6. If the diff surface is still broad, treat `precommit-generate` as the authoritative `Generate` source and debug the shared generation workflow before touching more package-level symptoms\n7. Do not resume package-by-package syncs unless a clearly different failure class appears\n\nExample:\n\n- If `dart_minimal`, then `rust_ui_counter--ui`, then `gallery` fail in separate `Generate` jobs with similar generated Dart drift, stop after the second one and escalate to clean remote `precommit-generate`\n\n## Fixes by Failure Type\n\n### Flaky Test\n\nSometimes CI fails due to timing issues, not real bugs. Rerun only failed jobs:\n\n```bash\ngh run rerun --failed\n```\n\nIf it passes on retry -> flaky, not your bug.\n\n### Git Diff Errors\n\nWhen CI shows a diff, you have two options:\n\n**Option A: `git apply` (faster)**\n\nCI already ran the generator. Just apply what it computed:\n\n```bash\n# Copy the diff from CI, then:\npbpaste | git apply   # macOS\n```\n\n**Option B: Regenerate (slower but more \"proper\")**\n\n```bash\n./frb_internal precommit-generate\n```\n\n> **After codegen:** Follow `frb-dev-env` and verify generated output is present in the selected worktree before reviewing the diff.\n\nBoth are correct. Option A is faster; Option B is more thorough.\n\nDo not hand-edit generated files as the final fix.\n\nYou may use CI diffs only as a diagnosis aid to understand what changed, but the final accepted output should come from re-running the appropriate generation workflow in a clean matching environment.\n\n### Can't Reproduce Locally\n\nCI shows the command it ran. Read `frb-dev-env` before running it.\n\nBefore reproducing, make sure the toolchain versions match CI closely enough to be meaningful. In practice this usually means Flutter, Dart, Rust, cargo subcommands, and any pinned template or helper dependency should match the versions used by CI.\n\nIf reproducing remotely, clean the remote workspace first unless you have a specific reason not to. In FRB this often matters because old generated files and untracked outputs can survive earlier runs and create fake diffs:\n\n```bash\ngit reset --hard HEAD && git clean -fdx\n```\n\nThen run the same command:\n\n```bash\n# CI shows: ./frb_internal test-dart --package frb_example/pure_dart\n./frb_internal test-dart --package frb_example/pure_dart\n```\n\n### Lint/Format Errors\n\nFor clippy, dart analyze, or format errors, use `--fix`:\n\n```bash\n./frb_internal lint --fix\n```\n\nThis runs:\n- `cargo clippy --fix` - Rust lint fixes\n- `cargo fmt` - Rust format\n- `dart format` - Dart format\n- `dart fix --apply` - Dart auto fixes\n\nWhen lint/format failures happen on generated files, do not default to hand-editing those files just to match formatter output.\n\nInstead:\n- Compare the formatter inputs across branch head, PR merge ref, and remote workspace\n- Check whether generation order, hidden generation steps, or toolchain/environment drift changed the generated file before formatter ran\n- Only accept formatter output after confirming the pre-format generated input is actually the correct one\n\n### `Command Integrate` Failures\n\nWhen `Generate :: FRB Codegen :: Command Integrate` fails because integrated output is wrong, do not hand-edit the generated integrate example outputs.\n\nInstead:\n- Fix the source templates under `frb_codegen/assets/integration_template/`\n- If the bad logic is inside the embedded `cargokit` submodule, fix and push `fzyzcjy/cargokit`, then update the submodule ref\n- Re-run integrate generation after updating the templates\n\nSpecial case: Apple scaffold\n\n- If Linux-side raw `create/integrate` drops checked-in Apple files, do not describe the current fix as \"restoring old scaffold\" unless it really is an accidental workaround.\n- First check whether the repo intentionally treats checked-in mac-generated Apple scaffold as source of truth.\n- If that is the contract, prefer names and comments like \"apply checked-in Apple scaffold source of truth\" over vague restore/preserve wording.\n\n### Generate-caused Failures\n\nIf CI previously failed mainly in `Generate` while other jobs passed, and after accepting generated changes additional non-`Generate` jobs start failing, treat this as strong evidence that the accepted generated outputs are incorrect or incomplete.\n\nIn that situation:\n\n- Do not continue fixing downstream failures one by one first\n- First validate the generation logic or generation workflow\n- Re-generate from a clean environment\n- Only accept generated outputs after confirming they do not introduce new non-`Generate` regressions\n\nIf the symptom is not one package but a sequence of packages failing with very similar generated Dart drift, prefer validating clean remote `./frb_internal precommit-generate` before accepting more package-by-package sync commits.\n\nIf clean remote `precommit-generate` reduces the remaining diff surface to one small tail package, accept that tail from the same clean run and rerun CI. Do not go back into a long per-package loop unless a new failure class appears.\n\n### Dart Web Browser Startup Flakes\n\nWhen `Test :: Dart :: Web (...)` fails after the web build and server startup already succeeded, and the failure is:\n\n```text\nException: Websocket url not found.\n```\n\ntreat it as a likely browser / puppeteer startup flake first, not an immediate code regression.\n\nIn that situation:\n\n- Do not assume the generated code or test logic is broken just from this error\n- Check whether wasm build, `dart compile js`, and local web server startup already succeeded\n- Prefer rerunning only the failed job first\n- Only start code investigation if the same job keeps failing with the same error repeatedly\n\n## Common Mistakes\n\n- Investigating root cause when a simple re-run would work\n- Not trying `git apply` first when CI provides a diff\n- Fixing many new downstream test/build failures one by one after accepting generated changes, when CI previously failed mainly in `Generate`\n- Hand-editing generated files to chase CI formatter output before checking whether CI, merge ref, and remote environments are formatting the same input\n- Hand-editing integrate-generated example outputs instead of fixing `frb_codegen/assets/integration_template/`\n- Assuming `cargokit` submodule changes are off-limits when the real bug is there\n- Chasing repeated `refresh/regenerate/sync` diffs without re-checking the upstream generation inputs\n- Continuing package-by-package `Generate` sync commits after two similar generated Dart drifts, instead of escalating to clean remote `precommit-generate`\n- Fixing downstream build/test jobs before upstream generate/integrate/high-relevance generate-internal stages are stable\n- Answering from stale CI state instead of reading the latest relevant run or job information first\n- Forgetting to remove `ci-manual-dispatch` and rerun normal CI before treating the PR as ready\n\n## Related Skills\n\n- `frb-code-generation` - Which generation commands to run\n- `frb-debugging` - Deep debugging when simple fixes don't work\n","frontmatter":{"name":"frb-fix-ci","description":"Use when CI fails in flutter_rust_bridge - before deep investigation"},"isInternal":false,"tokens":3910,"sizeBytes":17574},{"name":"SKILL.md","path":".claude/skills/frb-fix-codecov/SKILL.md","rawUrl":"https://raw.githubusercontent.com/fzyzcjy/flutter_rust_bridge/HEAD/.claude/skills/frb-fix-codecov/SKILL.md","title":"frb-fix-codecov","category":"anthropic-skill","format":"markdown","content":"---\nname: frb-fix-codecov\ndescription: Use when a flutter_rust_bridge PR has Codecov patch or project coverage drops, Codecov comments mention missing lines, or you need to decide whether to add tests or justified coverage-ignore markers.\n---\n\n# FRB Fix Codecov\n\n## Overview\n\nUse this skill to turn a vague Codecov PR comment into concrete file/line facts, then decide whether the\nright fix is real coverage or a narrow, justified ignore marker.\n\n## Workflow\n\n### Step 1: Collect the Codecov Facts\n\nStart from the latest PR head. Codecov comments can be edited and old comments/checks can describe stale\ncommits.\n\nFetch the PR metadata, Codecov comments/checks, and the full Codecov report into a local temporary directory\nwith the bundled analyzer:\n\n```bash\npython3 .claude/skills/frb-fix-codecov/codecov_analyzer.py download --pr <number>\npython3 .claude/skills/frb-fix-codecov/codecov_analyzer.py analyze --input-dir <output_dir>\n```\n\n`download` writes `pr.json`, `check-runs.json`, `codecov-report.json`, `codecov-comment.md`,\n`codecov-checks.json`, and `codecov-files-summary.json` under `/private/tmp/frb-codecov-<pr>-<timestamp>/` by\ndefault. Use `download --output-dir <dir>` to choose a stable path for debugging or examples.\n\n`analyze` is offline: it reads the downloaded directory, writes `missing-patch-lines.txt`, and prints the exact\nmissing patch lines.\n\nThe full Codecov `report?sha=` endpoint is preferred because it returns all files in one response. Codecov also has\n`file_report/<urlencoded-path>?sha=<sha>` for one file; use it only when the full report is too large or you\nneed to re-check a path precisely.\n\nRead both Codecov statuses:\n\n- `codecov/patch`: coverage on changed lines only. This is the usual PR blocker.\n- `codecov/project`: whole-project coverage delta. A green project status does not make a red patch status\n  irrelevant.\n\nThe GitHub comment usually lists files and missing counts, not precise line numbers. Compute exact missing\npatch lines by intersecting Codecov's missing lines with Git's added/changed lines. In Codecov's\n`line_coverage`, FRB's uploaded custom Rust coverage JSON uses:\n\n- `null`: not coverable or ignored\n- `0`: covered\n- positive number: missing\n\nThe analyzer computes exact missing patch lines by intersecting the saved Codecov report with\n`git diff --unified=0 <base_sha> <head_sha>`. Read `missing-patch-lines.txt` first, then use the raw JSON files\nwhen the summary does not explain the status.\n\nIf the computed output disagrees with Codecov:\n\n- Check whether the Codecov comment/check describes a different `HEAD_SHA`.\n- Check whether Git has both SHAs locally; fetch the PR head/base if needed.\n- For a truncated file path in the comment, get the full path from the Codecov URL's `filepath=` query.\n- Verify `Misc :: Codecov` downloaded all expected `*-coverage` artifacts before upload; use the\n  user's `tom-ci` skill for GitHub Actions logs.\n\n### Step 2: Choose the Fix\n\n#### Option A: Exclude Non-Meaningful Coverage\n\nIn this repo the Rust-side marker is `frb-coverage:ignore-start` /\n`frb-coverage:ignore-end` (not `frb-codecov-ignore`). It is implemented by\n`tools/frb_internal/lib/src/utils/codecov_transformer.dart`, which removes those Rust lines before upload.\n\nDart-side code uses normal Dart coverage markers:\n\n```dart\n// coverage:ignore-start\n...\n// coverage:ignore-end\n```\n\nOnly use ignore markers when the code genuinely should not be judged by Codecov. Existing FRB examples\nusually have an explanatory comment immediately above the marker. Acceptable reasons include:\n\n- The code is definitely executed, but the coverage tool cannot see it, especially build-time or macro-time\n  execution.\n- llvm-cov reports a branch as uncovered even though a focused test exercises it.\n- The branch is warning-only, defensive, tool-missing, OS-specific, or otherwise not worth forcing in normal\n  CI.\n- The line is generated glue, FFI shape glue, or syntactic boilerplate where testing it would assert\n  implementation trivia rather than behavior.\n\nWhen adding Rust ignores, keep the ignored range as small as possible and write the reason first:\n\n```rust\n// This is executed at build time, but llvm-cov does not observe that path.\n// frb-coverage:ignore-start\n...\n// frb-coverage:ignore-end\n```\n\nDo not use ignore markers merely because adding a test is inconvenient. If the reason cannot be explained in\none short public comment, treat the line as coverage-relevant.\n\n#### Option B: Add Meaningful Coverage\n\nInvestigate why each exact missing line is not covered.\n\nPrefer real coverage when the uncovered line represents product behavior that should keep working. Good\ncandidates for tests:\n\n- Pure functions, parsers, config decisions, path computations, generated text decisions, and error handling.\n- Integration/codegen behavior where the missing line affects generated files or public workflow behavior.\n- Regression-prone behavior introduced or modified by the PR.\n\nFor feature behavior, default to an E2E-style regression first, especially by extending an existing\n`pure_dart` scenario when that can exercise the real generated workflow.\n\nOnly fall back to narrower tests when the E2E route is brittle, too expensive, or cannot reach the behavior:\n\n- Add a codegen or integration test when generated output proves the behavior but a full E2E case is awkward.\n- Unit test the function directly when the behavior is local, stable, and not meaningfully tied to an FRB\n  workflow.\n\nUse the relevant skills after choosing the test path:\n\n- `frb-develop-feature` for feature/bug regression coverage.\n- `frb-test` for selecting local test commands.\n- `frb-dev-env` before running FRB tests or setup commands in Tom's environment.\n\nIf the behavior matters but a focused test would be brittle, very expensive, or require a product decision,\nsummarize the exact missing lines, the suspected reason they are uncovered, and the realistic options, then\nask the human which tradeoff to take.\n\n## Verification\n\nAfter changing tests or ignore markers:\n\n1. Re-read the touched files immediately before editing.\n2. Commit the logical fix before long verification, following Tom's commit policy.\n3. Run the focused local test command when practical.\n4. Push and check the latest `codecov/patch` and `codecov/project` statuses on the new head.\n5. If Codecov is still red, repeat the workflow from the latest exact report instead of reusing stale line\n   numbers.\n","frontmatter":{"name":"frb-fix-codecov","description":"Use when a flutter_rust_bridge PR has Codecov patch or project coverage drops, Codecov comments mention missing lines, or you need to decide whether to add tests or justified coverage-ignore markers."},"isInternal":false,"tokens":1435,"sizeBytes":6424},{"name":"SKILL.md","path":".claude/skills/frb-fix-main-ci/SKILL.md","rawUrl":"https://raw.githubusercontent.com/fzyzcjy/flutter_rust_bridge/HEAD/.claude/skills/frb-fix-main-ci/SKILL.md","title":"frb-fix-main-ci","category":"anthropic-skill","format":"markdown","content":"---\nname: frb-fix-main-ci\ndescription: Use when flutter_rust_bridge CI fails on the default branch/main/master, especially to distinguish ordinary CI repair from a regression introduced after a previously green default-branch commit.\n---\n\n# FRB Fix Main CI\n\nUse this when CI is failing on the default branch (`main` / `master`) or on a commit that is already part of the default-branch history.\n\n## Choose a Path\n\nDefault-branch CI failures usually have two possible repair paths. Choose based on the evidence in the latest relevant run.\n\n### Path A: Ordinary CI Repair\n\nUse this path when the failure looks like a known FRB CI shape: flaky job, generated diff, lint drift, stale generated output, dependency-order failure, or downstream symptom already covered by existing CI triage.\n\nRead `frb-fix-ci` and repair the failure directly:\n\n- Check the latest relevant run, not stale status.\n- Use the user's `tom-ci` skill when reading GitHub Actions job logs.\n- Classify flakes, generated diffs, lint drift, dependency-order failures, and propagated downstream symptoms before deep debugging.\n- Prefer fixing prerequisite jobs such as `Generate`, `Integrate`, or high-relevance `Generate Internal` before chasing later build/test symptoms.\n\n### Path B: Regression Investigation\n\nUse this path when the failure is not obviously a known FRB CI maintenance issue, or when several default-branch commits/runs suggest something recently broke.\n\nA default-branch failure is often a regression. There should usually be an older default-branch commit that passed the same CI path. Study what changed between the green baseline and the red commit, then fix the source of that regression.\n\nBefore writing a regression fix, answer these questions:\n\n1. What is the failing default-branch commit and workflow run?\n2. What is the nearest older default-branch commit with a green run for the same workflow or job family?\n3. Which commits or merged PRs sit between the green baseline and the red commit?\n4. Does the failure reproduce at the red commit but not at the green baseline, using the same `./frb_internal ...` command and the FRB dev environment rules?\n5. Which source change most directly explains the regression?\n\n## Regression Workflow\n\n1. Capture the exact failing jobs and the first useful error from CI logs.\n2. Find the newest passing default-branch run for the same workflow/job family.\n3. Compare the commit range between green and red:\n   ```bash\n   git log --oneline --decorate <green-sha>..<red-sha>\n   git diff --stat <green-sha>..<red-sha>\n   ```\n4. If the range is broad or the culprit is unclear, bisect with the smallest meaningful reproduction command.\n5. Fix the source of the regression, not just the generated or downstream symptom.\n6. Add or adjust regression coverage when the bug is behavioral. For pure CI/tooling drift, document the before/after evidence in the PR.\n\n## What Not To Do\n\n- Do not treat default-branch CI as \"just rerun until green\" unless logs strongly indicate infra flake.\n- Do not force every default-branch failure through regression bisect when `frb-fix-ci` already identifies a direct CI maintenance fix.\n- Do not only repair checked-in generated files if the generator, template, version pin, or environment change is the real source.\n- Do not chase downstream job failures while an upstream generation or integration job is still red.\n- Do not assume the newest red commit is the sole cause without checking the nearest green baseline and intervening changes.\n\n## PR Expectations\n\nFor a PR that fixes default-branch CI, include:\n\n- Failing commit SHA and workflow/job URL.\n- Nearest green baseline SHA or run, if available.\n- The reproduction command used locally or in Docker/Tart.\n- The suspected or confirmed culprit commit/PR.\n- Why the fix addresses the source of the regression.\n\nUse `frb-prepare-pr` before opening the PR.\n","frontmatter":{"name":"frb-fix-main-ci","description":"Use when flutter_rust_bridge CI fails on the default branch/main/master, especially to distinguish ordinary CI repair from a regression introduced after a previously green default-branch commit."},"isInternal":false,"tokens":820,"sizeBytes":3871},{"name":"SKILL.md","path":".claude/skills/frb-fix-merge-conflict/SKILL.md","rawUrl":"https://raw.githubusercontent.com/fzyzcjy/flutter_rust_bridge/HEAD/.claude/skills/frb-fix-merge-conflict/SKILL.md","title":"frb-fix-merge-conflict","category":"anthropic-skill","format":"markdown","content":"---\nname: frb-fix-merge-conflict\ndescription: Use when resolving merge or rebase conflicts in flutter_rust_bridge, especially conflicts involving generated files after master/main changed independently from a PR branch.\n---\n\n# FRB Fix Merge Conflict\n\nUse this skill before resolving merge or rebase conflicts in `flutter_rust_bridge`.\n\n## Core Rule\n\nResolve hand-written files semantically. Do not hand-resolve generated files as the final result.\n\nGenerated conflict output should be treated as a stale artifact from two independently valid histories. After the hand-written sources are merged, regenerate the generated outputs and commit that regenerated result.\n\n## Before Starting\n\n1. Read the FRB environment setup instructions for the current checkout before running generation or tests.\n2. Read `frb-code-generation` to choose the minimal regeneration command.\n3. If the conflict is discovered through CI or PR checks, read `frb-fix-ci` before deeper investigation.\n4. Make sure the worktree starts clean except for the merge/rebase conflict you are intentionally resolving.\n\n## Classify Conflicts\n\nList unmerged files:\n\n```bash\ngit diff --name-only --diff-filter=U\n```\n\nClassify each file into one of these buckets:\n\n| Bucket | Examples | Resolution |\n|--------|----------|------------|\n| Hand-written source | Rust APIs, codegen logic, Dart tests, docs, configs | Resolve manually with normal semantic merge |\n| Generated output | `frb_generated.*`, `*.freezed.dart`, `*.g.dart`, generated API wrappers, generated test entrypoints | Pick one side only as a temporary placeholder, then regenerate |\n| Ambiguous generated-like output | lockfiles, copied template outputs, integrate scaffolds | Confirm source of truth before choosing; use `frb-fix-ci` dependency graph if unsure |\n\nDo not use the generated-file shortcut for hand-written files merely because they are noisy.\n\n## Generated File Conflict Workflow\n\nWhen a generated file conflicts because both base and PR independently added tests or APIs:\n\n1. Resolve all hand-written source conflicts first.\n2. For each generated conflicted file, choose a conflict-free placeholder from either side.\n   The chosen side is not the final answer; it only clears conflict markers so the generator can run.\n3. Stage those placeholder generated files.\n4. Run the appropriate generation command from `frb-code-generation`.\n   For broad PR-vs-master generated drift, prefer:\n\n```bash\n./frb_internal precommit-generate\n```\n\n   For `frb_example/pure_dart` and `frb_example/pure_dart_pde` conflicts, the internal generator and package codegen cover different outputs. If the merge touches generated test entrypoints, copied PDE files, or the pure-Dart chain, run:\n\n```bash\n./frb_internal generate-internal-frb-example-pure-dart\n```\n\n   If the merge also brings in hand-written Rust or Dart API changes, run package codegen after the internal generator:\n\n```bash\n./frb_internal generate-run-frb-codegen-command-generate --package frb_example/pure_dart\n./frb_internal generate-run-frb-codegen-command-generate --package frb_example/pure_dart_pde\n```\n\n   Do not stop after the internal generator when CI errors mention missing methods on generated Dart APIs such as `RustLibApi`; that usually means the package `frb_generated.*` files are stale.\n\n5. Inspect the regenerated diff. The final generated files should reflect both sides' hand-written sources.\n6. Commit the merge/rebase resolution.\n\n### Placeholder Commands\n\nFor a normal `git merge master` into a PR branch, this is usually enough for generated paths:\n\n```bash\ngit checkout --ours -- <generated-path>\ngit add <generated-path>\n```\n\nFor a `git rebase`, remember Git reverses the intuitive meaning of ours/theirs: `--ours` is the branch being rebased onto, and `--theirs` is the replayed commit. Since generated files are only placeholders, either side can be acceptable if the file still exists and regeneration will overwrite it. When deletion/addition is involved, prefer the side that leaves the path in the shape expected by the generator, then verify after regeneration.\n\n## Validation\n\nAfter regeneration:\n\n```bash\ngit diff --check\ngit diff --name-only --diff-filter=U\ngit status --short\n```\n\nThen run the relevant local Docker-backed checks from the project skills. For broad generated-file conflict repairs, start with the same generation command CI would run, then use CI to validate the full matrix.\n\nIf CI later reports more generated diff, add the experience back to this skill so the next repair has a sharper rule.\n\n## Commit Shape\n\nKeep the conflict-resolution commit focused:\n\n- Include semantic hand-written conflict resolutions.\n- Include regenerated outputs from the chosen generation command.\n- Do not include unrelated cleanup.\n- Mention the generation command in the commit message body when useful.\n","frontmatter":{"name":"frb-fix-merge-conflict","description":"Use when resolving merge or rebase conflicts in flutter_rust_bridge, especially conflicts involving generated files after master/main changed independently from a PR branch."},"isInternal":false,"tokens":1008,"sizeBytes":4826},{"name":"SKILL.md","path":".claude/skills/frb-issue-to-green-pr/SKILL.md","rawUrl":"https://raw.githubusercontent.com/fzyzcjy/flutter_rust_bridge/HEAD/.claude/skills/frb-issue-to-green-pr/SKILL.md","title":"frb-issue-to-green-pr","category":"anthropic-skill","format":"markdown","content":"---\nname: frb-issue-to-green-pr\ndescription: \"Use when implementing a GitHub issue, bug fix, or feature in flutter_rust_bridge end-to-end: develop the change, add regression coverage, prepare and open a PR, and use the user's tom-ci skill to monitor CI until the PR is ready.\"\n---\n\n# FRB Issue to Green PR\n\nUse this skill as the top-level workflow when the user asks you to implement an issue, fix a bug, add a feature, or otherwise \"handle it end to end\" in `flutter_rust_bridge`, especially when they ask for an automatic PR or CI monitoring.\n\nThis skill orchestrates the narrower FRB skills. Do not duplicate their detailed instructions in memory; read them when their phase begins.\n\n## Required Subskills\n\nAlways read these first:\n\n1. `frb-dev-env` and any applicable user-level environment rules.\n2. `frb-develop-feature` for the reproduce -> fix -> final regression workflow.\n\nRead these when entering the matching phase:\n\n- `frb-code-generation` before running generation or changing Rust APIs, codegen, or example APIs.\n- `frb-test` before running local tests.\n- `frb-lint` before lint or format checks.\n- `frb-prepare-pr` before pushing or opening the PR.\n- `frb-pr-review` before treating a non-trivial PR as ready.\n- `frb-fix-ci` before diagnosing any CI failure.\n- The user's `tom-ci` skill for CI waiting, status inspection, and GitHub Actions logs.\n- `frb-manual-test` before writing a manual regression test report under `tools/manual_tests/`.\n- `frb-ci-filter` before creating an intentional red CI reproduction PR or using filtered CI.\n- `frb-debugging` when generated code is surprising or codegen behavior is unclear.\n\n## Workflow\n\n### 1. Start the completion loop\n\n- Keep the task active until all stop conditions are met.\n- Once the PR URL is known, follow the waiting workflow in the user's `tom-ci` skill between CI processing actions until the PR is ready.\n\n### 2. Understand the GitHub issue or requested change\n\n- Fetch the issue body and comments if the user gave an issue link or number.\n- Identify the smallest observable failing behavior or missing capability.\n- Check `git status --short` and do not disturb unrelated user or multi-agent changes.\n\n### 3. Reproduce the issue in an independent reproduction PR\n\n- Read and follow the reproduction parts of `frb-develop-feature`.\n- For bug fixes, do this before changing fix code or opening the fix PR.\n- Create an independent reproduction PR whose only purpose is to prove the bad behavior. The branch name, PR title, and PR body must say clearly that it is an intentional reproduction PR, not a real fix PR.\n- For intentional red CI reproduction PRs, use the title `Reproduce ISSUE_SUMMARY with intentional red CI`.\n- For manual-test reproduction PRs, use the title `Add manual reproduction for ISSUE_SUMMARY`.\n- If CI can reproduce the bad behavior, read `frb-ci-filter` and make the reproduction PR an intentional red CI PR: unchanged fix code, minimal reproducer or workflow adjustment, mandatory focused `ci_filter` run, and a failure whose error matches the user's report.\n- If CI cannot realistically reproduce the bad behavior, read `frb-manual-test` and make the independent reproduction PR add or update `tools/manual_tests/NAME.md` with a normal manual test procedure and mechanical execution steps an agent or human can run.\n- Do not proceed to the fix PR until the reproduction PR exists and has either a matching red CI run or a precise manual test report.\n- Save the reproduction PR URL, red CI run URL when applicable, job name or manual-test path, and matching error text for the fix PR reproduction report.\n\n### 4. Fix the issue\n\n- Read and follow the fix, iteration, local verification, regression coverage, and final placement parts of `frb-develop-feature`.\n- Implement the smallest change that fixes the reproduced behavior.\n- Add final regression coverage with the fixed behavior as the expected result.\n- Make atomic commits as soon as each completed logical unit is written; do not wait until the end of the task.\n- Stage only files intentionally changed for this task.\n- Always create a new commit unless the user explicitly asks to amend.\n- Before considering the change ready, explicitly pass the `frb-develop-feature` Final Placement Gate: final regression coverage belongs in `frb_example/pure_dart` with generated `pure_dart_pde` coverage, not only in `frb_example/dart_minimal`.\n- Keep generated-file edits produced by the appropriate generator, not by hand.\n\n### 5. Prepare and open the PR\n\n- Follow `frb-prepare-pr`.\n- Re-check that no final regression or feature coverage remains only in `frb_example/dart_minimal`. If it does, stop PR preparation and migrate it to `frb_example/pure_dart` first.\n- Push with upstream tracking.\n- Before drafting a PR title, inspect the user's recent PR titles and mimic the repo style.\n- Create the PR according to the active PR workflow and repository/user PR body rules.\n- If the work comes from a GitHub issue, ensure the PR body includes the appropriate closing keyword such as `Close #1234`, unless the active PR workflow explicitly requires an empty body.\n\n### 6. Run the review gate\n\n- Follow `frb-pr-review` for the full PR review gate, including correctness review and test-weakening review.\n\n### 7. Monitor CI until terminal\n\n- After the PR is opened or updated, do not leave the PR in an unknown queued or in-progress state.\n- On each wake-up, inspect the latest PR checks, not stale runs.\n- For bug-fix PRs with an intentional red CI reproduction PR, explicitly find the same job family or CI path that failed in the reproduction branch and verify that it is now green on the fix PR.\n- For bug-fix PRs with a manual-test report PR, state whether the manual regression was re-run, who or what ran it, and whether the observed behavior now matches the fixed expectation.\n- If CI fails, read `frb-fix-ci` and the user's `tom-ci` skill, diagnose the latest relevant failure, fix it, commit, push, and continue monitoring.\n- If CI appears flaky, rerun only failed jobs when appropriate, then keep monitoring.\n\n### 8. Stop only when ready\n\n- The PR checks are green or all remaining non-green checks are clearly unrelated and explained.\n- The branch is pushed, commits are present on the PR, and the final status is reported to the user with the PR URL.\n\n## Automation Rules\n\n- Follow the user's `tom-ci` skill to wait on the PR URL after each push, rerun, or handled CI event.\n- Treat its return as a wake-up signal, not a CI verdict. Inspect the reported checks, fix or rerun as required, then call the waiter again.\n- Do not add a separate time-based heartbeat or detached polling loop for the same PR.\n- Stop invoking the waiter once the stop conditions are met.\n\n## Failure Handling\n\n- If blocked by permissions, missing credentials, a required external reviewer delay, or unavailable infrastructure, explain the concrete blocker and keep the PR state explicit.\n- If a CI run is queued for a long time, continue bounded waits using the user's `tom-ci` skill.\n- If new user instructions arrive, let the newest instruction steer the workflow while preserving already-completed work.\n","frontmatter":{"name":"frb-issue-to-green-pr","description":"Use when implementing a GitHub issue, bug fix, or feature in flutter_rust_bridge end-to-end: develop the change, add regression coverage, prepare and open a PR, and use the user's tom-ci skill to monitor CI until the PR is ready."},"isInternal":false,"tokens":1566,"sizeBytes":7140},{"name":"SKILL.md","path":".claude/skills/frb-lint/SKILL.md","rawUrl":"https://raw.githubusercontent.com/fzyzcjy/flutter_rust_bridge/HEAD/.claude/skills/frb-lint/SKILL.md","title":"frb-lint","category":"anthropic-skill","format":"markdown","content":"---\nname: frb-lint\ndescription: Use when you need to run lint, format, or clippy checks in flutter_rust_bridge\n---\n\n# FRB Lint\n\n> **Note:** Read `frb-dev-env` before running commands and follow the active user's environment rules.\n\n## Quick Reference\n\n| Command | Description |\n|---------|-------------|\n| `./frb_internal lint --fix` | Run all lints and auto-fix (recommended) |\n| `./frb_internal lint` | Run all lints |\n| `./frb_internal lint-dart` | Dart only |\n| `./frb_internal lint-rust` | Rust only |\n\n## What Lint Includes\n\nIt contains:\n- `dart analyze`\n- `dart format`\n- `cargo clippy`\n- `cargo fmt`\n","frontmatter":{"name":"frb-lint","description":"Use when you need to run lint, format, or clippy checks in flutter_rust_bridge"},"isInternal":false,"tokens":168,"sizeBytes":608},{"name":"execution-template.md","path":".claude/skills/frb-manual-test/execution-template.md","rawUrl":"https://raw.githubusercontent.com/fzyzcjy/flutter_rust_bridge/HEAD/.claude/skills/frb-manual-test/execution-template.md","title":"Frb-manual-test Skill","category":"anthropic-skill","format":"markdown","content":"# Manual Test Execution\n\n## Test\n\n:::info\nIdentify the manual test that was executed and why this run happened. Copy the purpose from the test report or summarize it in one sentence.\n:::\n\n- Manual test: `TODO: tools/manual_tests/name.md`\n- Purpose: `TODO: short description copied or summarized from the test report`\n- Source context: `TODO: issue, PR, release, or request if relevant`\n\n## Run\n\n:::info\nRecord when, by whom, and against exactly what commit or release the manual test was executed. Include enough environment detail for someone else to compare results.\n:::\n\n- Date and timezone: `TODO: YYYY-MM-DD HH:MM TZ`\n- Executor: `TODO: human, agent, or automation`\n- Commit or release tested: `TODO: sha or version`\n- Environment: `TODO: OS, shell, device, simulator, browser, or service summary`\n\n## Procedure\n\n:::info\nRecord whether the written test was followed exactly. Any deviation matters, even if the result passed. Include exact commands or a path to a command script when the run used one.\n:::\n\n- Followed report revision: `TODO: commit containing the manual test report`\n- Command script or exact commands: `TODO: path to script, inline commands, or not applicable`\n- Deviations from written steps: `TODO: none, or list exact deviations`\n- Cleanup completed: `TODO: yes/no; explain if no`\n\n## Result\n\n:::info\nUse `pass`, `fail`, `blocked`, or `partial`. The summary should explain the result without requiring the reader to open logs first. Include exact command exit status and concrete pass/fail indicators from the logs.\n:::\n\n- Status: `TODO: pass/fail/blocked/partial`\n- Summary: `TODO: one or two sentences`\n- Command results: `TODO: each required command with exit status`\n- Evidence lines: `TODO: exact success/failure strings, log line numbers, or grep output proving the result`\n\n## Artifacts\n\n:::info\nLink or name the artifacts captured during execution. Use `not applicable` for artifact types that do not apply.\n:::\n\n- Execution markdown gist: `TODO: gist URL if this result belongs to a PR, issue, or release checklist; otherwise not applicable`\n- Terminal log: `TODO: path or link`\n- Screenshot or recording: `TODO: path or link, or not applicable`\n- Generated artifacts: `TODO: path or link, or not applicable`\n- Environment evidence: `TODO: exact tool versions, device/simulator name and UDID, browser version, or not applicable`\n- Cleanup evidence: `TODO: final git status, VM/container/device state, cleanup command output, or not applicable`\n- Other evidence: `TODO: path, link, log excerpt, or not applicable`\n\n## Follow-up\n\n:::info\nIf the result is not a clean pass, state the next action. Link the issue or PR when one exists.\n:::\n\n- Required follow-up: `TODO: none, issue link, PR link, or next action`\n- Notes: `TODO: anything future executors should know`\n","isInternal":false,"tokens":655,"sizeBytes":2798},{"name":"SKILL.md","path":".claude/skills/frb-manual-test/SKILL.md","rawUrl":"https://raw.githubusercontent.com/fzyzcjy/flutter_rust_bridge/HEAD/.claude/skills/frb-manual-test/SKILL.md","title":"frb-manual-test","category":"anthropic-skill","format":"markdown","content":"---\nname: frb-manual-test\ndescription: Use when adding, executing, reviewing, or updating flutter_rust_bridge manual test reports under tools/manual_tests for scenarios that require human or agent-driven manual verification.\n---\n\n# FRB Manual Test\n\nUse this skill for manual test reports in `tools/manual_tests/`. These reports describe repeatable checks that a human or agent can execute when automated tests are insufficient, unavailable, too expensive, or not the right interface for the behavior being verified.\n\nManual tests are normal software-engineering test artifacts. They may cover devices, host-specific setup, interactive UI behavior, credentials or account state, release packaging, marketplace flows, external services, or long-running workflows. They are not tied to any single PR workflow.\n\n## Add Manual Tests\n\nManual test reports live under:\n\n```text\ntools/manual_tests/NAME.md\n```\n\nWhen adding a report, do not start from a blank file. Copy the bundled test template first:\n\n```bash\ncp .claude/skills/frb-manual-test/test-template.md tools/manual_tests/NAME.md\n```\n\nUse a short kebab-case name. Include a date prefix when the report is an example, a one-off check, or does not yet have a stable feature name, such as:\n\n```text\ntools/manual_tests/2026-06-06-some-scenario.md\n```\n\nAfter copying, edit the report in place:\n\n- Replace every placeholder.\n- Remove sections that truly do not apply.\n- Keep commands and UI actions mechanical enough that a future human or agent can run them without the original conversation.\n- Prefer repo tooling such as `./frb_internal` when possible.\n- Do not rely on \"run the app normally\", \"check manually\", or \"verify it works\" without concrete commands or UI actions.\n- If credentials or private services are needed, name the required capability without including secrets.\n\nThe detailed content requirements live in `test-template.md`; keep that template as the source of truth for report shape.\n\n## Execute Manual Tests\n\nWhen executing a manual test, first read the target report under `tools/manual_tests/` and follow its preparation, steps, expected result, failure criteria, troubleshooting, and cleanup sections.\n\nBefore reporting the run result, use the bundled execution template:\n\n```bash\ncp .claude/skills/frb-manual-test/execution-template.md EXECUTION_RESULT_PATH.md\n```\n\nAlways fill out an execution markdown file before summarizing the result. Do not only say that the test \"passed\".\n\nExecution markdown is a run artifact, not a repository test definition. Do not create it under `tools/manual_tests/` or anywhere else inside the `flutter_rust_bridge` repository. Put it in an external artifacts location such as `/private/tmp`, `~/main/artifacts/flutter_rust_bridge/`, or another run-specific artifact directory.\n\nExecution markdown must contain precise, audit-ready evidence that proves what ran and why it passed, failed, or blocked. Include the exact commands, exit status, key success or failure log lines, relevant version output, selected device or simulator identifiers, generated artifact paths, final cleanup state, and links or paths to complete logs. For successful runs, quote or summarize the concrete pass indicators such as `All tests passed!`, `test result: ok`, artifact paths, simulator UDIDs, or final `git status --short` output. Avoid vague summaries like \"worked\", \"passed\", or \"looked good\" without evidence.\n\nIf the execution result belongs to a PR, issue, release checklist, or other reviewable workflow, upload the filled execution markdown and all review-relevant artifacts as a GitHub gist and link that gist in the PR description, PR comment, issue comment, or release checklist. Review-relevant artifacts include screenshots, recordings, logs, generated result files, and browser/device evidence needed to judge the result. The local execution markdown and artifact copies should still be kept in the run artifacts directory so the run can be audited without relying only on chat history.\n\nIf the result belongs only in chat, still fill out the execution markdown locally and include its path in the chat response.\n\nRun cleanup from the manual test report before declaring the execution complete, unless cleanup is intentionally skipped and documented in the execution record.\n\n### Reviewable Workflow Gate\n\nBefore creating or updating a PR, issue comment, release checklist, or any other reviewable artifact that cites a manual test execution:\n\n1. Fill out the execution markdown file locally.\n2. Collect every artifact needed by the manual test's `Results To Capture` section, especially screenshots, recordings, logs, and generated evidence files.\n3. Upload the execution markdown and those review-relevant artifacts into the same GitHub gist. Use a gist git clone and push when binary artifacts such as PNG screenshots need to be included.\n4. Copy the gist URL into the execution markdown's `Execution markdown gist` field if practical.\n5. Make the execution markdown point to the gist-contained artifact filenames, embedding screenshots or linking recordings when useful.\n6. Link the gist URL in the reviewable artifact.\n7. Keep local artifact paths only as supplemental audit evidence; they are not a substitute for gist-contained artifacts.\n\nDo not say that a PR-linked manual test execution is complete while any review-relevant artifact exists only as a local path. If gist upload or GitHub access fails, report the manual test execution as complete locally but the PR/update step as blocked until a gist containing the execution markdown and review-relevant artifacts can be created and linked.\n","frontmatter":{"name":"frb-manual-test","description":"Use when adding, executing, reviewing, or updating flutter_rust_bridge manual test reports under tools/manual_tests for scenarios that require human or agent-driven manual verification."},"isInternal":false,"tokens":1112,"sizeBytes":5603},{"name":"test-template.md","path":".claude/skills/frb-manual-test/test-template.md","rawUrl":"https://raw.githubusercontent.com/fzyzcjy/flutter_rust_bridge/HEAD/.claude/skills/frb-manual-test/test-template.md","title":"Frb-manual-test Skill","category":"anthropic-skill","format":"markdown","content":"# Manual Test Template\n\n## Purpose\n\n:::info\nWrite one or two concrete sentences explaining what behavior this manual test verifies. Name the user-visible behavior, platform, package, or workflow under test. Do not leave this as a generic phrase like \"verify it works\".\n:::\n\nTODO: Write what this test verifies.\n\n## Source\n\n:::info\nLink the reason this test exists: issue, PR, feature request, release checklist, user report, or maintenance task. If there is no external link, state the local context in plain text.\n:::\n\n- Context: `TODO: issue, PR, feature, release, or user report`\n- Related docs or skills: `TODO: paths or links`\n\n## When To Run\n\n:::info\nState the trigger for this manual test. Examples: before a release, after changing a named subsystem, after upgrading Flutter/Rust/Dart, before closing a bug, or when validating a specific platform.\n:::\n\nTODO: Write when this test is required or useful.\n\n## Preconditions\n\n:::info\nList everything that must already be true before starting. Include checkout state, expected branch family if relevant, submodules, generated files, devices, simulators, credentials, account state, network access, and external services. Never paste secrets. Keep this section about reusable requirements, not one particular run.\n:::\n\n- Repository: `fzyzcjy/flutter_rust_bridge`\n- Required checkout state: `TODO: clean checkout with submodules initialized, generated files present, etc.`\n- Required credentials or account state: `TODO: required capability, never paste secrets`\n- Required device or simulator state: `TODO: device must be available, app uninstalled, cache reset, etc.`\n\n## Environment\n\n:::info\nRecord exact versions and runtime context needed to make the result reproducible. If a tool is not required, write `not required` instead of leaving it ambiguous.\n:::\n\n- OS: `TODO: macOS / Linux / Windows version, or not constrained`\n- Flutter: `TODO: flutter --version, or not required`\n- Dart: `TODO: dart --version, or not required`\n- Rust: `TODO: rustc --version, or not required`\n- Device or simulator: `TODO: device name, OS version, UDID if applicable, or not required`\n- Browser or external service: `TODO: name and version if applicable, or not required`\n\n## Preparation\n\n:::info\nGive copy-pasteable commands that take an existing repository root to the state needed by the test. Prefer repository tooling such as `./frb_internal`. If generated files, caches, or dependencies are required, include the exact commands here. Keep source acquisition and revision selection outside the reusable test case.\n:::\n\n```bash\ngit submodule update --init --recursive\n```\n\nIf this test needs generated output or dependencies, list the exact setup commands here.\n\n```bash\n./frb_internal --help\n```\n\n## Test Data\n\n:::info\nDescribe all inputs and fixtures. Include paths, account setup, generated files, sample API code, reset commands, or \"none\" if the test uses no special data.\n:::\n\n- Input files, API examples, account fixtures, or generated assets: `TODO: paths or setup notes, or none`\n- Reset procedure before each run: `TODO: commands or UI actions`\n\n## Steps\n\n:::info\nWrite ordered mechanical steps. Each step should be executable by a human or agent without guessing. For UI steps, name buttons, screens, field values, and expected intermediate state. For command steps, include complete commands.\n:::\n\n1. TODO: Prepare the target package, example, device, account, or service.\n\n   ```bash\n   TODO_COMMAND\n   ```\n\n2. TODO: Run the command or manual action that verifies the behavior.\n\n   ```bash\n   TODO_COMMAND\n   ```\n\n3. If UI interaction is required, describe each click, field value, device action, or expected screen by name.\n\n## Expected Result\n\n:::info\nDefine the pass condition as observable output: command exit status, log text, generated artifact, UI state, screenshot content, or external service state. Avoid vague language.\n:::\n\nTODO: The command or manual flow should complete successfully and show the expected observable behavior.\n\n```text\nTODO: success output, UI text, artifact path, or other pass criteria\n```\n\n## Failure Criteria\n\n:::info\nList concrete conditions that mean the test failed or is blocked. Include setup failures that should be reported separately from product failures.\n:::\n\nThe test fails if any of the following happens:\n\n- The command exits non-zero unexpectedly.\n- The UI, artifact, or log output does not match the expected result.\n- The required device, account, or service state cannot be prepared.\n\n## Results To Capture\n\n:::info\nSay exactly what artifacts the executor should save or link in the execution record. Include terminal logs, screenshots, recordings, generated files, device details, run URLs, or external service observations.\n:::\n\n- Full terminal log from the command.\n- Screenshot or screen recording if UI behavior is involved.\n- Device, simulator, or browser version.\n- Any generated artifact path needed to inspect the result.\n\n## Troubleshooting\n\n:::info\nProvide known recovery steps for common environment and setup failures. This section should help distinguish a blocked manual run from a real product failure.\n:::\n\n- If dependencies are missing, re-run the repository setup commands and record the exact failure.\n- If a device or simulator is unavailable, record the discovered devices and mark the run as blocked.\n- If credentials or external services are unavailable, record the missing capability without exposing secrets.\n\n## Cleanup\n\n:::info\nList commands and manual actions needed to restore the checkout, stop services, reset devices, and undo external account state. If no cleanup is needed, say so explicitly.\n:::\n\n```bash\ngit status --short\n```\n\nRemove temporary local files, stop simulators or servers started for the test, and restore any external account or device state changed during the run.\n\n## Future Automation\n\n:::info\nExplain whether this manual test could later become automated. If yes, name the likely CI job, package, or test harness. If no, explain what keeps it manual.\n:::\n\nIf this scenario later becomes reliable and affordable to automate, replace or supplement this manual report with an automated test and link the PR here.\n","isInternal":false,"tokens":1316,"sizeBytes":6173},{"name":"SKILL.md","path":".claude/skills/frb-pr-chain-split/SKILL.md","rawUrl":"https://raw.githubusercontent.com/fzyzcjy/flutter_rust_bridge/HEAD/.claude/skills/frb-pr-chain-split/SKILL.md","title":"frb-pr-chain-split","category":"anthropic-skill","format":"markdown","content":"---\nname: frb-pr-chain-split\ndescription: Use when an FRB PR contains independently landable work that should be extracted into predecessor PRs and connected as a GitHub PR chain without wasting full automatic CI.\n---\n\n# 1 When to use\n\n- Use this skill while developing or reviewing a `flutter_rust_bridge` PR when part of its diff is independent of the PR's stated objective.\n- Split the independent work proactively. Do not wait for the user to notice the mixed scope.\n- Treat a change as independently landable when it:\n    - has a coherent behavior or maintenance objective of its own;\n    - can be implemented and validated against the current default branch;\n    - does not require the main task's API, schema, generated output, or version change;\n    - leaves the main PR smaller and easier to review.\n- Keep inseparable companion changes in the main PR. Generated snapshots, tests, and documentation required by the main change are not independent merely because they touch different files.\n\n# 2 Choose the chain boundary\n\nClassify every suspicious change before moving commits:\n\n| Classification | Destination | Reason |\n| --- | --- | --- |\n| Independent bug fix or hardening | Predecessor PR | It can land and provide value before the main task |\n| Independent mechanical refactor | Predecessor PR | It removes review noise from the main task |\n| Main-task prerequisite with its own contract | Predecessor PR | The main PR can depend on a separately reviewable foundation |\n| Generated output caused by the main task | Main PR | It cannot land meaningfully by itself |\n| Test or documentation for the main behavior | Main PR | Splitting would separate the contract from its implementation |\n\n- Prefer the smallest coherent predecessor, not the smallest textual diff.\n- Create several predecessor PRs when the extracted work contains multiple independent contracts.\n- Order predecessors by dependency. Do not infer order from commit timestamps.\n\n# 3 Build each predecessor\n\n- Fetch the canonical default branch before creating branches.\n- Create the first predecessor from the current default branch.\n- Create each later predecessor from the preceding predecessor branch.\n- Reconstruct the coherent final delta. Do not blindly cherry-pick commits that mix predecessor and main-task changes.\n- Include the predecessor's own tests and documentation.\n- Validate the predecessor independently before pushing.\n- Read `frb-prepare-pr` for generation, lint, test, and review requirements.\n- Use one branch and one PR per independently landable unit.\n- Push every chain branch to the same repository as the main PR. GitHub cannot use an unpushed local branch as a PR base.\n\n# 4 Construct the GitHub PR chain\n\n- Build a stacked branch chain, not sibling PRs that all target the default branch:\n\n```text\nmaster <- predecessor-a <- predecessor-b <- main-task\n```\n\n- Use GitHub's official `github/gh-stack` extension through `gh stack`.\n- Use `gh stack` for every stack operation: initialization, restructuring, linking, submission, synchronization, rebasing, pushing, and verification.\n- Never construct or maintain a stack with `gh pr create --base`, `gh pr edit --base`, direct API base edits, or branch ancestry alone.\n- Git commands may prepare the commits and linear branch ancestry, but the result is not complete until `gh stack` creates or updates the native GitHub stack object.\n- Check and install the extension when necessary. The extension requires `gh` 2.0 or later; a missing `gh stack` command normally means the extension is absent, not that core `gh` is outdated.\n\n```bash\ngh stack --help\ngh extension install github/gh-stack\n```\n\n- For an unpublished main branch, branch it directly from the final predecessor.\n- Initialize and submit a new local stack with `gh stack init` and `gh stack submit`.\n- For an already published main PR:\n    - create a timestamped backup tag before non-trivial history work;\n    - record the original final tree and three-dot PR diff;\n    - restack the main branch onto the final predecessor with a fully linear history;\n    - do not introduce merge commits because native GitHub stacks require linear ancestry;\n    - compare the rebuilt tree and PR delta with the recorded originals before pushing;\n    - use `--force-with-lease` only after those checks when published history must be rewritten.\n- Adopt existing branches or PRs with `gh stack link`. It pushes branches, creates missing PRs, corrects their base branches, and creates the native GitHub stack object.\n- Create and connect a new stack with commands shaped like:\n\n```bash\ngh stack init --base master <predecessor-branch> <main-task-branch>\ngh stack submit\n```\n\n- Adopt already prepared branches or existing PRs with:\n\n```bash\ngh stack link --base master <predecessor-branch> <main-task-branch>\n```\n\n- Verify local ancestry, native stack metadata, and PR bases:\n\n```bash\ngit merge-base --is-ancestor <predecessor-branch> <main-task-branch>\ngh stack view --json\n```\n\n- Compare three-dot diffs after constructing the chain:\n    - `<previous-branch>...<predecessor-branch>` contains only that predecessor;\n    - `<final-predecessor-branch>...<main-task-branch>` no longer contains the extracted work;\n    - the final main-task tree is unchanged except for intentional cleanup.\n\n# 5 Conserve CI on predecessor PRs\n\n- Read `frb-ci-filter` before manipulating CI.\n- Immediately add `ci-manual-dispatch` after creating every predecessor PR:\n\n```bash\ngh pr edit <predecessor-pr-number> --add-label ci-manual-dispatch\n```\n\n- Add the label even when local validation is already green. Predecessor PRs must not spend the full CI matrix merely because they were split for reviewability.\n- The label action triggers a new `pull_request:labeled` run. The workflow concurrency group cancels the earlier automatic PR run, and the labeled run produces an empty heavy-job plan.\n- Do not dispatch GitHub CI when local validation is sufficient.\n- When a predecessor specifically needs GitHub-only evidence:\n    - keep `ci-manual-dispatch` on the PR;\n    - validate the filter locally;\n    - dispatch only the smallest relevant job or matrix entry.\n\n```bash\n./frb_internal plan-ci --filter '<filter>'\ngh workflow run ci.yaml --ref <predecessor-branch> -f 'ci_filter=<filter>'\n```\n\n- Do not treat filtered CI as full merge-readiness evidence.\n- When a predecessor becomes the next PR to merge, remove `ci-manual-dispatch` and obtain the normal required CI unless the maintainer explicitly accepts narrower evidence.\n\n# 6 Completion checks\n\n- Confirm each predecessor can be reviewed without understanding the main task.\n- Confirm each PR's GitHub base matches its immediate predecessor branch.\n- Confirm Git ancestry matches the GitHub base chain.\n- Confirm the main PR diff excludes all extracted changes.\n- Confirm every predecessor PR has `ci-manual-dispatch` while it is not awaiting final full CI.\n- Report the PR numbers, branch order, validation performed, and any intentionally dispatched `ci_filter`.\n\n# 7 Reliability pitfalls\n\n- Do not create independent sibling PRs and merely call them a chain.\n- Do not substitute manual PR base edits for `gh stack link` or `gh stack submit`.\n- Do not treat matching branch ancestry as sufficient; verify the native stack with `gh stack view --json`.\n- Do not force-push a published main PR merely to make its history prettier.\n- Do not split generated files away from the generator or source change that owns them.\n- Do not leave an extracted commit duplicated in the visible main PR diff.\n- Do not spend full CI on every dormant predecessor.\n- Do not leave `ci-manual-dispatch` on the PR when claiming normal full-CI readiness.\n","frontmatter":{"name":"frb-pr-chain-split","description":"Use when an FRB PR contains independently landable work that should be extracted into predecessor PRs and connected as a GitHub PR chain without wasting full automatic CI."},"isInternal":false,"tokens":1622,"sizeBytes":7663},{"name":"SKILL.md","path":".claude/skills/frb-pr-review/SKILL.md","rawUrl":"https://raw.githubusercontent.com/fzyzcjy/flutter_rust_bridge/HEAD/.claude/skills/frb-pr-review/SKILL.md","title":"frb-pr-review","category":"anthropic-skill","format":"markdown","content":"---\nname: frb-pr-review\ndescription: Review a flutter_rust_bridge PR before treating it as ready, including subagent checks for correctness and test weakening.\n---\n\n# 1 Human SoT\n\n# 2 FRB PR Review\n\nUse this before treating a non-trivial `flutter_rust_bridge` PR as ready, especially after CI-driven fixes, Flutter upgrades, generated drift, test changes, workflow changes, or broad merge conflict resolution.\n\n## 2.1 Review Gate\n\nRun the following review before final readiness:\n\n1. Review the PR diff against its base:\n   - Spawn a subagent to review correctness.\n     - Ask it to inspect the PR diff against the PR base.\n     - Focus on real behavior bugs, stale generated output, incorrect CI workarounds, coverage-only changes, and unrelated drift.\n     - Require findings with file paths, line numbers, impact, and suggested fix.\n   - Spawn a subagent to review test weakening.\n     - Inspect changed or deleted assertions, relaxed thresholds, new skips, ignored failures, timeout changes, and coverage exclusions.\n     - Require a concrete justification for each reduction in coverage; restore unjustified weakening and fix the underlying failure.\n     - If `tom-style-tests` is installed, apply its common test principles and weakening gate; use `frb-test` for FRB execution commands.\n     - Treat unjustified skipped tests, weaker assertions, broader ignores, fake timeouts, and coverage hiding as blockers.\n\n2. Write a concise review conclusion.\n   - Put the conclusion in the PR description or an agent-context draft when the user asks for a Markdown artifact.\n   - Include the subagents used, accepted findings, dismissed findings, fixes made, and remaining risks.\n\n## 2.2 Stop Condition\n\nDo not call the PR ready until:\n\n- Correctness review has no unresolved actionable findings and test-weakening review has no unjustified weakening.\n- CI status is green, or remaining non-green checks are clearly unrelated and explained.\n","frontmatter":{"name":"frb-pr-review","description":"Review a flutter_rust_bridge PR before treating it as ready, including subagent checks for correctness and test weakening."},"isInternal":false,"tokens":403,"sizeBytes":1938},{"name":"SKILL.md","path":".claude/skills/frb-prepare-pr/SKILL.md","rawUrl":"https://raw.githubusercontent.com/fzyzcjy/flutter_rust_bridge/HEAD/.claude/skills/frb-prepare-pr/SKILL.md","title":"frb-prepare-pr","category":"anthropic-skill","format":"markdown","content":"---\nname: frb-prepare-pr\ndescription: Use when about to create a PR or push changes in flutter_rust_bridge to ensure code generation is up to date\n---\n\n# FRB Prepare for PR\n\n> **Note:** Read `frb-dev-env` before running commands and follow the active user's environment rules.\n\n## Overview\n\nBefore creating a PR, ensure generated code is up to date, lint passes, and bug fixes have a concrete reproduction report in the PR description. Before treating a non-trivial PR as ready, run `frb-pr-review`.\n\n**Core principle:** Generate → Lint → Commit → PR.\n\n> **After codegen:** Follow `frb-dev-env` and verify generated output is present in the selected worktree before reviewing the diff.\n\n## Workflow\n\n```\n1. Read frb-code-generation skill\n   |\n   +-- Run required generation commands\n   |\n   +-- 2. For integrate output diffs, verify templates and run precommit-integrate\n   |\n   +-- 3. Read frb-lint skill --> Run lint --fix\n   |\n   +-- 4. (Optional) Read frb-test skill --> Run relevant tests\n   |\n   +-- 5. For non-trivial PRs, read frb-pr-review --> Run review gate\n   |\n   +-- 6. For bug fixes, add the reproduction report to the PR description\n   |\n   +-- 7. Commit all changes\n   |\n   +-- 8. Create PR (follow the active user-level PR workflow)\n```\n\n## Quick Checklist\n\n1. [ ] **REQUIRED:** Read `frb-code-generation` skill, run commands if needed\n2. [ ] **REQUIRED for integrate output diffs:** If the PR changes Flutter integrate example outputs or platform scaffolds, confirm whether `frb_codegen/assets/integration_template/` is the source that should change, then run `./frb_internal precommit-integrate`\n3. [ ] **REQUIRED for copied Cargokit diffs:** If the PR changes copied `cargokit` files under `frb_example/**`, read `frb-cargokit`, then run `./frb_internal sync-cargokit-copies`\n4. [ ] **REQUIRED:** Read `frb-lint` skill, run `./frb_internal lint --fix`\n5. [ ] (Optional) Read `frb-test` skill, run relevant tests\n6. [ ] **REQUIRED for non-trivial PRs:** Read `frb-pr-review`, run the review gate before final readiness\n7. [ ] **REQUIRED for bug fixes:** PR description includes the reproduction report from `frb-develop-feature`, including baseline commit, mechanical steps, observed failure, and expected behavior\n8. [ ] **REQUIRED for CI iteration branches:** Read `frb-ci-filter`, then remove `ci-manual-dispatch` and rerun normal CI if the PR used filtered CI, unless the PR is explicitly about changing CI behavior\n9. [ ] Commit all changes\n10. [ ] Push and create PR\n\n## What CI Will Do\n\nCI automatically runs:\n- Code generation check (fails if generated code is outdated)\n- All tests (Rust, Dart, Flutter, Web)\n- Lint and format checks\n\nRun lint locally to avoid CI failures. Tests are optional locally.\n\nWhile iterating, it can be useful to dispatch `.github/workflows/ci.yaml` with `ci_filter` so only the relevant job family or matrix entry runs. Read `frb-ci-filter` before doing this. Do not leave `ci-manual-dispatch` on the final PR unless changing CI behavior is the actual purpose of the PR.\n\nIf your PR fixes Flutter integrate example outputs and the real bug is inside the embedded `cargokit` submodule, do not stop at copied example files. Push the `cargokit` fix to `fzyzcjy/cargokit` and update the submodule ref in this repo before pushing the PR branch.\n\nIf the PR changes integrate-generated example output but not `frb_codegen/assets/integration_template/`, explicitly verify that the output-only change is intentional. Most integrate scaffold behavior should be fixed in the template and regenerated, not patched only in `frb_example/**`.\n\n## Related Skills\n\n- `frb-code-generation` - Determines which generation commands to run\n- `frb-lint` - Lint and format checks\n- `frb-test` - For local debugging when CI fails\n- `frb-pr-review` - PR readiness review gate\n- `frb-ci-filter` - Filtered CI dispatch rules\n- Follow the active user-level PR workflow when available; otherwise use `gh pr create` with the repository's title and body conventions.\n","frontmatter":{"name":"frb-prepare-pr","description":"Use when about to create a PR or push changes in flutter_rust_bridge to ensure code generation is up to date"},"isInternal":false,"tokens":975,"sizeBytes":3998},{"name":"SKILL.md","path":".claude/skills/frb-publish-release/SKILL.md","rawUrl":"https://raw.githubusercontent.com/fzyzcjy/flutter_rust_bridge/HEAD/.claude/skills/frb-publish-release/SKILL.md","title":"frb-publish-release","category":"anthropic-skill","format":"markdown","content":"---\nname: frb-publish-release\ndescription: \"Publish a flutter_rust_bridge release end to end: preflight checks, changelog preparation, frb_internal release publishing, released-version polling, CI babysitting, and post-release CI verification.\"\n---\n\n# FRB Publish Release\n\nUse this skill when preparing, publishing, or babysitting a `flutter_rust_bridge` release.\n\n## Workflow\n\n### 1. Preflight\n\n- Work from the repository root on the intended release branch, normally fresh `master`. If the checkout is detached but already points at the intended release commit, switch or create a local release branch from that commit before running mutating release commands; do not treat detached HEAD itself as a release blocker.\n- Check `git status --short --branch` and do not start publishing from a dirty tree.\n- Check submodules before publishing:\n\n  ```bash\n  git submodule update --init --recursive\n  git submodule status --recursive\n  ```\n\n  Stop if any submodule path remains uninitialized. A release built without initialized submodules can publish incomplete package contents. The release script performs the same submodule-status guard before publishing.\n- Treat `master` as a moving branch. Before each irreversible release phase, fetch `origin master`, confirm the current release branch or detached checkout still points at the intended latest `origin/master` commit, and re-run `git status --short --branch` to confirm there are no unexpected local changes.\n- Confirm the target version in `CHANGELOG.md`, root `Cargo.toml`, and `frb_dart/pubspec.yaml`.\n- Compute the release versions the same way `./frb_internal release` does: the top `CHANGELOG.md` version is the new version and the next release section is the old version.\n- Verify both old and new versions are legal before running any mutating release command. The only allowed shapes are stable SemVer `MAJOR.MINOR.PATCH` such as `2.0.0`, or beta SemVer `MAJOR.MINOR.PATCH-beta.N` such as `2.0.0-beta.1`. Use exactly `^\\d+\\.\\d+\\.\\d+(-beta\\.\\d+)?$`.\n- Reject versions with any other prerelease label, build metadata, missing numeric components, leading `v`, or loose text. Stop if either old or new version fails this check.\n- Confirm the new version is different from the old version.\n- Run the publish container credential preflight before starting irreversible publish steps:\n\n  ```bash\n  .claude/skills/frb-dev-env/frb_dev_env.py docker-run-rm --with-publish-credentials -- true\n  ```\n\n  Stop if the preflight fails. It checks GitHub CLI auth, Cargo credentials, and Dart pub credentials inside the same temporary credential layout used by release publishing.\n- Confirm normal CI is green for the release commit before publishing. This is the default hard gate.\n\n  A narrow exception is allowed only when all of these are true:\n\n  - A recent earlier commit on the same release branch has fully green normal CI.\n  - Every commit between that green commit and the intended release commit is unrelated to the publishable packages, version sources, generated release artifacts, and `frb_internal` release logic.\n  - The mechanical gate script below exits successfully and its output is recorded in the release notes or journal.\n  - The agent explicitly states that it is using the exception before publishing.\n\n  Run the mechanical gate from the repository root:\n\n  ```bash\n  uv run --script .claude/skills/frb-publish-release/release_ci_gate.py \\\n    --base-green-ref <LAST_GREEN_SHA> \\\n    --release-ref HEAD\n  ```\n\n  Stop and wait for normal CI if the script reports any `BLOCK` path. The script is intentionally conservative: release-surface paths such as `Cargo.toml`, `frb_dart/pubspec.yaml`, `frb_codegen/**`, `frb_macros/**`, `frb_rust/**`, `frb_utils/**`, `frb_example/**`, `tools/frb_internal/**`, `pubspec.yaml`, `melos.yaml`, and lockfiles are not in the allowlist. Release metadata and its generated copies are allowed: `CHANGELOG.md`, `frb_dart/CHANGELOG.md`, `frb_hooks/CHANGELOG.md`, `.all-contributors-custom.yaml`, `.all-contributorsrc`, `README.md`, `frb_dart/README.md`, and `website/**`. These files are reviewed directly before publishing and do not affect package execution. Other `frb_dart/**` and `frb_hooks/**` paths remain blocked. This exception is for release metadata, docs, agent tooling, devcontainer, selected CI configuration, and other explicitly non-release paths only.\n\n### 2. Reconcile Contributors\n\n- Before writing the release changelog, use `frb-add-contributor` to identify contributors from the target release range who may need all-contributors credit or whose human-written custom description does not cover every release-range PR.\n- Follow `frb-add-contributor` exactly. In particular, after determining which contributors may need to be added or have their custom descriptions extended, stop for human confirmation before editing contributor files, posting GitHub comments, triggering all-contributors, opening PRs, or merging contributor PRs.\n- Do not continue to changelog preparation until contributor reconciliation is complete, or confirmed unnecessary because all contributors are credited and their custom descriptions cover every third-party PR in the release range.\n\n### 3. Write Changelog\n\n- Use `frb-write-changelog` and follow it exactly. It is the single source of truth for beta and stable changelog content, grouping, ordering, attribution, review, verification, and generated package changelog copies. Do not duplicate those rules here.\n- Do not publish until the human-review gate required by `frb-write-changelog` is satisfied.\n- The top `CHANGELOG.md` version is the source used by `frb_internal release`.\n- If changelog or version files changed, commit that release preparation before publishing.\n- Land contributor and changelog preparation commits on `origin/master` before publishing. Do not leave release preparation only on a side branch.\n\n### 4. Pre-Publish Independent Audit\n\n- Immediately before the first irreversible publish command, delegate a read-only audit to an independent subagent that has not participated in preparing the release.\n- Give the subagent the intended version and the raw evidence needed to verify the work completed so far:\n  - Current branch, exact `HEAD`, latest `origin/master`, and `git status --short --branch`.\n  - Previous and target release versions from `CHANGELOG.md`, root `Cargo.toml`, and `frb_dart/pubspec.yaml`.\n  - Final target changelog section, contributor reconciliation result, human changelog approval, and relevant diffs.\n  - Submodule status, credential preflight result, and normal CI green evidence or evidence that every narrow CI-gate exception condition in Step 1 is satisfied, including successful `release_ci_gate.py` output.\n  - Exact commands and results for any additional focused release tests that were run. These tests do not replace the normal CI gate or its defined narrow exception.\n  - Exact one-shot publish command that will run next.\n- Ask the subagent to return one verdict:\n  - `GO`: all release instructions are satisfied and the evidence is internally consistent.\n  - `NO-GO`: list every blocker, missing artifact, ambiguity, or inconsistent result with concrete evidence.\n- Do not tell the subagent the desired verdict or provide only a prepared summary. Let it inspect the repository and raw command outputs independently.\n- Stop before publishing on `NO-GO`, an ambiguous verdict, missing evidence, or unavailable subagent capability. Resolve every blocker and run a fresh independent audit.\n- Treat the audit as valid only for the exact audited `HEAD`, `origin/master`, clean worktree state, evidence snapshot, and next publish command.\n- After a `GO` verdict, run that exact publish command without any intervening mutation. If the command changes, the worktree becomes dirty, evidence is refreshed or invalidated, or `HEAD` or `origin/master` changes, repeat the affected preflight checks and the independent audit.\n\n### 5. Publish\n\nUse the repository release command through a temporary Docker container with publish credentials as the normal publishing path:\n\n```bash\n.claude/skills/frb-dev-env/frb_dev_env.py docker-run-rm --with-publish-credentials -- ./frb_internal release\n```\n\nRun the one-shot command from a clean local `master` that tracks the latest `origin/master`. `release-update-git` uses plain `git push`, so do not run the command from a side release branch.\n\nDo not split the normal release into separate `release-update-*` or publish commands. The one-shot command is the source of truth for release sequencing: it computes old/new versions from `CHANGELOG.md`, updates checked-in versions and generated version text, updates Scoop metadata, commits and pushes the version bump, creates the GitHub release, then runs the registry publisher:\n\n```bash\n.claude/skills/frb-dev-env/frb_dev_env.py docker-run-rm --with-publish-credentials -- ./frb_internal release-publish-all\n```\n\nFor beta versions such as `2.13.0-beta.1`, do not require the GitHub release to be labeled as a pre-release. FRB intentionally publishes beta GitHub releases as normal GitHub releases while the package version itself remains a SemVer prerelease.\n\n`release-publish-all` publishes these packages:\n\n- `frb_codegen` -> crates.io package `flutter_rust_bridge_codegen`\n- `frb_macros` -> crates.io package `flutter_rust_bridge_macros`\n- `frb_rust` -> crates.io package `flutter_rust_bridge`\n- `frb_dart` -> pub.dev package `flutter_rust_bridge`\n- `frb_hooks` -> pub.dev package `flutter_rust_bridge_hooks`\n\nOnly use a split subcommand as a recovery path after confirming which one-shot step already completed. For example, if the version bump and GitHub release already exist and only registry publication is needed, use `.claude/skills/frb-dev-env/frb_dev_env.py docker-run-rm --with-publish-credentials -- ./frb_internal release-publish-all` directly.\n\n### 6. Check Released Versions\n\nPoll registry state with:\n\n```bash\n./frb_internal get-released-version\n```\n\nTo check a target version from a checkout whose manifests have not been bumped, pass it explicitly:\n\n```bash\n./frb_internal get-released-version --version <VERSION>\n```\n\nThe command prints JSON:\n\n```json\n{\n  \"allReleased\": true,\n  \"packages\": [\n    {\n      \"registry\": \"crates.io\",\n      \"name\": \"flutter_rust_bridge_codegen\",\n      \"manifestVersion\": \"2.12.0\",\n      \"releasedVersion\": \"2.12.0\",\n      \"isReleased\": true\n    }\n  ]\n}\n```\n\nWait until every package has `isReleased: true` and `allReleased: true`. If one registry lags, keep polling with bounded waits and record the mismatched package instead of assuming the publish failed. Do not add the target version option to the mutating `release-*` subcommands; they derive their version from `CHANGELOG.md` and checked-in manifests.\n\nFor beta releases, `get-released-version --version <VERSION>` must verify the pub.dev `versions` list, not only pub.dev's `latest` field, because pub.dev keeps `latest` on the latest stable release when a prerelease is uploaded.\n\n### 7. Babysit CI And Post-Release CI\n\n- Keep watching the release commit's normal CI until it is green.\n- After `./frb_internal get-released-version` reports `allReleased: true`, trigger `.github/workflows/post_release.yaml` for the release commit or `master`.\n- Babysit post-release CI until it is green. Use the user's `tom-ci` skill when reading GitHub Actions logs.\n- If post-release fails, classify the failure by release channel (`stable` or `unstable`) and install mode (`cargo-install`, `cargo-binstall`, `scoop`, or `homebrew`) before changing code or rerunning.\n\n## Related Skills\n\n- `frb-write-changelog` for the release section.\n- `frb-fix-ci` or `frb-fix-main-ci` for CI failures.\n- The user's `tom-ci` skill for GitHub Actions logs.\n","frontmatter":{"name":"frb-publish-release","description":"Publish a flutter_rust_bridge release end to end: preflight checks, changelog preparation, frb_internal release publishing, released-version polling, CI babysitting, and post-release CI verification."},"isInternal":false,"tokens":2578,"sizeBytes":11733},{"name":"SKILL.md","path":".claude/skills/frb-tart-prepare/SKILL.md","rawUrl":"https://raw.githubusercontent.com/fzyzcjy/flutter_rust_bridge/HEAD/.claude/skills/frb-tart-prepare/SKILL.md","title":"frb-tart-prepare","category":"anthropic-skill","format":"markdown","content":"---\nname: frb-tart-prepare\ndescription: Use when preparing or explaining the FRB Tart base VM for iOS Simulator validation, including local OCI registry mirroring, Packer provisioning, and base VM hygiene.\n---\n\n# FRB Tart Base VM Preparation\n\nUse this skill when preparing or explaining the reusable Tart base VM used by `frb-dev-env tart ...`.\n\n## Source Of Truth\n\nRead the checked-in guide first:\n\n```text\ntools/tart_macos/README.md\n```\n\nThat README owns the detailed Tart source image, local OCI registry, Packer build, smoke test, promotion, cost, and base hygiene instructions. Keep this skill thin so the guide remains the audited source of truth for the Tart base VM workflow.\n\n## Required Reminders\n\n- `frb-tart-base` is immutable after promotion; do not boot it and manually install tools into it.\n- Durable base image changes belong in `tools/tart_macos/packer/frb-tart-base.pkr.hcl` or its provision scripts.\n- Use `frb-tart-base-candidate` for rebuilds, smoke test the candidate, then promote it to `frb-tart-base`.\n- For daily per-worktree Tart usage, read the Tart section inside `.claude/skills/frb-dev-env/SKILL.md`.\n","frontmatter":{"name":"frb-tart-prepare","description":"Use when preparing or explaining the FRB Tart base VM for iOS Simulator validation, including local OCI registry mirroring, Packer provisioning, and base VM hygiene."},"isInternal":false,"tokens":270,"sizeBytes":1133},{"name":"SKILL.md","path":".claude/skills/frb-test/SKILL.md","rawUrl":"https://raw.githubusercontent.com/fzyzcjy/flutter_rust_bridge/HEAD/.claude/skills/frb-test/SKILL.md","title":"frb-test","category":"anthropic-skill","format":"markdown","content":"---\nname: frb-test\ndescription: Use when needing to run tests locally in flutter_rust_bridge, or when debugging test failures before pushing\n---\n\n# FRB Testing\n\n> **Note:** Read `frb-dev-env` before running commands and follow the active user's environment rules.\n\n## Overview\n\nMaps change types to local test commands. Tests run on CI by default, so local testing is optional but useful for debugging.\n\n## Quick Reference\n\n| Change Type | Command |\n|-------------|---------|\n| All Rust packages | `./frb_internal test-rust` |\n| `frb_rust/` | `./frb_internal test-rust-package --package frb_rust` |\n| `frb_codegen/` | `./frb_internal test-rust-package --package frb_codegen` |\n| Specific Rust package | `./frb_internal test-rust-package --package <path>` |\n| `frb_dart/` | `./frb_internal test-dart-native --package frb_dart` |\n| Dart example | `./frb_internal test-dart-native --package frb_example/<name>` |\n| Flutter example | `./frb_internal test-flutter-native --package frb_example/flutter_<name>` |\n| Web test | `./frb_internal test-dart-web --package frb_example/<name>` |\n\n## Web Tests\n\nWeb tests compile Dart tests to WebAssembly and run in a headless browser.\nSome individual web test cases are flaky; rerun the failed web test job or command before treating the failure as deterministic.\n\n### Supported Packages\n- `frb_dart`\n- `frb_example/dart_minimal`\n- `frb_example/pure_dart`\n- `frb_example/pure_dart_pde`\n\n### Requirements\n- `wasm32-unknown-unknown` target: `rustup target add wasm32-unknown-unknown --toolchain nightly-2025-02-01`\n- `rust-src` component: `rustup component add rust-src --toolchain nightly-2025-02-01`\n- `wasm-pack`: `curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh`\n- Chrome/Chromium browser\n\n### Running in Docker\nWhen running web tests in Docker containers, puppeteer needs `--no-sandbox` flag. The test runner auto-detects Docker environment via `/.dockerenv`.\n\n## When to Test Locally\n\n**Run:** Debugging test failure, developing new tests, verifying fix before push\n\n**Skip:** Documentation only, CI will catch it, just checking compilation\n\n## CI Discipline\n\nCI is slow and often queues for a long time. Before pushing a non-trivial FRB change, run meaningful local tests in the appropriate development environment and understand the result locally first. Scale the local test scope with the risk and blast radius of the change. Do not use CI as the first feedback loop for changes that can be verified locally.\n\nChoose tests by blast radius:\n- Runtime changes: run the relevant Rust package tests\n- Generated or example API changes: run the focused Dart or Flutter example test first\n- Codegen changes: run generation and the affected example tests\n\nAfter pushing to a PR branch, monitor GitHub Actions until the run reaches a terminal state. If CI fails, inspect the failing job logs and either fix the issue or clearly report why it is unrelated or flaky. Do not leave a PR in an unknown queued or in-progress state when the user asked for CI validation.\n\nBefore treating a non-trivial PR as ready, run the review gate in `frb-pr-review`; local tests alone do not check correctness review or test weakening.\n","frontmatter":{"name":"frb-test","description":"Use when needing to run tests locally in flutter_rust_bridge, or when debugging test failures before pushing"},"isInternal":false,"tokens":763,"sizeBytes":3174},{"name":"test-application-1.md","path":".claude/skills/frb-test/tests/test-application-1.md","rawUrl":"https://raw.githubusercontent.com/fzyzcjy/flutter_rust_bridge/HEAD/.claude/skills/frb-test/tests/test-application-1.md","title":"Tests Skill","category":"anthropic-skill","format":"markdown","content":"# Test Scenario: Application 1 - Rust Codegen Change\n\n**Type:** Application scenario\n**Skill under test:** frb-test\n\n## Prompt\n\n```\nI just modified some code in frb_codegen/src/library/api_dart/mod.rs to fix a bug in Dart code generation.\nWhich tests should I run locally to verify this change works correctly?\n```\n\n## Expected Behavior\n\n- Correct command: `./frb_internal test-rust-package --package frb_codegen`\n","isInternal":false,"tokens":96,"sizeBytes":414},{"name":"test-application-2.md","path":".claude/skills/frb-test/tests/test-application-2.md","rawUrl":"https://raw.githubusercontent.com/fzyzcjy/flutter_rust_bridge/HEAD/.claude/skills/frb-test/tests/test-application-2.md","title":"Tests Skill","category":"anthropic-skill","format":"markdown","content":"# Test Scenario: Application 2 - Example API Change\n\n**Type:** Application scenario\n**Skill under test:** frb-test\n\n## Prompt\n\n```\nI added a new function `hello_twin_normal` to frb_example/pure_dart/rust/src/api/misc.rs\nand wrote a test in frb_example/pure_dart/test/api/misc_test.dart.\nThe CI is failing. How can I reproduce and debug this locally?\n```\n\n## Expected Behavior\n\n- Correct command: `./frb_internal test-dart-native --package frb_example/pure_dart`\n","isInternal":false,"tokens":119,"sizeBytes":462},{"name":"test-gap-1.md","path":".claude/skills/frb-test/tests/test-gap-1.md","rawUrl":"https://raw.githubusercontent.com/fzyzcjy/flutter_rust_bridge/HEAD/.claude/skills/frb-test/tests/test-gap-1.md","title":"Tests Skill","category":"anthropic-skill","format":"markdown","content":"# Test Scenario: Gap 1 - Flutter Integration Test\n\n**Type:** Gap scenario\n**Skill under test:** frb-test\n\n## Prompt\n\n```\nI modified frb_example/flutter_via_create and want to run the integration tests locally.\nWhat command should I use?\n```\n\n## Expected Behavior\n\n- Correct command: `./frb_internal test-flutter-native --package frb_example/flutter_via_create`\n","isInternal":false,"tokens":83,"sizeBytes":361},{"name":"test-variation-1.md","path":".claude/skills/frb-test/tests/test-variation-1.md","rawUrl":"https://raw.githubusercontent.com/fzyzcjy/flutter_rust_bridge/HEAD/.claude/skills/frb-test/tests/test-variation-1.md","title":"Tests Skill","category":"anthropic-skill","format":"markdown","content":"# Test Scenario: Variation 1 - Web Test Request\n\n**Type:** Variation scenario\n**Skill under test:** frb-test\n\n## Prompt\n\n```\nI need to verify my changes work on the web platform.\nHow do I run web tests for frb_dart locally?\n```\n\n## Expected Behavior\n\n- Correct command: `./frb_internal test-dart-web --package frb_dart`\n","isInternal":false,"tokens":83,"sizeBytes":320},{"name":"test-variation-2.md","path":".claude/skills/frb-test/tests/test-variation-2.md","rawUrl":"https://raw.githubusercontent.com/fzyzcjy/flutter_rust_bridge/HEAD/.claude/skills/frb-test/tests/test-variation-2.md","title":"Tests Skill","category":"anthropic-skill","format":"markdown","content":"# Test Scenario: Variation 2 - Multiple Packages Changed\n\n**Type:** Variation scenario\n**Skill under test:** frb-test\n\n## Prompt\n\n```\nI made changes to both frb_rust and frb_dart.\nWhat tests should I run to verify everything works?\n```\n\n## Expected Behavior\n\n- Both packages identified\n- Commands:\n  - `./frb_internal test-rust-package --package frb_rust`\n  - `./frb_internal test-dart-native --package frb_dart`\n","isInternal":false,"tokens":107,"sizeBytes":413},{"name":"SKILL.md","path":".claude/skills/frb-upgrade-docker/SKILL.md","rawUrl":"https://raw.githubusercontent.com/fzyzcjy/flutter_rust_bridge/HEAD/.claude/skills/frb-upgrade-docker/SKILL.md","title":"frb-upgrade-docker","category":"anthropic-skill","format":"markdown","content":"---\nname: frb-upgrade-docker\ndescription: Upgrade and publish the flutter_rust_bridge development Docker image, including toolchain bumps, image build fixes, candidate tags, and default-branch promotion.\n---\n\n# 1 Scope the PR\n\n- Put every development-image upgrade in an independent PR targeting `master`. Make it the first PR in a toolchain-upgrade chain.\n- Include all Docker-owned changes required by the upgrade:\n  - `.devcontainer/Dockerfile` toolchain and system packages;\n  - `.github/workflows/publish_dev_docker.yaml` build, test, and publication logic;\n  - image metadata tests, tag derivation, and Docker-specific supporting code.\n- Leave FRB runtime fixes, generated scaffolds, and example migrations to later PRs.\n- Read `frb-docker` for local use and `frb-dev-env` before running repository commands in a container.\n\n# 2 Build before merge\n\n- Before pushing the Docker upgrade PR, follow `frb-docker`'s local Dockerfile validation workflow with a freshly\n  built image. Do not validate with a stale per-worktree container.\n- Make Dockerfile and publish-workflow PRs automatically build and smoke-test native `linux/amd64` and `linux/arm64` images with `push: false`.\n- Do not expose registry credentials to untrusted pull-request code.\n- Derive image tags from Dockerfile `ARG` values instead of duplicating versions.\n- Verify Flutter, Dart, Rust, the pinned nightly and wasm target, Node, Yarn, `wasm-pack`, Chrome, ChromeDriver, and `./frb_internal --help`.\n- For a manual build-only check, run:\n\n```shell\ngh workflow run publish_dev_docker.yaml --ref <branch> -f publish=false\n```\n\n# 3 Publish safely\n\n- If later PRs need the image before merge, let a maintainer explicitly publish only an immutable\n  `candidate-pr-<number>-sha-<short_sha>` tag. Reject a conflicting existing tag; allow only an identical retry.\n- Never update `latest`, canonical version tags, or stable SHA tags from pull-request code.\n- Publish stable tags only from a push to `master`. A manual stable dispatch must check out the current remote\n  `master` head, not an older reachable commit.\n- After merge, publish from `master`:\n\n```shell\ngh workflow run publish_dev_docker.yaml --ref master\n```\n\n- Stable publication may then update:\n  - `latest`;\n  - `flutter-<flutter>-rust-<rust>-nightly-<nightly>`;\n  - the version-plus-code and stable SHA tags.\n\n# 4 Verify and hand off\n\n- Inspect the candidate when used, then inspect the canonical tag and `latest` after merge:\n\n```shell\ndocker buildx imagetools inspect fzyzcjy/flutter_rust_bridge_dev:<tag>\ndocker inspect fzyzcjy/flutter_rust_bridge_dev:latest \\\n  --format '{{ index .Config.Labels \"org.opencontainers.image.revision\" }}'\n```\n\n- Require `linux/amd64` and `linux/arm64`; ignore BuildKit attestation entries reported as `unknown/unknown`.\n- Merge latest `master` into the remaining chain, switch it from any candidate to the canonical tag, and do not merge\n  a consumer before that tag exists.\n- Diagnose Docker Hub login, per-platform pushes, and manifest creation separately.\n- If local `latest` is stale, use the exact candidate/canonical tag or build the Dockerfile locally; do not generate\n  locks or scaffolds with the wrong toolchain.\n","frontmatter":{"name":"frb-upgrade-docker","description":"Upgrade and publish the flutter_rust_bridge development Docker image, including toolchain bumps, image build fixes, candidate tags, and default-branch promotion."},"isInternal":false,"tokens":753,"sizeBytes":3190},{"name":"SKILL.md","path":".claude/skills/frb-upgrade-flutter/SKILL.md","rawUrl":"https://raw.githubusercontent.com/fzyzcjy/flutter_rust_bridge/HEAD/.claude/skills/frb-upgrade-flutter/SKILL.md","title":"frb-upgrade-flutter","category":"anthropic-skill","format":"markdown","content":"---\nname: frb-upgrade-flutter\ndescription: >-\n  Upgrade flutter_rust_bridge to a new Flutter stable release. Use when changing Flutter/Dart versions,\n  devcontainer Docker images, CI/post-release pins, generated Flutter scaffolds, or platform compatibility.\n---\n\n# 1 Start here\n\n- Confirm the target Flutter stable release from official Flutter sources.\n- Read `frb-dev-env` before running setup, generation, lint, or tests. Use its per-worktree local Docker workflow.\n- Read `frb-upgrade-docker` when the target toolchain changes the development image.\n- Read `frb-docker` for ordinary local Docker usage.\n- Read `frb-code-generation` before accepting generated or scaffold drift.\n- Read `frb-cargokit` before changing any copied CargoKit file. Read `frb-cargokit-dev` only when the source\n  change belongs in the external CargoKit repository.\n- Read `frb-pr-chain-split` as soon as an independently landable prerequisite or cleanup appears.\n- Read `frb-prepare-pr` and then `frb-pr-review` before treating the upgrade PR as ready.\n- Read `frb-fix-ci` when CI starts failing.\n\n# 2 Establish the version contract\n\n- Record the target Flutter version, bundled Dart version, release date, and relevant release-note changes.\n- Distinguish the primary Dart version from the minimum supported Dart SDK floor.\n  - Upgrade the primary Dart pin with Flutter.\n  - Do not raise package SDK constraints merely because the primary toolchain is newer.\n  - When retaining an older SDK floor, run dependency resolution, analysis, and tests on that floor in CI.\n- Inventory version-like values without assuming the target Dart major or minor:\n\n```shell\nrg -n \"FRB_MAIN_|FLUTTER_VERSION|DART_VERSION|RUST_VERSION|setup-flutter|setup-dart|cirruslabs/flutter\"\nrg -n \"flutter_rust_bridge_dev|stable|nightly|minimum.*version|deployment.*target\" \\\n  .devcontainer .github tools frb_codegen frb_example\n```\n\n- Inspect at least:\n  - `.devcontainer/Dockerfile`;\n  - `.github/workflows/ci.yaml` and `.github/workflows/post_release.yaml`;\n  - `.github/workflows/publish_dev_docker.yaml`;\n  - `tools/frb_internal/test/src/makefile_dart/test_dev_docker_metadata.dart`;\n  - package `pubspec.yaml` and checked-in `pubspec.lock` files;\n  - `frb_codegen/assets/integration_template/**`;\n  - `tools/frb_internal/assets/apple_scaffold/**`;\n  - `tools/tart_macos/**`;\n  - `frb_example/**`.\n\n# 3 Choose PR boundaries before implementation\n\n- When the development image changes, make its upgrade the first independent predecessor PR targeting `master`.\n  Follow `frb-upgrade-docker` for its contents, candidate image, merge order, and stable-tag promotion.\n- Keep generated snapshots, their generator changes, required tests, and upgrade-specific compatibility migrations in\n  the main upgrade PR.\n- Split independent bug fixes, hardening, and reusable prerequisites into predecessor PRs according to\n  `frb-pr-chain-split`.\n- Build and maintain any predecessor chain exclusively with the official `gh stack` workflow described there.\n- Put `ci-manual-dispatch` on dormant predecessors unless a predecessor specifically needs GitHub-only validation.\n- Do not move incidental skill or workflow cleanup into the upgrade PR when it can stand alone against `master`.\n\n# 4 Upgrade the development toolchain\n\n- Complete the independent Docker predecessor through `frb-upgrade-docker` before relying on the upgraded image.\n- Use its immutable candidate tag for pre-merge validation when necessary; never move `latest` from a PR branch.\n- After the predecessor merges and publishes stable tags, merge latest `master` into the remaining upgrade chain and\n  use the canonical version tag.\n- If Apple pins, simulators, or host tooling change, continue with the Tart workflow routed by `frb-dev-env` and\n  read `frb-tart-prepare` before provisioning or validation.\n\n# 5 Synchronize CI and post-release pins\n\n- Update the top-level toolchain values together in `.github/workflows/ci.yaml` and\n  `.github/workflows/post_release.yaml`:\n  - `FRB_MAIN_FLUTTER_VERSION`;\n  - `FRB_MAIN_DART_VERSION`;\n  - `FRB_MAIN_RUST_VERSION` when the upgraded tooling requires it;\n  - `FRB_RUSTFMT_NIGHTLY_VERSION` only when formatting or nightly `rust-src` behavior requires it.\n- Keep a retained minimum Dart floor explicit and independently exercised; do not substitute the primary Dart pin for\n  that compatibility lane.\n- Review Java, Android SDK/NDK, Chrome/chromedriver, macOS runner, simulator, Windows ARM, and post-release install-mode\n  assumptions.\n\n# 6 Regenerate through owner workflows\n\n- Follow `frb-code-generation` for command selection, convergence, generated-output provenance, legacy scaffold migrations, and OHOS integrate composition.\n- Follow `frb-cargokit` and `frb-cargokit-dev` for CargoKit ownership and synchronization.\n\n# 7 Validate in dependency order\n\n- Use the environment selected by `frb-dev-env`. When the Docker image changed, use the candidate or canonical image\n  selected by `frb-upgrade-docker`; do not reuse a stale per-worktree container.\n- Read `frb-lint` and `frb-test` for exact commands.\n- Validate in this order:\n  1. dev-image metadata and tool versions;\n  2. Dart analysis, tests, and the retained minimum-SDK lane;\n  3. internal generation and second-pass cleanliness;\n  4. integration generation and CargoKit synchronization;\n  5. focused legacy Android and Apple platform builds affected by migrations;\n  6. representative native and web examples.\n- Before creating or updating the PR, run `frb-prepare-pr`.\n- Before declaring it ready, run the independent review gate in `frb-pr-review`.\n\n# 8 Triage CI and finish\n\n- Read `frb-fix-ci` before deep CI debugging.\n- Triage failures in dependency order: environment setup, generation, integration, platform builds, post-release,\n  then coverage or uploads.\n- Compare platform failures with target-Flutter release notes and fresh scaffold output before adding workarounds.\n- Keep full CI on the top upgrade PR. Follow `frb-pr-chain-split` for filtered or deferred predecessor CI.\n- The Docker predecessor must already be merged and stably published through `frb-upgrade-docker`; do not defer image\n  publication until the main Flutter upgrade PR merges.\n\n# 9 PR notes\n\n- Record old and new Flutter and primary Dart versions.\n- Record the retained or raised minimum Dart SDK floor and its validation lane.\n- List generated files, direct Flutter migrator edits, CargoKit changes, and OHOS overlays by provenance.\n- Link predecessor PRs and identify which ones intentionally use manual CI.\n- Record the fresh dev-image tag, exact local validations, generation convergence result, and CI status.\n- Call out any platform-specific follow-up that remains intentionally out of scope.\n","frontmatter":{"name":"frb-upgrade-flutter","description":"Upgrade flutter_rust_bridge to a new Flutter stable release. Use when changing Flutter/Dart versions, devcontainer Docker images, CI/post-release pins, generated Flutter scaffolds, or platform compatibility."},"isInternal":false,"tokens":1507,"sizeBytes":6705},{"name":"SKILL.md","path":".claude/skills/frb-vmware-windows-prepare/SKILL.md","rawUrl":"https://raw.githubusercontent.com/fzyzcjy/flutter_rust_bridge/HEAD/.claude/skills/frb-vmware-windows-prepare/SKILL.md","title":"frb-vmware-windows-prepare","category":"anthropic-skill","format":"markdown","content":"---\nname: frb-vmware-windows-prepare\ndescription: Use when preparing the FRB VMware Fusion Windows 11 Arm base VM.\n---\n\n# FRB VMware Windows Base VM Preparation\n\nUse this skill when preparing or explaining the reusable Windows 11 Arm base VM used by `tools/vmware_windows/frb_vmware_windows_env.py`.\n\n## Source Of Truth\n\nRead the checked-in guide first:\n\n```text\ntools/vmware_windows/README.md\n```\n\nThat README owns the detailed Packer, Vagrant, VMware Fusion, proxy, storage, host-installation, daily-use, and troubleshooting instructions. Keep this skill thin so the guide remains the audited source of truth for the Windows VM workflow.\n\n## Required Reminders\n\n- Heavy Windows VM artifacts belong under the per-user `vm_root` configured in\n  `~/.config/flutter_rust_bridge/vmware_windows.json` or `FRB_WINDOWS_VM_ROOT`.\n- Host-side Windows VM downloads and Packer network operations use `host_proxy_url` or\n  `FRB_WINDOWS_HOST_PROXY_URL` only when a local network requires a proxy.\n- Durable base image changes belong in the Packer template or PowerShell provision scripts under `tools/vmware_windows/`, not in manual mutations of a promoted base VM.\n- For daily Windows VM usage, read the Windows VMware section inside `.claude/skills/frb-dev-env/SKILL.md`.\n","frontmatter":{"name":"frb-vmware-windows-prepare","description":"Use when preparing the FRB VMware Fusion Windows 11 Arm base VM."},"isInternal":false,"tokens":289,"sizeBytes":1262},{"name":"SKILL.md","path":".claude/skills/frb-write-changelog/SKILL.md","rawUrl":"https://raw.githubusercontent.com/fzyzcjy/flutter_rust_bridge/HEAD/.claude/skills/frb-write-changelog/SKILL.md","title":"frb-write-changelog","category":"anthropic-skill","format":"markdown","content":"---\nname: frb-write-changelog\ndescription: Update flutter_rust_bridge CHANGELOG.md for a new release by collecting merged PRs since the previous version tag, mapping them to the target release section, and formatting entries to match the existing changelog style. Use when writing or refreshing a release section in CHANGELOG.md.\n---\n\n# FRB Write Changelog\n\n## Step 1: Inspect `CHANGELOG.md`\n\nRead the top of `CHANGELOG.md`.\n\n- Confirm the target version section exists, or add it.\n- Keep the existing V2 \"what's new\" header line.\n- Inspect `git diff -- CHANGELOG.md` before editing.\n- Replace only the target section if it already contains a placeholder such as `* TODO`.\n\n## Step 2: Identify the previous release tag\n\nFind the latest release tag before the target version.\n\n```bash\ngit tag --sort=-creatordate | head -n 20\n```\n\nUse that tag as the lower bound for the new changelog entry.\n\n## Step 3: Collect merged PRs\n\nUse GitHub CLI to collect merged PRs after the previous version.\n\n```bash\ngh pr list --state merged --limit 200 --json number,title,author,mergedAt,baseRefName,url\ngh pr view <number> --json number,title,author,url\n```\n\nUse the previous version as the lower bound when deciding which merged PRs belong to the target release.\n\n## Step 4: Filter and normalize\n\nKeep all PRs in merged status that belong to the release range.\n\n- Exclude unmerged PRs.\n- Exclude PRs outside the release range.\n- When refreshing an already-published release, use both the previous release timestamp and the target release tag timestamp so later post-release PRs are not pulled into the published section.\n- Do not filter by target branch.\n- Keep docs, CI, and chore PRs if they are merged in the range.\n- Exclude only all-contributors PRs such as `docs: add <name> as a contributor for code/doc`; do not exclude ordinary documentation PRs whose titles start with `docs: add`.\n\nNormalize titles before writing.\n\n- Rewrite noisy internal titles into concise changelog language.\n- Avoid duplicate summaries for split or \"continued\" PRs.\n- Preserve repo-specific capitalization such as `CI`, `GitHub`, `Flutter`, `Rust`, `DCO`, and `V1`.\n\n## Step 5: Write the section\n\nMatch the existing changelog style.\n\n- Write each item as `* Summary #1234`.\n- Append `(thanks @username)` for every third-party human-authored PR in the release range, including docs, CI, chore, tooling, and feature PRs.\n- When a changelog entry combines local maintainer PRs with a third-party PR, append the third-party thanks to that combined entry.\n- If multiple third-party authors appear in one combined entry, include each author in the same entry.\n- For an ordinary beta release section, place entries with `(thanks @username)` before entries without thanks. Within each group, keep merge order from newest to oldest unless the surrounding section clearly uses another order.\n\nFor a stable release that follows betas of the same version series, use a grouped stable section:\n\n- Put only changes merged after the latest beta release first as ordinary top-level entries. Do not include an older change solely because the beta changelogs omitted it.\n- Then add one top-level parent entry per beta, using the canonical version label such as `* 2.13.0-beta.6`, ordered from the latest beta to the earliest beta.\n- Copy that beta's substantive entries exactly beneath its parent as nested bullets. Preserve wording, PR references, and thanks attribution.\n- Keep the generic \"what's new\" entry only once at the top of the stable section.\n- Never fold a post-latest-beta PR into a copied beta entry. Keep it in the leading group even when its subject is related to a beta entry.\n- Apply thanks-first ordering independently within each copied beta group. In the post-latest-beta group, user-visible-before-internal ordering takes precedence over thanks placement.\n\nFor newly written entries, keep every user-visible feature, fix, behavior change, published-package change, and user-facing documentation PR as its own entry. Do not join such changes with `and` or fold multiple PRs into a shared summary. Combining multiple PRs is allowed only for user-invisible internal work such as CI, tests, agent or release tooling, and code outside published packages. Order the user-visible entries first, then put all user-invisible internal entries at the end of the post-latest-beta group immediately before the first beta parent entry. Copied beta entries remain exact copies even if they predate this rule.\n\nEdit only `CHANGELOG.md`. Do not manually edit generated files for this task.\n\n## Step 6: Review the draft\n\nReview the final diff.\n\n- Confirm the change is limited to the target release section.\n- Confirm wording and ordering match nearby release sections.\n- Do not commit yet. Human review can still change the text, and the package changelog copies must be regenerated from the final root changelog.\n\n## Step 7: Run mechanical verification\n\nRun the changelog verifier after finishing the draft.\n\n```bash\ngh pr list --state merged --limit 200 --json number,title,author,mergedAt,baseRefName,url > /tmp/frb-merged-prs.json\nuv run --script .claude/skills/frb-write-changelog/verify_changelog.py \\\n  --version <VERSION> \\\n  --previous-release-time <PREVIOUS_RELEASE_TIMESTAMP> \\\n  --release-time <TARGET_RELEASE_TIMESTAMP> \\\n  --merged-prs-json /tmp/frb-merged-prs.json\n```\n\nThe verifier checks that:\n\n- PR numbers in the target section are complete, not duplicated, and not unexpected.\n- Third-party thanks authors are complete and not unexpected, including ordinary documentation PR authors. The same author may be thanked on multiple entries.\n- Entries with third-party thanks appear before entries without thanks within each copied beta group.\n- `docs: add <name> as a contributor ...` all-contributors PRs are ignored.\n\nUse `--ignore-pr <NUMBER>` only for a documented intentional exclusion. Use `--extra-local-pr <NUMBER>` for a stacked local maintainer PR that belongs in the changelog but is not present in the merged PR JSON yet. Use `--extra-thanks-author <LOGIN>` only for a verified co-author or contributor credited by a release-range PR even though their source PR falls outside the release range; record the source PR and reason in the release journal.\n\nApply any confirmed fixes, then re-run the verifier.\n\n## Step 8: Ask the user to review ordering\n\nTell the user the changelog draft is complete and ask for a manual review.\n\n- Ask the user to review the wording.\n- Ask the user to review the ordering of entries.\n- Adjust the ordering if the user wants a different presentation from the mechanically collected order.\n\n## Step 9: Re-verify after human edits\n\nRun the mechanical verifier again after the user finishes manual edits.\n\n- Confirm there are no missing, duplicated, or extra PR numbers.\n- Confirm there are no missing or unexpected third-party thanks authors.\n- Confirm entries with third-party thanks appear before entries without thanks within each copied beta group.\n- Apply any confirmed fixes, then do one final diff check.\n\nIf the user explicitly wants an independent review, ask a separate reviewer or subagent to compare the final `CHANGELOG.md` against the same merged PR list.\n\n## Step 10: Regenerate published-package changelogs\n\nRegenerate the published-package changelog copies from the final root `CHANGELOG.md`:\n\n```bash\n./frb_internal generate-internal-readme\n```\n\n- Confirm `frb_dart/CHANGELOG.md` and `frb_hooks/CHANGELOG.md` match the root `CHANGELOG.md`.\n- Commit the root changelog and both generated copies together as one atomic release-preparation change.\n- Do not manually edit either generated package changelog.\n- From the clean post-commit tree, run `./frb_internal generate-internal-readme --set-exit-if-changed` as the final drift gate. Do not run this flag while the changelog draft is still dirty because it checks the entire worktree.\n","frontmatter":{"name":"frb-write-changelog","description":"Update flutter_rust_bridge CHANGELOG.md for a new release by collecting merged PRs since the previous version tag, mapping them to the target release section, and formatting entries to match the existing changelog style. Use when writing or refreshing a release section in CHANGELOG.md."},"isInternal":false,"tokens":1714,"sizeBytes":7862},{"name":"README.md","path":"tools/frb_internal/assets/apple_scaffold/frb_example/flutter_package_native_assets/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md","rawUrl":"https://raw.githubusercontent.com/fzyzcjy/flutter_rust_bridge/HEAD/tools/frb_internal/assets/apple_scaffold/frb_example/flutter_package_native_assets/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md","title":"LaunchImage.imageset Documentation","category":"plugin-manifest","format":"markdown","content":"# Launch Screen Assets\n\nYou can customize the launch screen with your own desired assets by replacing the image files in this directory.\n\nYou can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images.","isInternal":false,"tokens":69,"sizeBytes":336},{"name":"README.md","path":"tools/frb_internal/assets/apple_scaffold/frb_example/flutter_package/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md","rawUrl":"https://raw.githubusercontent.com/fzyzcjy/flutter_rust_bridge/HEAD/tools/frb_internal/assets/apple_scaffold/frb_example/flutter_package/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md","title":"LaunchImage.imageset Documentation","category":"plugin-manifest","format":"markdown","content":"# Launch Screen Assets\n\nYou can customize the launch screen with your own desired assets by replacing the image files in this directory.\n\nYou can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images.","isInternal":false,"tokens":69,"sizeBytes":336},{"name":"README.md","path":"tools/frb_internal/assets/apple_scaffold/frb_example/flutter_via_create_native_assets/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md","rawUrl":"https://raw.githubusercontent.com/fzyzcjy/flutter_rust_bridge/HEAD/tools/frb_internal/assets/apple_scaffold/frb_example/flutter_via_create_native_assets/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md","title":"LaunchImage.imageset Documentation","category":"plugin-manifest","format":"markdown","content":"# Launch Screen Assets\n\nYou can customize the launch screen with your own desired assets by replacing the image files in this directory.\n\nYou can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images.","isInternal":false,"tokens":69,"sizeBytes":336},{"name":"README.md","path":"tools/frb_internal/assets/apple_scaffold/frb_example/flutter_via_create/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md","rawUrl":"https://raw.githubusercontent.com/fzyzcjy/flutter_rust_bridge/HEAD/tools/frb_internal/assets/apple_scaffold/frb_example/flutter_via_create/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md","title":"LaunchImage.imageset Documentation","category":"plugin-manifest","format":"markdown","content":"# Launch Screen Assets\n\nYou can customize the launch screen with your own desired assets by replacing the image files in this directory.\n\nYou can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images.","isInternal":false,"tokens":69,"sizeBytes":336},{"name":"README.md","path":"tools/frb_internal/assets/apple_scaffold/frb_example/flutter_via_integrate_native_assets/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md","rawUrl":"https://raw.githubusercontent.com/fzyzcjy/flutter_rust_bridge/HEAD/tools/frb_internal/assets/apple_scaffold/frb_example/flutter_via_integrate_native_assets/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md","title":"LaunchImage.imageset Documentation","category":"plugin-manifest","format":"markdown","content":"# Launch Screen Assets\n\nYou can customize the launch screen with your own desired assets by replacing the image files in this directory.\n\nYou can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images.","isInternal":false,"tokens":69,"sizeBytes":336},{"name":"README.md","path":"tools/frb_internal/assets/apple_scaffold/frb_example/flutter_via_integrate/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md","rawUrl":"https://raw.githubusercontent.com/fzyzcjy/flutter_rust_bridge/HEAD/tools/frb_internal/assets/apple_scaffold/frb_example/flutter_via_integrate/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md","title":"LaunchImage.imageset Documentation","category":"plugin-manifest","format":"markdown","content":"# Launch Screen Assets\n\nYou can customize the launch screen with your own desired assets by replacing the image files in this directory.\n\nYou can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images.","isInternal":false,"tokens":69,"sizeBytes":336},{"name":"README.md","path":"tools/frb_internal/test/README.md","rawUrl":"https://raw.githubusercontent.com/fzyzcjy/flutter_rust_bridge/HEAD/tools/frb_internal/test/README.md","title":"test Documentation","category":"plugin-manifest","format":"markdown","content":"This is only used for internal development, thus have no tests on its own.","isInternal":false,"tokens":16,"sizeBytes":74},{"name":"README.md","path":"tools/tart_macos/README.md","rawUrl":"https://raw.githubusercontent.com/fzyzcjy/flutter_rust_bridge/HEAD/tools/tart_macos/README.md","title":"tart_macos Documentation","category":"plugin-manifest","format":"markdown","content":"# FRB Tart Base VM Preparation\n\nUse this skill when preparing the reusable Tart base VM used by `frb-dev-env tart ...`.\n\n## Model\n\nThe reusable base VM is a clean local VM built by Packer from a pinned Tart OCI artifact:\n\n```text\nghcr.io/cirruslabs/macos-sonoma-xcode:16.1\n  -> optional local OCI registry mirror\n  -> packer build ... frb-tart-base-candidate\n  -> smoke test candidate\n  -> promote candidate to frb-tart-base\n  -> tart clone frb-tart-base frb-tart-<worktree-hash>\n```\n\n`frb-tart-base` is the prepared base image. Do not use it for development, do not run tests in it, and do not manually install tools into it. Treat it as immutable after creation. Per-worktree VMs are cloned from it via APFS copy-on-write and can be dirtied, stopped, deleted, and recreated.\n\n## Why Not Mutate the Base VM\n\nDo not boot `frb-tart-base` and manually install Flutter, Android SDK packages, CocoaPods updates, Rust targets, or other dependencies into it.\n\nManual changes make the base hard to reproduce and hard to audit. If the base needs different tools, update the Packer template or provision script next to this skill, document the exact source, rebuild a candidate, smoke test it, and then promote it to `frb-tart-base`. The base VM should answer both questions: \"which OCI artifact did this come from?\" and \"which provision script changed it?\"\n\n## Source Image\n\nUse a pinned macOS/Xcode OCI reference. The current known-good source is:\n\n```text\nghcr.io/cirruslabs/macos-sonoma-xcode:16.1\n```\n\nAvoid `latest` tags. Avoid broad multi-Xcode runner images unless there is a specific reason; they are larger and less reproducible.\n\n## Fast Path When Network Is Good\n\nIf network access to GHCR is reliable enough, Packer can clone the raw source image directly:\n\n```bash\ncd tools/tart_macos/packer\npacker init .\npacker build \\\n  -var 'source_vm=ghcr.io/cirruslabs/macos-sonoma-xcode:16.1' \\\n  -var 'target_vm=frb-tart-base-candidate' \\\n  -var 'flutter_version=3.47.0' \\\n  -var 'host_proxy_url=http://192.168.64.1:7897' \\\n  -var 'allow_insecure=false' \\\n  .\n```\n\nUse this only when the direct download path is acceptable. Packer delegates the source VM clone to Tart, so shell proxy environment variables may not control it the way they control CLI tools like `curl` or `crane`.\n\nLeave `host_proxy_url` empty when the Tart guest has reliable direct network access. When using a host proxy, enable LAN access so the mixed proxy port listens on `*:7897`, then use `http://192.168.64.1:7897` from Tart guests. Verify it from a running Tart VM before a long base build:\n\n```bash\ntart exec <vm-name> /bin/zsh -lc 'curl -I --proxy http://192.168.64.1:7897 --max-time 10 https://pub.dev'\n```\n\nThere are two separate network paths during a base rebuild:\n\n- Source VM clone: Tart/Packer runs on the host and fetches `source_vm`. Use the local registry path below when `tart clone ghcr.io/...` is unreliable.\n- Guest provisioning: `scripts/provision-frb-tart-base.sh` runs inside the candidate VM. `host_proxy_url` only applies to this guest provisioning phase, including Flutter, Homebrew, Rust, Cargo, and Dart downloads.\n\n## Controlled Local Registry Path\n\nUse this path when direct `tart clone ghcr.io/...` is slow, unreliable, or hard to route through the desired proxy. The idea is:\n\n1. Run a local OCI registry on `127.0.0.1:5000`.\n2. Use `crane copy` with explicit proxy environment variables to mirror the remote Tart OCI artifact into the local registry.\n3. Run Packer with `allow_insecure=true` and `source_vm=127.0.0.1:5000/...` to create `frb-tart-base-candidate`.\n4. Smoke test the candidate.\n5. Promote it to `frb-tart-base`.\n\n### Start Local Registry\n\nUse a large disk for registry storage:\n\n```bash\nmkdir -p /Volumes/MyExternal/temp/frb-mobile-registry\n\ndocker run -d --name frb-mobile-registry \\\n  -p 127.0.0.1:5000:5000 \\\n  -v /Volumes/MyExternal/temp/frb-mobile-registry:/var/lib/registry \\\n  registry:2\n```\n\nVerify it:\n\n```bash\nNO_PROXY=127.0.0.1,localhost,::1 no_proxy=127.0.0.1,localhost,::1 \\\n  curl -fsS http://127.0.0.1:5000/v2/\n```\n\nUse `127.0.0.1`, not `localhost`, to avoid IPv6 or host service surprises.\n\n### Mirror OCI Artifact\n\nMirror with `crane`. If a proxy is needed, set it explicitly for this command:\n\n```bash\nPROXY_URL=http://localhost:7897\n\nHTTP_PROXY=$PROXY_URL HTTPS_PROXY=$PROXY_URL \\\nhttp_proxy=$PROXY_URL https_proxy=$PROXY_URL \\\nALL_PROXY=$PROXY_URL all_proxy=$PROXY_URL \\\nNO_PROXY=localhost,127.0.0.1,::1 no_proxy=localhost,127.0.0.1,::1 \\\n  crane --insecure copy --jobs 4 \\\n  ghcr.io/cirruslabs/macos-sonoma-xcode:16.1 \\\n  127.0.0.1:5000/cirruslabs/macos-sonoma-xcode:16.1\n```\n\nFor long mirrors, run it in `screen` and write logs to disk:\n\n```bash\nPROXY_URL=http://localhost:7897\n\nscreen -dmS frb-mobile-crane-copy zsh -lc \"HTTP_PROXY=$PROXY_URL HTTPS_PROXY=$PROXY_URL http_proxy=$PROXY_URL https_proxy=$PROXY_URL ALL_PROXY=$PROXY_URL all_proxy=$PROXY_URL NO_PROXY=localhost,127.0.0.1,::1 no_proxy=localhost,127.0.0.1,::1 crane --insecure copy --jobs 4 ghcr.io/cirruslabs/macos-sonoma-xcode:16.1 127.0.0.1:5000/cirruslabs/macos-sonoma-xcode:16.1 >> /Volumes/MyExternal/temp/frb-mobile-crane-copy.log 2>&1\"\n```\n\nCheck that `crane` only connects to the intended local proxy, if any, and local registry:\n\n```bash\nPROXY_PORT=7897\n\nlsof -nP -p <crane-pid> | rg \"TCP|127\\\\.0\\\\.0\\\\.1|:${PROXY_PORT}|:5000|ghcr|github\"\n```\n\nExpected network shape:\n\n- `127.0.0.1:<ephemeral> -> 127.0.0.1:<selected-proxy-port>` for remote GHCR fetches through the selected proxy.\n- `127.0.0.1:<ephemeral> -> 127.0.0.1:5000` for local registry writes.\n- No other proxy ports unless they were intentionally selected.\n\n### Verify Local Registry Digest\n\nUse an empty Docker config if the host credential helper interferes:\n\n```bash\nmkdir -p /private/tmp/frb-empty-docker-config\n\nDOCKER_CONFIG=/private/tmp/frb-empty-docker-config \\\nNO_PROXY=localhost,127.0.0.1,::1 no_proxy=localhost,127.0.0.1,::1 \\\n  crane --insecure digest 127.0.0.1:5000/cirruslabs/macos-sonoma-xcode:16.1\n```\n\nKnown digest for the current source image:\n\n```text\nsha256:f181b76eede9acd7a6db76438a4cf73e76550c3f9e8104aed16347122e132184\n```\n\n## Build Base VM With Packer\n\nBuild a candidate base VM from the local registry:\n\n```bash\ncd tools/tart_macos/packer\npacker init .\npacker build \\\n  -var 'source_vm=127.0.0.1:5000/cirruslabs/macos-sonoma-xcode:16.1' \\\n  -var 'target_vm=frb-tart-base-candidate' \\\n  -var 'flutter_version=3.47.0' \\\n  -var 'host_proxy_url=http://192.168.64.1:7897' \\\n  -var 'allow_insecure=true' \\\n  .\n```\n\nThe Packer template clones the raw source VM, boots it as `admin/admin`, runs `scripts/provision-frb-tart-base.sh`, and leaves a stopped local VM named by `target_vm`. The current provision step initializes Xcode, checks out the requested Flutter SDK version, installs CocoaPods with Homebrew, installs Rust matching the devcontainer, adds the `aarch64-apple-ios-sim` and `x86_64-apple-ios` Rust targets for both the pinned toolchain and the `stable-aarch64-apple-darwin` toolchain used by CargoKit, and pre-caches Flutter iOS and macOS artifacts. If `host_proxy_url` is set, the provision step receives standard `HTTP_PROXY` / `HTTPS_PROXY` / `ALL_PROXY` / `CARGO_HTTP_PROXY` environment variables. It also verifies the image contains Xcode and simulator tooling.\n\nKeep `flutter_version` in sync with `FRB_MAIN_FLUTTER_VERSION` in `.github/workflows/ci.yaml` and the Flutter version in `.devcontainer/Dockerfile`. Do not rely on the source Tart OCI image's preinstalled Flutter version, because Flutter template drift can change generated scaffold files.\n\nVerify the candidate:\n\n```bash\ntart list\ntart get frb-tart-base-candidate --format json\n```\n\nDo not boot and mutate `frb-tart-base` during verification. If you need to inspect runtime state, clone a disposable probe VM from the candidate:\n\n```bash\ntart clone frb-tart-base-candidate frb-tart-probe\ntart run --no-graphics frb-tart-probe\ntart ip frb-tart-probe --wait 180\ntart exec frb-tart-probe /bin/zsh -lc 'sw_vers && xcodebuild -version && flutter --version && flutter --version --machine | python3 -c '\"'\"'import json,sys; assert json.load(sys.stdin)[\"frameworkVersion\"] == \"3.47.0\"'\"'\"' && pod --version && rustc --version && cargo --version && rustup target list --installed | grep aarch64-apple-ios-sim && rustup target list --installed | grep x86_64-apple-ios && rustup target list --toolchain stable-aarch64-apple-darwin --installed | grep aarch64-apple-ios-sim && rustup target list --toolchain stable-aarch64-apple-darwin --installed | grep x86_64-apple-ios'\ntart stop frb-tart-probe\ntart delete frb-tart-probe\n```\n\nAfter the candidate passes smoke, promote it by moving the old base out of the way and renaming the candidate:\n\n```bash\nstamp=$(date +%Y%m%d-%H%M%S)\ntart rename frb-tart-base \"frb-tart-base-before-packer-$stamp\"\ntart rename frb-tart-base-candidate frb-tart-base\n```\n\nIf there is no existing `frb-tart-base`, only the second rename is needed.\n\n## Cost Notes\n\nLocal VM-to-VM cloning is cheap because Tart uses APFS copy-on-write for local clones. `du` may count shared blocks and look large, so prefer `df` available space when measuring real disk pressure.\n\nEven though local clone is cheap, keep a conservative free-space threshold before creating worktree VMs. `frb-dev-env tart create` requires at least 150GB free by default because base image setup, caches, build products, and simulator state can grow quickly.\n","isInternal":false,"tokens":2666,"sizeBytes":9404},{"name":"README.md","path":"tools/vmware_windows/README.md","rawUrl":"https://raw.githubusercontent.com/fzyzcjy/flutter_rust_bridge/HEAD/tools/vmware_windows/README.md","title":"vmware_windows Documentation","category":"plugin-manifest","format":"markdown","content":"# VMware Windows local development environment\n\nThis directory contains the VMware Fusion + Vagrant + Packer workflow for local Windows 11 Arm\nvalidation of `flutter_rust_bridge`.\n\nUse this path only for Windows desktop validation. Keep using Docker for Linux/headless checks, Tart\nfor macOS/iOS checks, and the host Android Emulator workflow for Android runtime checks.\n\nConfigure a local storage root before creating or building VMs. The helper reads:\n\n1. CLI flags, where available.\n2. Environment variables such as `FRB_WINDOWS_VM_ROOT`.\n3. The user config file, defaulting to `~/.config/flutter_rust_bridge/vmware_windows.json`.\n\nExample config:\n\n```json\n{\n  \"vm_root\": \"/path/to/frb-windows-vmware\",\n  \"host_proxy_url\": \"http://localhost:PORT\",\n  \"iso_path\": \"/path/to/frb-windows-vmware/downloads/iso/Windows11_Arm64.iso\",\n  \"iso_sha256\": \"sha256:<digest>\",\n  \"vm_gui\": true\n}\n```\n\nDo not store long-lived Windows VM bundles or boxes on the internal disk. When the storage root is\nunder `/Volumes/<name>/`, the helper refuses\nto create directories unless that volume is actually mounted, preventing accidental writes to the\ninternal disk if an external drive is absent.\n\nIf host-side Windows VM downloads and Packer network operations need a proxy, set:\n\n```bash\nexport FRB_WINDOWS_HOST_PROXY_URL=http://localhost:PORT\n```\n\nLeave `host_proxy_url` unset when direct network access is preferred.\n\n## Model\n\nThe reusable base is built by Packer from a Windows 11 Arm ISO and then consumed by Vagrant on top\nof VMware Fusion:\n\n```text\nWindows 11 Arm ISO\n  -> packer build ... frb-windows11-arm64.box\n  -> vagrant box add frb/windows11-arm64 <box>\n  -> vagrant up per-worktree VM under the configured VM root\n```\n\nThe base box is immutable after promotion. Do not manually install Flutter, Rust, Visual Studio\nBuild Tools, or other dependencies into the promoted base and then forget to update Packer. Manual\nexperiments are allowed only as discovery; every durable base change must be reflected in the Packer\ntemplate or provision scripts.\n\nUse `tools/manual_tests/frb-local-vmware-windows-dev-env.md` for audit-ready local validation and\nexecution records.\n\n## Quick Start\n\n```bash\ntools/vmware_windows/frb_vmware_windows_env.py init-root\ntools/vmware_windows/frb_vmware_windows_env.py check-host\ntools/vmware_windows/frb_vmware_windows_env.py self-test\ntools/vmware_windows/frb_vmware_windows_env.py packer-init\ntools/vmware_windows/frb_vmware_windows_env.py prepare-iso \\\n  --iso /path/to/frb-windows-vmware/downloads/iso/Windows11_Arm64.iso\ntools/vmware_windows/frb_vmware_windows_env.py packer-build\ntools/vmware_windows/frb_vmware_windows_env.py vagrant-box-add \\\n  --box-file /path/to/frb-windows-vmware/packer/boxes/frb-windows11-arm64.box\ntools/vmware_windows/frb_vmware_windows_env.py start\ntools/vmware_windows/frb_vmware_windows_env.py upload\ntools/vmware_windows/frb_vmware_windows_env.py test-flutter-windows --package frb_example/flutter_via_create\n```\n\nFor the initial base-image build on a developer Mac, prefer the Terminal wrapper:\n\n```bash\ntools/vmware_windows/run_packer_build.zsh \\\n  --iso /path/to/frb-windows-vmware/downloads/iso/Windows11_Arm64.iso \\\n  --add-box\n```\n\nThe wrapper uses the same helper, applies the configured host proxy when present, writes a full log\nunder the configured VM root, shows the VMware console by default, and stops the active build if `/`\nor the VM storage root drops below 100GB free.\nWhen a host proxy is configured, it also adds the Packer WinRM guest host to `NO_PROXY` so local VM\ncontrol traffic does not get sent through the host download proxy.\n\nBefore starting the long VMware build, the wrapper checks that the ISO exists, `hdiutil`, `xorriso`,\nPacker, Vagrant, and VMware `vmrun` are available, and the Packer output directory is not already\noccupied. Use `--preflight-only` to run only those wrapper checks without remastering an ISO,\nstarting Packer, or booting VMware.\n\n## Host Installation\n\n`tools/vmware_windows/frb_vmware_windows_env.py check-host` verifies VMware `vmrun`, Vagrant, and\nPacker. The helper itself uses only the Python standard library, so it does not need `uv`, `pip`, or\na first-run Python package download. Packer can be installed with Homebrew. VMware Fusion Pro may\nrequire an authenticated Broadcom/VMware download, and Vagrant's macOS pkg may require an\ninteractive sudo password. Install those tools through their normal macOS flows when non-interactive\nHomebrew installation is blocked.\n\nIf your network requires a proxy for host-side downloads, configure it explicitly before preparing\nthis VM workflow:\n\n```bash\nexport HTTP_PROXY=http://localhost:PORT\nexport HTTPS_PROXY=http://localhost:PORT\nexport http_proxy=http://localhost:PORT\nexport https_proxy=http://localhost:PORT\n```\n\nIf using Homebrew, keep auto-update disabled:\n\n```bash\nHOMEBREW_NO_AUTO_UPDATE=1 brew install --cask vmware-fusion\nHOMEBREW_NO_AUTO_UPDATE=1 brew install --cask vagrant\nHOMEBREW_NO_AUTO_UPDATE=1 brew install --cask vagrant-vmware-utility\nHOMEBREW_NO_AUTO_UPDATE=1 brew install packer\nHOMEBREW_NO_AUTO_UPDATE=1 brew install xorriso\nvagrant plugin install vagrant-vmware-desktop\n```\n\nIn practice, these host installers can require human action:\n\n- `vmware-fusion` may be disabled in Homebrew because Broadcom/VMware requires an authenticated\n  download. Download VMware Fusion Pro manually from the VMware/Broadcom portal, install `VMware\n  Fusion.app`, then verify `/Applications/VMware Fusion.app/Contents/Library/vmrun` exists.\n- `vagrant` is a macOS `.pkg` cask. A non-interactive agent shell can download it, but\n  installation may fail at `/usr/sbin/installer` because `sudo` needs an administrator password.\n  Install it manually with the macOS installer, or run the Homebrew cask command yourself from an\n  interactive terminal.\n- `vagrant-vmware-utility` is also a macOS `.pkg` cask and can require the same interactive\n  administrator installation path.\n- `packer` can be installed non-interactively with Homebrew and should appear as\n  `/opt/homebrew/bin/packer`.\n- `xorriso` is required to remaster the Windows ISO so `Autounattend.xml` is available from the\n  root of the boot media. Install it with `HOMEBREW_NO_AUTO_UPDATE=1 brew install xorriso`.\n- Vagrant's VMware provider requires both the Vagrant VMware Utility and the\n  `vagrant-vmware-desktop` plugin. Install the utility with\n  `brew install --cask vagrant-vmware-utility`, then install the plugin with Vagrant.\n- VMware Fusion 26 provides the Arm64 Windows drivers used during Packer setup at\n  `/Applications/VMware Fusion.app/Contents/Library/isoimages/arm64/drivers-arm64.zip`. The helper\n  injects those drivers into the remastered ISO and installs `vmxnet3.inf` during Windows setup so\n  the guest can obtain a NAT address before Packer waits for WinRM. Set\n  `FRB_VMWARE_FUSION_ARM64_DRIVERS_ZIP` only if Fusion stores the archive somewhere else.\n\nAfter installing VMware Fusion and Vagrant, install the VMware Vagrant provider utility and plugin:\n\n```bash\nHOMEBREW_NO_AUTO_UPDATE=1 brew install --cask vagrant-vmware-utility\nvagrant plugin install vagrant-vmware-desktop\n```\n\nThen re-run:\n\n```bash\ntools/vmware_windows/frb_vmware_windows_env.py check-host\n```\n\nExpected result:\n\n```json\n{\n  \"missing\": [],\n  \"tools\": {\n    \"packer\": \"/opt/homebrew/bin/packer\",\n    \"vagrant\": \"/opt/vagrant/bin/vagrant\",\n    \"vmrun\": \"/Applications/VMware Fusion.app/Contents/Library/vmrun\"\n  }\n}\n```\n\nValidate the host without starting a VM:\n\n```bash\ntools/vmware_windows/frb_vmware_windows_env.py check-host\ntools/vmware_windows/frb_vmware_windows_env.py self-test\ntools/vmware_windows/validate_offline.zsh\n```\n\n`self-test` is a dependency-free helper check. It does not start VMware, Vagrant, Packer, or touch\nthe external VM root; it only validates local helper logic such as path handling, checksum sidecars,\nand guest command argument normalization.\n\n`validate_offline.zsh` is the local no-VM regression check for this directory. It checks Python\nsyntax and helper self-tests, the Packer build wrapper syntax, Vagrantfile syntax when Ruby is\navailable, Packer formatting when Packer is available, website sidebar JavaScript syntax when Node\nis available, and whitespace errors.\n\n## Windows ISO\n\nDownload the Windows 11 Arm ISO from Microsoft's official Arm64 download page:\n\n```text\nhttps://www.microsoft.com/software-download/windows11arm64\n```\n\nThe page generates ISO links dynamically and those links expire. Choose `Windows 11 (multi-edition\nISO for Arm64)`, then choose the language you want to install. For the default English base VM,\nchoose `English`.\n\nPlace the ISO under:\n\n```text\n/path/to/frb-windows-vmware/downloads/iso/Windows11_Arm64.iso\n```\n\nSet `FRB_WINDOWS_ISO` if using another filename. Do not commit ISO files or generated boxes. If\n`Windows11_Arm64.iso` is absent and exactly one non-`autounattend` `.iso` exists in the ISO\ndirectory, the helper and wrapper use that source ISO automatically.\n\nProvide a SHA-256 checksum before a real Packer build. Either set:\n\n```bash\nexport FRB_WINDOWS_ISO_CHECKSUM=sha256:<digest>\n```\n\nor place the digest in:\n\n```text\n/path/to/frb-windows-vmware/downloads/iso/Windows11_Arm64.iso.sha256\n```\n\nFor the current Microsoft Windows 11 Arm64 English ISO, the official verify table lists this\nSHA-256:\n\n```text\n638AA2C88E94385B00F4F178D071E3DF0B7D9E335577A83BD533B7F2EB65ADF0\n```\n\nWrite the checksum file as either the bare digest or `sha256:<digest>`:\n\n```bash\nprintf '%s\\n' '638AA2C88E94385B00F4F178D071E3DF0B7D9E335577A83BD533B7F2EB65ADF0' \\\n  > /path/to/frb-windows-vmware/downloads/iso/Windows11_Arm64.iso.sha256\n```\n\n`packer-build --validate` uses a placeholder checksum only to validate template structure. A real\n`packer-build` fails fast until a checksum is provided.\n\n## Build The Base Box With Packer\n\nInitialize and build from the checked-in template:\n\n```bash\ntools/vmware_windows/frb_vmware_windows_env.py packer-init\ntools/vmware_windows/frb_vmware_windows_env.py prepare-iso\ntools/vmware_windows/frb_vmware_windows_env.py packer-build\n```\n\nThe helper sets `PACKER_CACHE_DIR` under the external root and passes explicit variables for the\nISO, checksum, generated unattended-install file, output directory, and provision scripts. By\ndefault, `packer-build` first remasters the source Windows ISO with `hdiutil` and `xorriso` so\n`Autounattend.xml` is present at the root of a UEFI-bootable install ISO; this matches Windows\nSetup's most reliable unattended-install discovery path under VMware Fusion on Apple Silicon. The\ntemplate also exposes `Autounattend.xml` through both Packer floppy media and a secondary\n`AUTOUNATTEND` CD as a fallback. The template provisions guest command access, OpenSSH, PowerShell\ntooling, Rust, Flutter, CMake/Ninja, and Visual Studio Build Tools.\n\nPacker connects to the freshly installed guest over VMware Fusion NAT WinRM. The helper passes\n`FRB_WINDOWS_PACKER_WINRM_HOST` to the template, defaulting to `172.16.0.128`, which is the first\nlease observed for the generated Packer VM on VMware Fusion NAT. Override this value if another VM\nalready occupies that address or if your Fusion NAT subnet assigns a different first lease:\n\n```bash\nexport FRB_WINDOWS_PACKER_WINRM_HOST=172.16.0.128\n```\n\nWhen a host proxy is configured for downloads, the helper and wrapper add this WinRM host to\n`NO_PROXY` and `no_proxy`. Without that bypass, Packer's WinRM HTTP requests can be routed through\nthe proxy and fail with `http response error: 502 - invalid content type`.\n\nThe remastered ISO is stored next to the source ISO:\n\n```text\n/path/to/frb-windows-vmware/downloads/iso/<source>.frb-autounattend-uefi.iso\n```\n\nUse `prepare-iso --force` only when intentionally regenerating the remastered ISO after changing\n`Autounattend.xml` generation. Use `packer-build --no-remaster-iso` only for low-level\ntroubleshooting of the original Microsoft ISO plus Packer's separate autounattend media.\n\nPacker requires the output directory to be absent before a build starts. The helper removes an empty\nstale output directory automatically, but it stops if that directory contains files so an existing\nbox is not overwritten accidentally.\n\nFor unattended first-run builds from an ordinary macOS Terminal, use:\n\n```bash\ntools/vmware_windows/run_packer_build.zsh \\\n  --iso /path/to/frb-windows-vmware/downloads/iso/Windows11_Arm64.iso \\\n  --add-box\n```\n\nUseful variants:\n\n```bash\ntools/vmware_windows/run_packer_build.zsh --iso /path/to/Windows11_Arm64.iso\ntools/vmware_windows/run_packer_build.zsh --iso /path/to/Windows11_Arm64.iso --preflight-only\ntools/vmware_windows/run_packer_build.zsh --iso /path/to/Windows11_Arm64.iso --prepare-only\ntools/vmware_windows/run_packer_build.zsh --iso /path/to/Windows11_Arm64.iso --headless\ntools/vmware_windows/run_packer_build.zsh --iso /path/to/Windows11_Arm64.iso --no-force-prepare-iso\ntools/vmware_windows/run_packer_build.zsh --iso /path/to/Windows11_Arm64.iso --add-box --force-box\n```\n\nThe wrapper is intentionally thin: it calls `init-root`, `check-host`, `packer-init`, `prepare-iso`,\n`packer-build`, and optionally `vagrant-box-add`. It exists to keep the proxy, log path, external\nstorage root, preflight checks, and disk-space guard consistent during the long VMware build. Use\n`--preflight-only` to validate host readiness without starting the build; use `--prepare-only` to\nvalidate ISO remastering before starting VMware.\n\nIf the first unattended install fails, keep the full Packer log under `logs/`, record the exact\nfailure in the execution artifact, fix the template or scripts, and rerun. Do not fix the base by\nbooting it and manually installing tools without updating the source files.\n\nAfter Packer produces a box, import it with:\n\n```bash\ntools/vmware_windows/frb_vmware_windows_env.py vagrant-box-add \\\n  --box-file /path/to/frb-windows-vmware/packer/boxes/frb-windows11-arm64.box\n```\n\nThe default box name is `frb/windows11-arm64`. Override with `FRB_WINDOWS_VAGRANT_BOX` only when\nintentionally testing another base.\n\nThe base VM installs Flutter 3.44.1, Dart 3.12.1, Rust stable, Git, CMake, Ninja, and Visual Studio\nBuild Tools with the C++ workload. Keep this list synchronized with\n`provision/install-frb-toolchain.ps1` when the repository raises its Windows toolchain floor.\n\n## Daily Use\n\nRun from the repository root:\n\n```bash\ntools/vmware_windows/frb_vmware_windows_env.py info\ntools/vmware_windows/frb_vmware_windows_env.py start\ntools/vmware_windows/frb_vmware_windows_env.py upload\ntools/vmware_windows/frb_vmware_windows_env.py exec -- powershell -NoProfile \\\n  -Command \"cd C:\\frb\\worktree; flutter --version\"\ntools/vmware_windows/frb_vmware_windows_env.py stop\n```\n\nThe Vagrantfile starts VMware Fusion with a visible GUI by default because VMware Fusion 26 on Apple\nSilicon can power off this Windows Arm guest shortly after boot in `nogui` mode. Set\n`FRB_WINDOWS_VM_GUI=false` only when explicitly revalidating headless startup on a newer Fusion or\nprovider combination.\n\n`upload` creates a zip archive of the host checkout, uploads it through Vagrant's WinRM\ncommunicator, and expands it into a VM-local directory before heavy builds. Do not compile from a\nVMware shared folder because shared folders are slower for many small files, can distort build\nbehavior, and are sensitive to Vagrant VMware provider compatibility.\n\nAfter the VM is running and code is uploaded:\n\n```bash\ntools/vmware_windows/frb_vmware_windows_env.py test-flutter-windows \\\n  --package frb_example/flutter_via_create\n```\n\n## Credentials\n\nDo not put guest passwords in the repository. Use one of:\n\n```text\nFRB_WINDOWS_VM_USERNAME\nFRB_WINDOWS_VM_PASSWORD\n~/.secrets/FRB_WINDOWS_VM_PASSWORD\n```\n\nWhen a password is required, source it in the shell or pass it through the environment. Do not write\nit into command logs.\n\nPassword resolution order is `FRB_WINDOWS_VM_PASSWORD`, then `~/.secrets/FRB_WINDOWS_VM_PASSWORD`,\nthen the helper-generated fallback file.\n\nIf `FRB_WINDOWS_VM_PASSWORD` is unset, the helper generates a local per-root fallback password at:\n\n```text\n/path/to/frb-windows-vmware/credentials/guest-password.txt\n```\n\nTreat that file as local machine state. It is not part of the repository, and the helper passes it\nto both Packer and Vagrant through environment variables.\n\n## Cleanup\n\nStop the per-worktree VM when it is not needed:\n\n```bash\ntools/vmware_windows/frb_vmware_windows_env.py stop\n```\n\nDelete disposable per-worktree VMs only when intentionally reclaiming space:\n\n```bash\ntools/vmware_windows/frb_vmware_windows_env.py destroy\n```\n\nKeep the Packer-built base box and external caches unless rebuilding the Windows base.\n\n## Layout\n\n```text\ntools/vmware_windows/\n├── Vagrantfile\n├── frb_vmware_windows_env.py\n├── run_packer_build.zsh\n├── packer/\n│   ├── windows11-arm64.pkr.hcl\n└── provision/\n    ├── expand-uploaded-worktree.ps1\n    ├── install-frb-toolchain.ps1\n    └── prepare-guest-control.ps1\n```\n\nThe checked-in scripts are source of truth. Manual changes inside a Windows base VM should be\ntreated as experiments and then translated back into Packer or provision scripts.\n\n## Troubleshooting\n\n### VMware Cancels VM Startup\n\nIf `packer-build` reaches `Powering on virtual machine...` and then fails with:\n\n```text\nerror starting virtual machine: ... Error: The operation was canceled\n```\n\nopen VMware Fusion once from `/Applications`, finish any first-run prompts, accept the license, and\napprove any macOS privacy, network, or helper permissions it requests. Then verify the command-line\nbridge still works:\n\n```bash\n/Applications/VMware\\ Fusion.app/Contents/Library/vmrun -T fusion list\nlaunchctl print system/com.vagrant.vagrant-vmware-utility\n```\n\nAfter those commands work without permission dialogs, rerun:\n\n```bash\ntools/vmware_windows/frb_vmware_windows_env.py packer-build \\\n  --iso /path/to/frb-windows-vmware/downloads/iso/Windows11_Arm64.iso\n```\n\nIf the VM still cancels immediately in headless mode, run once with `--gui` so VMware Fusion can\nshow any remaining prompt:\n\n```bash\ntools/vmware_windows/frb_vmware_windows_env.py packer-build --gui \\\n  --iso /path/to/frb-windows-vmware/downloads/iso/Windows11_Arm64.iso\n```\n\nIf VMware Fusion reports that the `lsilogic` device type for `scsi0` is not supported, keep the\nPacker template on the checked-in NVMe disk adapter and `pvscsi` VMX override. To preserve the\nfailed VM directory and inspect `vmware.log`, rerun with:\n\n```bash\ntools/vmware_windows/frb_vmware_windows_env.py packer-build --gui --on-error=abort \\\n  --iso /path/to/frb-windows-vmware/downloads/iso/Windows11_Arm64.iso\n```\n\nIf the VM reaches UEFI Boot Manager instead of Windows Setup, the Packer boot command must select\nthe SATA CDROM entry and send boot keys during the Windows ISO's short prompt window before WinRM\ncan ever become available. Keep the checked-in boot command unless a future VMware Fusion release\nchanges the Boot Manager entry order.\n","isInternal":false,"tokens":4672,"sizeBytes":18957}],"systemPromptSnippet":"<yakaai_skills repo=\"fzyzcjy/flutter_rust_bridge\">\n<!-- File: AGENTS.md (Tokens: ~4 | Category: root-instruction) -->\nCLAUDE.md\n\n<!-- File: CLAUDE.md (Tokens: ~309 | Category: claude-rule) -->\n# flutter_rust_bridge Development Rules\n\n## Rules\n\n### Do not manually edit these auto-generated files\n\n| File Pattern | Description |\n|--------------|-------------|\n| `frb_generated.*` | Generated by `frb_codegen` - modify generator logic instead |\n| `*.freezed.dart` | Freezed output |\n| `*.g.dart` | Code generator output |\n\n### PR size and complexity\n\n- When humans describe a PR as large or complex, generated code is excluded.\n- Keep implementations as simple as possible while preserving correctness.\n\n### Feature flags\n\nWhen adding or testing feature flags, read `frb-feature-flag`.\n\n## Skills\n\n- `frb-code-generation` - Which generation commands to run\n- `frb-develop-feature` - New feature development workflow\n- `frb-feature-flag` - Feature flag API surface and enabled/disabled coverage rules\n- `frb-test` - How to run tests\n- `frb-debugging` - Debug code generation issues\n- `frb-lint` - Lint and format checks\n- `frb-manual-test` - Manual regression test reports\n- `frb-prepare-pr` - PR preparation\n- `frb-fix-ci` - CI fixes\n- `frb-fix-main-ci` - Default-branch CI regression triage\n- `frb-docker` - Ordinary Docker/devcontainer usage and local validation\n- `frb-upgrade-docker` - Dev Docker toolchain upgrades, candidate images, and stable publication\n\n<!-- File: .claude/skills/frb-add-contributor/SKILL.md (Tokens: ~1469 | Category: anthropic-skill) -->\n---\nname: frb-add-contributor\ndescription: Add or reconcile flutter_rust_bridge contributors through all-contributors PRs. Use when finding contributor PRs, posting all-contributors comments, validating contributor metadata, resolving contributor PR merge conflicts, or merging the generated contributor PRs.\n---\n\n# FRB Add Contributor\n\nUse this skill when contributors need to be added to `flutter_rust_bridge` via `all-contributors`, especially when multiple recent PR authors should be credited in batch.\n\n## Step 1: Find the correct source PRs\n\nFor each GitHub username, find their PRs in `fzyzcjy/flutter_rust_bridge`.\n\n```bash\ngh search prs --repo fzyzcjy/flutter_rust_bridge --author <username> --limit 100 --json number,title,author,url,state\n```\n\nImportant:\n\n- Deduplicate by contributor. A single person may have multiple merged PRs.\n- If the task is about `new contributor PR`s, use the contributor's first PR only.\n- For release reconciliation, inspect every third-party human-authored PR in the same release range used by `frb-write-changelog`.\n- For each such PR, confirm that the contributor's human-written `.all-contributors-custom.yaml` summary covers that contribution. Being present in `.all-contributorsrc` or `README.md` is not sufficient.\n- Treat an existing contributor whose summary does not cover a release-range PR as needing reconciliation, but do not expect a new all-contributors PR for that person.\n- If the contributor already exists in `.all-contributorsrc` or `README.md`, do not expect a new all-contributors PR to be created.\n\n## Step 2: Stop for human confirmation\n\nAfter determining which contributors may need to be added or have their custom descriptions extended, stop and ask a human to confirm the contributor list and source PRs before taking any mutating action.\n\nThis stop is mandatory.\n\n- Do not edit `.all-contributors-custom.yaml`.\n- Do not regenerate contributor artifacts.\n- Do not post all-contributors comments.\n- Do not create or merge any PRs.\n- Do not perform any other GitHub mutation.\n\nOnly continue after the human explicitly confirms which contributors to add or update and which source PRs those changes cover.\n\n## Step 3: Prepare `.all-contributors-custom.yaml` and stop for human input\n\nBefore regenerating contributor artifacts, append each new contributor to the end of `.all-contributors-custom.yaml`:\n\n```yaml\n- <username>: TODO\n```\n\nDo this only for contributors who are not already present in `.all-contributors-custom.yaml`.\n\nFor an existing credited contributor with no custom entry, append:\n\n```yaml\n- <username>: TODO(#<pr-number>)\n```\n\nFor an existing contributor whose summary does not cover a release-range PR, preserve the existing human-written text and append a marker for each uncovered PR:\n\n```yaml\n- <username>: <existing human-written summary> TODO(#<pr-number>)\n```\n\nThen stop and ask a human to replace each `TODO` or `TODO(#<pr-number>)` with a concise contribution summary.\n\nThis stop is mandatory.\n\n- Do not continue to Step 3 or any later step in this skill until the human has replaced every `TODO`.\n- Do not run contributor generation.\n- Do not post all-contributors comments.\n- Do not resolve or merge contributor PRs.\n\nOnly resume after the human-written descriptions are present in `.all-contributors-custom.yaml`.\n\n## Step 4: Trigger all-contributors\n\nPost the comment on the chosen source PR:\n\n```bash\ngh pr comment <pr-number> --repo fzyzcjy/flutter_rust_bridge --body '@all-contributors please add <username> for code'\n```\n\nThen inspect the bot response on that PR.\n\n- Skip this step for an existing contributor whose only gap was an incomplete custom description.\n- If the bot says the user already contributed before, stop there for that user.\n- Otherwise, wait for the bot to open a PR named `docs: add <username> as a contributor for code`.\n\n## Step 5: Validate contributor metadata\n\nWhen a contributor PR exists, check the generated contributor data before merging.\n\nFiles to inspect:\n\n- `.all-contributorsrc`\n- `README.md`\n\nRequired validation:\n\n- Homepage URLs must use `https://`, never `http://`.\n- If the generated homepage is `http://`, change it to `https://` if the site supports it.\n- If the personal site does not support `https://`, prefer the contributor's GitHub profile URL instead of keeping `http://`.\n\n## Step 6: Regenerate contributor output\n\nAfter the human has replaced all `TODO` markers, confirm that the custom summaries cover every third-party release-range PR. Then regenerate the contributor artifacts instead of hand-editing `README.md`.\n\nUse:\n\n```bash\n./frb_internal generate-internal-contributor\n```\n\nThis command updates:\n\n- `.all-contributorsrc`\n- `README.md` via `all-contributors generate`\n- `website/docs/index.md` via `generate-internal-readme`\n\nIf you only need to refresh the website copy of the README, use:\n\n```bash\n./frb_internal generate-internal-readme\n```\n\n## Step 7: Resolve merge conflicts in contributor PRs\n\nThese PRs usually conflict only in:\n\n- `.all-contributorsrc`\n- `README.md`\n\nRecommended workflow for each open contributor PR:\n\n```bash\ngh pr checkout <pr-number> --repo fzyzcjy/flutter_rust_bridge\ngit fetch origin master\ngit merge --no-ff origin/master\n```\n\nIf the merge conflicts:\n\n1. Keep the latest `origin/master` content.\n2. Re-apply the contributor entry from the bot branch.\n3. Ensure the contributor card remains present in `README.md`.\n4. Re-run `./frb_internal generate-internal-contributor` if you edited contributor metadata or finished filling in `.all-contributors-custom.yaml`.\n5. `git add` the resolved files and commit the merge.\n6. Push the contributor branch back to origin.\n\nDo not revert unrelated contributor entries that were merged by other PRs.\n\n## Step 8: Merge the contributor PRs\n\nAfter the branch is updated and GitHub reports it mergeable:\n\n```bash\ngh pr merge <pr-number> --repo fzyzcjy/flutter_rust_bridge --merge --delete-branch\n```\n\nNotes:\n\n- GitHub may temporarily report `DIRTY` or reject merge immediately after push.\n- If that happens, wait briefly, re-check the PR state, and retry the merge.\n- After merging one contributor PR, fetch `origin/master` again before resolving the next one.\n\n## Step 9: Final verification\n\nAt the end, verify there are no remaining open all-contributors PRs:\n\n```bash\ngh pr list --repo fzyzcjy/flutter_rust_bridge --search \"author:allcontributors[bot] is:open\" --limit 50 --json number,title,mergeStateStatus,url\n```\n\nAlso verify the credited contributors appear in:\n\n- `.all-contributorsrc`\n- `README.md`\n- `website/docs/index.md` if contributor regeneration was run\n\n<!-- File: .claude/skills/frb-android-emulator-prepare/SKILL.md (Tokens: ~1954 | Category: anthropic-skill) -->\n---\nname: frb-android-emulator-prepare\ndescription: Use when preparing, installing, diagnosing, or explaining the host Android Emulator environment for flutter_rust_bridge local runtime validation, including Android SDK command-line tools, emulator packages, AVD creation, host emulator startup, and Docker-local ADB connectivity to the emulator TCP endpoint.\n---\n\n# FRB Android Emulator Preparation\n\nUse this skill when a task requires a **host Android Emulator** for FRB local runtime validation.\n\n## Boundary\n\nFRB Android runtime validation currently supports a host Android Emulator path: host needs Java, Android SDK command-line tools, `emulator`, a system image, and an AVD; Docker runs FRB/Flutter commands through a Docker-local ADB server connected to the emulator TCP endpoint.\n\nDo not install Android Emulator packages unless the user has explicitly asked to prepare or run an emulator. Installing the emulator changes host state and downloads large SDK packages.\n\n## Default Host Locations\n\nUse standard Android Studio locations on macOS:\n\n```text\n~/Library/Android/sdk\n~/.android/avd\n~/.android\n```\n\nRespect `ANDROID_HOME` if it is already set. Otherwise use `~/Library/Android/sdk`. Do not invent repo-specific defaults such as `/workspace`, `/tmp`, or a user's personal artifacts directory.\n\n## Preflight\n\nOfficial references for this workflow:\n\n- Android Studio and command-line tools download page: https://developer.android.com/studio\n- `sdkmanager` command-line package manager: https://developer.android.com/tools/sdkmanager\n- `avdmanager` command-line AVD manager: https://developer.android.com/tools/avdmanager\n- Android Emulator command line: https://developer.android.com/studio/run/emulator-commandline\n- ADB command-line tool: https://developer.android.com/tools/adb\n\nRun these before installing anything:\n\n```bash\nsw_vers\nuname -m\nwhich adb || true\nadb version || true\necho \"${ANDROID_HOME:-}\"\nls -ld \"$HOME/Library/Android/sdk\" \"$HOME/.android\" \"$HOME/.android/avd\" 2>/dev/null || true\nwhich java || true\njava -version || true\n```\n\nIf the task requires an emulator, record whether these tools already exist:\n\n```bash\nls \"$HOME/Library/Android/sdk/cmdline-tools/latest/bin/sdkmanager\" 2>/dev/null || true\nls \"$HOME/Library/Android/sdk/cmdline-tools/latest/bin/avdmanager\" 2>/dev/null || true\nls \"$HOME/Library/Android/sdk/emulator/emulator\" 2>/dev/null || true\n```\n\n## Installation Policy\n\nBefore installing or downloading anything, state exactly what will be installed and ask for approval through the command escalation mechanism.\n\nFor a minimal macOS emulator setup, expect these host changes:\n\n```text\nHomebrew openjdk, if Java is missing\n~/Library/Android/sdk/cmdline-tools/latest\n~/Library/Android/sdk/emulator\n~/Library/Android/sdk/platform-tools\n~/Library/Android/sdk/platforms/android-<api>\n~/Library/Android/sdk/system-images/android-<api>/google_apis/arm64-v8a\n~/.android/avd/<avd-name>.avd\n~/.android/avd/<avd-name>.ini\n```\n\nPrefer ARM64 system images on Apple Silicon:\n\n```text\nsystem-images;android-35;google_apis;arm64-v8a\n```\n\nUse a different API level only when the task or Flutter version requires it.\n\n## Install Java\n\nIf `/usr/bin/java` is missing and Homebrew is available, prefer the Homebrew `openjdk` formula because it does not require the sudo package installer:\n\n```bash\nHOMEBREW_NO_AUTO_UPDATE=1 brew install openjdk\n```\n\nFor SDK commands in the current shell, prepend the Homebrew JDK:\n\n```bash\nexport PATH=\"/opt/homebrew/opt/openjdk/bin:$PATH\"\n```\n\nDo not use `brew install --cask temurin` in non-interactive agent runs unless the user has approved sudo-capable installation; it may invoke macOS Installer through `sudo`.\n\n## Install Command-Line Tools\n\nIf Android Studio is not installed and `cmdline-tools/latest` is missing, install the official Android command-line tools into the standard SDK root. This follows the Android Developers command-line tools instructions on the Android Studio download page: https://developer.android.com/studio. The important shape is to download the command-line tools package from that page, extract it, and place it under `cmdline-tools/latest` in the Android SDK root.\n\n```bash\nmkdir -p \"$HOME/Library/Android/sdk/cmdline-tools/latest\"\ncurl -L -o /private/tmp/commandlinetools-mac-latest.zip \\\n  https://dl.google.com/android/repository/commandlinetools-mac-14742923_latest.zip\nmkdir -p /private/tmp/frb-android-commandlinetools-extract\nditto -x -k /private/tmp/commandlinetools-mac-latest.zip /private/tmp/frb-android-commandlinetools-extract\ncp -R /private/tmp/frb-android-commandlinetools-extract/cmdline-tools/. \\\n  \"$HOME/Library/Android/sdk/cmdline-tools/latest/\"\n```\n\nThen set:\n\n```bash\nexport ANDROID_HOME=\"$HOME/Library/Android/sdk\"\nexport ANDROID_SDK_ROOT=\"$ANDROID_HOME\"\nexport PATH=\"/opt/homebrew/opt/openjdk/bin:$ANDROID_HOME/cmdline-tools/latest/bin:$ANDROID_HOME/platform-tools:$ANDROID_HOME/emulator:$PATH\"\n```\n\n## Install Emulator Packages\n\nAccept licenses and install the minimal packages with `sdkmanager`, the Android SDK package manager documented by Android Developers at https://developer.android.com/tools/sdkmanager:\n\n```bash\nyes | sdkmanager --licenses\nsdkmanager \\\n  \"emulator\" \\\n  \"platform-tools\" \\\n  \"platforms;android-35\" \\\n  \"system-images;android-35;google_apis;arm64-v8a\"\n```\n\nIf the user wants a smaller or non-Google image, choose a matching `system-images;android-<api>;default;arm64-v8a` package instead.\n\n## Create AVD\n\nUse a predictable AVD name that the `frb-dev-env` examples can reference. This uses `avdmanager`, the Android Developers command-line tool for creating and managing AVDs documented at https://developer.android.com/tools/avdmanager:\n\n```bash\navdmanager create avd \\\n  --name FRB_API_35 \\\n  --package \"system-images;android-35;google_apis;arm64-v8a\" \\\n  --device \"pixel_8\"\n```\n\nIf the device profile is unavailable, list device profiles and choose a common phone:\n\n```bash\navdmanager list device\n```\n\n## Start And Verify Emulator\n\nStart the host emulator through `frb-dev-env`. The underlying emulator invocation follows the Android Emulator command-line interface documented at https://developer.android.com/studio/run/emulator-commandline:\n\n```bash\n.claude/skills/frb-dev-env/frb_dev_env.py android emulator --avd FRB_API_35 --port 5554\n```\n\nVerify host ADB can see the emulator. This uses ADB, the Android Developers command-line tool documented at https://developer.android.com/tools/adb:\n\n```bash\nadb devices -l\n```\n\nFor emulator runtime tests, let Docker use its own ADB server and connect directly to the host emulator ADB TCP endpoint. For emulator console port `5554`, that endpoint is `host.docker.internal:5555`:\n\n```bash\n.claude/skills/frb-dev-env/frb_dev_env.py docker exec --android-emulator-adb -- adb devices -l\n```\n\nOnly after both ADB checks pass, run an FRB Android runtime command. Use the Docker-visible emulator serial:\n\n```bash\n.claude/skills/frb-dev-env/frb_dev_env.py docker exec --android-emulator-adb -- \\\n  bash -lc './frb_internal test-flutter-native --package frb_example--flutter_via_create --flutter-test-args \"--device-id host.docker.internal:5555\"'\n```\n\n## Common Failures\n\n- `Android SDK root does not exist`: install Android Studio or command-line tools into `~/Library/Android/sdk`, or set `ANDROID_HOME`.\n- `emulator: command not found`: install the SDK `emulator` package and ensure `~/Library/Android/sdk/emulator` is on `PATH`.\n- No AVD found: create `FRB_API_35` with `avdmanager create avd`, or pass the existing AVD name to `android emulator --avd`.\n- Docker cannot see the host emulator through `--android-emulator-adb`: verify the host emulator is running on console port `5554`, verify host `adb devices -l` shows `emulator-5554`, then run `adb connect host.docker.internal:5555` inside Docker or retry the helper command.\n- Flutter integration test fails with a VM service or DDS connection refused on `127.0.0.1`: the command likely used a host ADB server against an emulator. Use `--android-emulator-adb` so `adb forward` binds inside Docker.\n\n## Cleanup\n\nStop the emulator from host ADB:\n\n```bash\nadb -s emulator-5554 emu kill\n```\n\nAvoid deleting SDK packages or AVDs unless the user explicitly asks.\n\n<!-- File: .claude/skills/frb-cargokit-dev/SKILL.md (Tokens: ~1314 | Category: anthropic-skill) -->\n---\nname: frb-cargokit-dev\ndescription: Develop or update CargoKit for flutter_rust_bridge. Use when a bug or feature belongs in fzyzcjy/Cargokit, when updating the FRB CargoKit submodule pointers, or when preparing the paired CargoKit and FRB PRs that synchronize CargoKit changes into FRB integrate templates and examples.\n---\n\n# FRB CargoKit Development\n\n## Overview\n\nUse this skill when the actual fix belongs in the external `fzyzcjy/Cargokit` repository and FRB needs a follow-up PR that points its CargoKit submodules at the updated CargoKit branch or commit.\n\nThe usual shape is two PRs:\n\n1. A CargoKit PR with the real implementation.\n2. An FRB PR that updates the two CargoKit submodule pointers and regenerates copied integrate outputs.\n\nDo not implement CargoKit behavior only in copied FRB example output. The source of truth is the CargoKit repository referenced by the template submodules.\n\n## Source Locations\n\nFRB embeds CargoKit as git submodules in two template locations:\n\n- `frb_codegen/assets/integration_template/app/rust_builder/cargokit`\n- `frb_codegen/assets/integration_template/plugin/cargokit`\n\nBoth submodules should normally point at the same CargoKit branch or commit for the same logical update. Downstream generated copies may appear under:\n\n- `frb_example/**/cargokit/**`\n- `frb_example/**/rust_builder/cargokit/**`\n\nRead `frb-cargokit` first if you need to classify an existing diff before deciding where the change belongs.\n\n## Workflow\n\n### 1. Prepare The CargoKit Change\n\nWork in `fzyzcjy/Cargokit` for the actual CargoKit implementation.\n\n1. Create a CargoKit branch whose name is easy to reference from the FRB PR.\n2. Implement the fix in CargoKit and run the relevant CargoKit checks from that repository.\n3. Push the branch and open a CargoKit PR.\n4. Keep the branch alive until the FRB PR has passed CI.\n\nIf you are editing through an FRB submodule checkout, remember that commits belong to the nested CargoKit repository, not the outer FRB repository. Push the nested repository branch before updating the outer FRB submodule pointer.\n\n### 2. Update FRB To Point At The CargoKit Branch\n\nIn the FRB repository:\n\n1. Initialize submodules if needed:\n\n   ```bash\n   git submodule update --init --recursive\n   ```\n\n2. Fetch and check out the CargoKit branch or commit in both submodule checkouts:\n\n   ```bash\n   git -C frb_codegen/assets/integration_template/app/rust_builder/cargokit fetch origin <branch-or-ref>\n   git -C frb_codegen/assets/integration_template/app/rust_builder/cargokit checkout <commit-or-branch>\n   git -C frb_codegen/assets/integration_template/plugin/cargokit fetch origin <branch-or-ref>\n   git -C frb_codegen/assets/integration_template/plugin/cargokit checkout <commit-or-branch>\n   ```\n\n3. Stage the submodule pointer updates in the outer FRB repository:\n\n   ```bash\n   git add frb_codegen/assets/integration_template/app/rust_builder/cargokit\n   git add frb_codegen/assets/integration_template/plugin/cargokit\n   ```\n\n4. Confirm the outer FRB diff shows submodule pointer changes for both paths.\n\nUseful checks:\n\n```bash\ngit submodule status --recursive\ngit diff --submodule\n```\n\nDo not leave one template submodule on the old CargoKit commit unless the app and plugin templates intentionally need different CargoKit revisions.\n\n### 3. Regenerate FRB Integrate Outputs\n\nAfter changing either CargoKit submodule pointer, run:\n\n```bash\n./frb_internal precommit-integrate\n```\n\nThis updates copied CargoKit output and platform scaffold files under `frb_example/**`. Those downstream diffs are expected when CargoKit files are copied from the updated template.\n\nStage and commit the regenerated integrate output together with the submodule pointer update:\n\n```bash\ngit add frb_example frb_codegen/assets/integration_template\ngit commit -m \"Update CargoKit template pointers\"\n```\n\nThen run the normal PR checks from `frb-prepare-pr`, especially:\n\n```bash\n./frb_internal lint --fix\n```\n\nRun focused tests when the CargoKit change affects a platform that can be exercised locally. Otherwise let FRB CI cover the platform matrix and use `frb-fix-ci` for failures.\n\n### 4. Open The FRB PR\n\nThe FRB PR should mention that CargoKit is intentionally pointed at the temporary CargoKit PR branch or commit. Keep the PR body empty if the local PR workflow requires it, but use GitHub comments when you need to leave coordination notes.\n\nBefore asking for final review:\n\n1. Wait for the FRB PR CI to pass with the temporary CargoKit pointer.\n2. Ask reviewers to merge the CargoKit PR first.\n3. Update the FRB submodule pointers from the temporary branch commit to the merged CargoKit commit on the canonical branch.\n4. Rerun `./frb_internal precommit-integrate` if the CargoKit commit changed.\n5. Push the refreshed FRB PR and wait for CI again.\n6. Ask reviewers to merge the FRB PR.\n\n## CI Failure Triage\n\nWhen FRB CI fails after a CargoKit update, classify the failure before editing:\n\n- CargoKit behavior failure: fix it in `fzyzcjy/Cargokit`, then update both FRB submodule pointers.\n- Missing integrate refresh: run `./frb_internal precommit-integrate` and commit the copied output.\n- Lint or formatting drift: run `./frb_internal lint --fix`.\n- Platform-specific FRB scaffold issue: fix the FRB integration template, then run `./frb_internal precommit-integrate`.\n\nDo not patch copied `frb_example/**/cargokit/**` files directly as the final fix. They should be produced from the updated CargoKit submodule and integrate templates.\n\n## Related Skills\n\n- `frb-cargokit` - Understand CargoKit ownership and diff layers\n- `frb-code-generation` - Choose the correct generation command\n- `frb-prepare-pr` - Prepare an FRB PR\n- `frb-fix-ci` - Diagnose CI failures after the PR is opened\n\n<!-- File: .claude/skills/frb-cargokit/SKILL.md (Tokens: ~967 | Category: anthropic-skill) -->\n---\nname: frb-cargokit\ndescription: Explain how cargokit is sourced, copied, and analyzed inside flutter_rust_bridge. Use when working on `integration_template/**/cargokit`, `frb_example/**/cargokit`, integrate/generate output drift, or any diff that mentions cargokit and you need to identify the true source of truth before judging the change.\n---\n\n# FRB Cargokit\n\n## Overview\n\nUse this skill to avoid mixing together three different things: the external cargokit repository, the template submodule pointers in FRB, and copied cargokit files inside generated examples.\n\nThe main job is to determine which layer a change belongs to before analyzing whether the change is correct.\n\n## Mental Model\n\nThere are two important cargokit forms inside FRB:\n\n1. External source\n   FRB depends on an external cargokit repository through git submodules under `frb_codegen/assets/integration_template/**/cargokit`.\n\n2. Copied outputs\n   FRB commands such as integrate/generate often copy concrete cargokit files into `frb_example/**/cargokit` or `frb_example/**/rust_builder/cargokit`.\n\nThis means a large cargokit diff in examples often does not mean FRB designed a large new cargokit behavior. It may only mean a template or submodule change propagated into copied outputs.\n\n## Source Of Truth\n\nTreat these paths as the source of truth when deciding cargokit behavior:\n\n- `frb_codegen/assets/integration_template/app/rust_builder/cargokit`\n- `frb_codegen/assets/integration_template/plugin/cargokit`\n\nTreat these paths as downstream copies unless the evidence shows otherwise:\n\n- `frb_example/**/cargokit/**`\n- `frb_example/**/rust_builder/cargokit/**`\n\nDo not start by judging copied example files in isolation. First determine which source submodule pointer or template content produced them.\n\n## Synchronizing Copies\n\nUse `./frb_internal sync-cargokit-copies` to refresh checked-in Cargokit copies from the template submodules when those copies are not already regenerated by the integrate CI. The command mirrors the Rust integration template writer's Cargokit filtering and copied-file prelude.\n\nDo not add `flutter_via_create`, `flutter_via_integrate`, or `flutter_package` to this sync map; the integrate generation CI already recreates those examples from templates. The CI `Generate :: Internal` job runs this as part of `./frb_internal generate-internal --set-exit-if-changed --coverage`, so the normal generate-internal diff gate also catches Cargokit copy drift. If Cargokit copies drift, fix the source-of-truth template/submodule first when appropriate, run `./frb_internal sync-cargokit-copies`, and commit the resulting copied-output refresh.\n\n## Diff Analysis Order\n\nWhen a user asks \"what happened to cargokit?\" follow this order:\n\n1. Identify the layer.\n   Decide whether the diff is a submodule pointer update, a template file change, or copied output churn.\n\n2. If the change is a submodule pointer update, inspect the two external cargokit commits first.\n   Compare the old and new submodule SHAs inside the submodule repository before saying anything about the FRB-side diff.\n\n3. Only then inspect copied example files.\n   Ask whether they merely reflect the upstream change or whether FRB also introduced independent local edits.\n\n4. Separate semantic changes from churn.\n   Distinguish real logic changes from format-only refreshes, lockfile updates, scaffold refreshes, and regenerated outputs.\n\n## What To Say Explicitly\n\nWhen answering, state which of these is true:\n\n- \"This is an external cargokit change exposed by a submodule pointer update.\"\n- \"This is downstream copy churn caused by integrate/generate.\"\n- \"This repository also has local copied cargokit edits in examples.\"\n\nIf discussing correctness, tie the judgment to the correct layer:\n\n- For submodule updates, judge the diff between the two external SHAs.\n- For copied outputs, judge whether the copy is expected and synchronized.\n- Do not infer upstream intent from a regenerated example diff alone.\n\n## Common Mistakes\n\nAvoid these mistakes:\n\n- Treating `frb_example/**/cargokit/**` as the primary implementation source.\n- Assuming a large example diff implies a large semantic cargokit change.\n- Analyzing copied files before checking the submodule pointer diff.\n- Mixing \"FRB updated the pointer\" with \"FRB locally rewrote cargokit.\"\n\n## Scope Boundary\n\nThis skill explains ownership, propagation, and diff interpretation.\n\nUse `frb-code-generation` when you need to know which generation command to run.\nUse `frb-debugging` when generation or cargokit-related behavior is failing and needs deeper investigation.\n\n<!-- File: .claude/skills/frb-categorize-code-diff/SKILL.md (Tokens: ~490 | Category: anthropic-skill) -->\n---\nname: frb-categorize-code-diff\ndescription: Use when classifying a flutter_rust_bridge branch or PR diff into test and non-test changes, including colocated Rust test modules.\n---\n\n# 1 When to use\n\n- Use this skill to answer which changes in a branch or PR are tests and which are not.\n- Use the repository script instead of classifying paths by hand. Rust tests are commonly colocated in production `.rs` files.\n\n## 2 Run\n\n- Fetch the PR base before relying on `origin/master`.\n- Run from the repository root:\n\n```bash\nuv run --no-project .claude/skills/frb-categorize-code-diff/categorize_code_diff.py --base origin/master --head HEAD\n```\n\n- Add `--json` when another tool will consume the result.\n- Pass the PR's actual base revision when it is not `origin/master`.\n\n## 3 Interpret\n\n- `test-only`: every changed line is in a dedicated test path or a recognized colocated Rust test region.\n- `non-test-only`: every changed line is outside recognized test code.\n- `mixed`: the file contains both test and non-test changed lines.\n- `unclassified`: the diff cannot be mapped confidently, such as a binary patch.\n- Treat the non-test list as conservative. Unknown code is non-test rather than silently counted as test.\n\n## 4 Reliability\n\n- The script compares `base...head`, matching GitHub pull request diff semantics.\n- Dedicated test paths include `test/`, `tests/`, `test_fixtures/`, and conventional test filenames.\n- Rust inline classification recognizes brace-delimited items preceded by `#[cfg(test)]` or `#[test]` in both the base and head revisions.\n- Re-run after every PR update. Line identities and rename targets may change.\n- Inspect `mixed` files manually before claiming that a PR contains no production changes.\n\n## 5 Validation\n\n- Run the script tests after changing its classification logic:\n\n```bash\nPYTHONDONTWRITEBYTECODE=1 uv run --no-project --with pytest --with typer pytest -q .claude/skills/frb-categorize-code-diff/test_categorize_code_diff.py\n```\n\n- Keep `PYTHONDONTWRITEBYTECODE=1`; otherwise pytest leaves an untracked `__pycache__/` inside the skill directory.\n\n<!-- File: .claude/skills/frb-ci-filter/SKILL.md (Tokens: ~2315 | Category: anthropic-skill) -->\n---\nname: frb-ci-filter\ndescription: Use when running focused flutter_rust_bridge GitHub Actions CI via ci_filter, adding/removing the ci-manual-dispatch PR label, choosing exact CI jobs or matrix entries, documenting intentionally partial CI runs, or repeatedly validating an exact CI target on one commit.\n---\n\nUse this skill when CI feedback is too expensive and the current task needs a focused GitHub Actions signal instead of automatic full CI.\n\nThe preferred mechanism is the formal `workflow_dispatch` input in `.github/workflows/ci.yaml`:\n\n```bash\ngh workflow run ci.yaml --ref <branch> -f 'ci_filter=<filter>'\n```\n\nDo not temporarily hack workflow jobs just to get partial CI unless the formal filter cannot express the target.\n\n# 1 Filter Syntax\n\n```text\nfull\njob_id\njob_a,job_b\njob_id[dimension=value]\njob_id[dimension=value|other_value,other_dimension=value]\njob_a,job_b[dimension=value]\n```\n\n- `full` or `*` runs the normal full CI plan.\n- `job_id` runs the whole job, including its full matrix if it has one.\n- `job_id[...]` runs only matching matrix entries.\n- Bracket filters currently require `dimension=value`.\n- Commas outside brackets combine multiple jobs.\n- Explicit `dimension=value` conditions inside brackets are ANDed.\n- `|` means OR within one dimension.\n- Matrix dimensions include direct keys such as `image`, `package`, `device`, `api-level`, `sanitizer`, `platforms`, and nested `matrix.info` keys such as `platform`, `target`, `version`.\n- Numeric matrix values are matched as strings, e.g. `api-level=35`.\n- Values may contain spaces, e.g. iOS simulator device names.\n- Unknown jobs, unknown dimensions, malformed filters, and filters matching no matrix entries fail in the `Plan :: CI` job.\n\n# 2 Manual Dispatch Label\n\nUse the `ci-manual-dispatch` PR label when an agent or human wants to prevent automatic PR CI from running the full surface while iterating.\n\n```bash\ngh pr edit <pr-number> --add-label ci-manual-dispatch\n```\n\nWith the label present:\n\n- normal PR-triggered CI produces an empty plan for the heavy jobs\n- `workflow_dispatch` remains available for explicit focused runs\n- the PR must not be treated as merge-ready from partial runs alone\n\nBefore final readiness:\n\n```bash\ngh pr edit <pr-number> --remove-label ci-manual-dispatch\n```\n\nThen let the normal full CI surface run. Removing the label triggers PR CI because the workflow listens to `labeled` and `unlabeled` pull request events.\n\n# 3 Manual Dispatch PR Comment\n\nManual `workflow_dispatch` runs of `.github/workflows/ci.yaml` comment from `Plan :: CI` on the matching open PR with the selected `ci_filter` and run link; normal `push` and `pull_request` runs do not comment.\n\n# 4 Local Planning\n\nBefore dispatching an expensive manual run, smoke the filter locally:\n\n```bash\n./frb_internal plan-ci --filter 'test_dart_web[package=frb_example--pure_dart_pde]'\n```\n\nThe command emits a GitHub Actions output line:\n\n```text\nplan={...}\n```\n\nThe workflow consumes it with paths like:\n\n```yaml\nfromJSON(needs.plan_ci.outputs.plan).test_dart_web.enable\nfromJSON(needs.plan_ci.outputs.plan).test_dart_web.matrix\n```\n\nThat output shape is intentional; each CI job has its own top-level entry containing `enable`, and matrix jobs also contain `matrix`.\n\n# 5 Repeated Precise Validation\n\nUse repeated precise validation when a flaky fix needs several independent executions of one CI job or matrix entry on exactly the same commit.\n\n- **Fix the commit**: Push the branch once, record its SHA, and do not move the branch until all runs finish.\n- **Smoke the filter**: Run `./frb_internal plan-ci --filter '<filter>'` before dispatching.\n- **Dispatch independently**: Create five separate `workflow_dispatch` runs, not one run with five matrix copies and not five attempts of one run.\n- **Keep the filter identical**: Pass the same exact `ci_filter` to every dispatch.\n- **Verify the SHA**: Confirm every selected run reports the recorded `headSha`; a branch name alone is not evidence that queued runs used the same commit.\n- **Count target jobs**: Count the conclusion of the selected job or matrix entry, not the workflow conclusion or skipped jobs.\n- **Preserve every run link**: Report all five run URLs, the fixed SHA, the filter, and the target-job result.\n- **Do not move or cancel**: A new commit invalidates the comparison; cancellation does not count as a completed validation.\n\nRecord the immutable input:\n\n```bash\nbranch=codex/example-fix\nexpected_sha=$(git rev-parse HEAD)\nfilter='test_dart_web[package=frb_example--pure_dart]'\ngit push -u origin \"$branch\"\n```\n\nDispatch five independent runs. Manual dispatches use unique concurrency groups, so these runs do not cancel one another:\n\n```bash\nfor run_number in 1 2 3 4 5; do\n  gh workflow run ci.yaml --ref \"$branch\" -f \"ci_filter=$filter\"\ndone\n```\n\nList the candidate runs once, then select the five runs created for this validation window:\n\n```bash\ngh run list \\\n  --workflow ci.yaml \\\n  --branch \"$branch\" \\\n  --event workflow_dispatch \\\n  --limit 10 \\\n  --json databaseId,createdAt,headSha,status,conclusion,url\n```\n\nFor each selected run:\n\n- Confirm `headSha == expected_sha`.\n- Confirm `Plan :: CI` creates only the requested job or matrix entry.\n- Wait for the target job to complete.\n- If it fails, archive and inspect that job's complete log.\n- Report a result only after all five target jobs finish.\n\nDo not claim “5/5” from any of these shapes:\n\n- One workflow run containing five matrix entries.\n- One workflow run rerun five times.\n- Five workflow runs whose `headSha` values differ.\n- Five workflow conclusions where the target job was skipped, cancelled, or never started.\n- Fewer than five completed target jobs plus queued or in-progress jobs.\n\n# 6 Examples\n\nThese examples are protected by `tools/frb_internal/test/ci_plan_test.dart`; update that test whenever adding or changing examples here.\n\nFull CI:\n\n```bash\ngh workflow run ci.yaml --ref <branch> -f 'ci_filter=full'\n```\n\nOne non-matrix job:\n\n```bash\ngh workflow run ci.yaml --ref <branch> -f 'ci_filter=lint_dart_primary'\n```\n\nSeveral whole jobs:\n\n```bash\ngh workflow run ci.yaml --ref <branch> -f 'ci_filter=lint_dart_primary,test_dart_web'\n```\n\nOne non-matrix job plus one matrix entry:\n\n```bash\ngh workflow run ci.yaml --ref <branch> -f 'ci_filter=lint_dart_primary,test_dart_web[package=frb_example--pure_dart]'\n```\n\nOne Dart web package:\n\n```bash\ngh workflow run ci.yaml --ref <branch> -f 'ci_filter=test_dart_web[package=frb_example--pure_dart_pde]'\n```\n\nTwo Dart web packages:\n\n```bash\ngh workflow run ci.yaml --ref <branch> -f 'ci_filter=test_dart_web[package=frb_dart|frb_example--pure_dart_pde]'\n```\n\nTwo Dart web packages plus one non-matrix job:\n\n```bash\ngh workflow run ci.yaml --ref <branch> -f 'ci_filter=test_dart_web[package=frb_dart|frb_example--pure_dart_pde],lint_rust_primary'\n```\n\nTwo precise matrix tuples in the same job:\n\n```bash\ngh workflow run ci.yaml --ref <branch> -f 'ci_filter=test_rust[image=ubuntu-latest,version=nightly],test_rust[image=ubuntu-latest,version=1.85.0]'\n```\n\nOne Dart native package on Linux:\n\n```bash\ngh workflow run ci.yaml --ref <branch> -f 'ci_filter=test_dart_native[image=ubuntu-24.04,package=tools--frb_internal]'\n```\n\nRust tests on selected toolchains:\n\n```bash\ngh workflow run ci.yaml --ref <branch> -f 'ci_filter=test_rust[image=ubuntu-latest,version=nightly|1.85.0]'\n```\n\nOne Flutter desktop package on Linux:\n\n```bash\ngh workflow run ci.yaml --ref <branch> -f 'ci_filter=test_flutter_native_desktop[platform=linux,package=frb_example--gallery]'\n```\n\nOne Android emulator matrix entry:\n\n```bash\ngh workflow run ci.yaml --ref <branch> -f 'ci_filter=test_flutter_native_android[package=frb_example--flutter_via_create,device=pixel,api-level=35]'\n```\n\nOne iOS simulator matrix entry:\n\n```bash\ngh workflow run ci.yaml --ref <branch> -f 'ci_filter=test_flutter_native_ios[device=iPhone 16 Pro Max Simulator (18.6),package=frb_example--rust_ui_counter--ui]'\n```\n\nOne codegen command-generate entry:\n\n```bash\ngh workflow run ci.yaml --ref <branch> -f 'ci_filter=generate_run_frb_codegen_command_generate[image=ubuntu-24.04,package=frb_example--integrate_third_party]'\n```\n\nOne OHOS command-integrate entry:\n\n```bash\ngh workflow run ci.yaml --ref <branch> -f 'ci_filter=generate_run_frb_codegen_command_integrate[platforms=ohos]'\n```\n\nOne sanitizer entry:\n\n```bash\ngh workflow run ci.yaml --ref <branch> -f 'ci_filter=test_dart_sanitizer[sanitizer=asan,package=frb_example--pure_dart]'\n```\n\nOne benchmark runner:\n\n```bash\ngh workflow run ci.yaml --ref <branch> -f 'ci_filter=bench_dart_native[image=ubuntu-24.04]'\n```\n\n# 7 Choosing Evidence\n\nFor intentional red reproduction PRs:\n\n- Run only the single job or matrix entry that proves the reported bad behavior.\n- State the exact `ci_filter` in the PR body.\n- State the expected failure text or behavior.\n- Do not treat the narrowed red run as fix readiness evidence.\n\nFor ordinary iteration branches:\n\n- Prefer the smallest filter that exercises the changed behavior.\n- Say in PR status/comments when only filtered CI has run.\n- Before final review or merge readiness, remove `ci-manual-dispatch` and run normal CI.\n\n# 8 Validation\n\nBefore pushing changes to the filter mechanism or examples:\n\n```bash\ncd tools/frb_internal\ndart test test/ci_plan_test.dart\n```\n\nUseful smoke check:\n\n```bash\n./frb_internal plan-ci --filter 'test_dart_web[package=frb_example--pure_dart_pde]'\n```\n\n<!-- File: .claude/skills/frb-code-generation/SKILL.md (Tokens: ~1247 | Category: anthropic-skill) -->\n---\nname: frb-code-generation\ndescription: Use when modifying Rust APIs, codegen, generated examples, or platform scaffolds in flutter_rust_bridge to select generation commands and preserve source-of-truth and convergence rules\n---\n\n# FRB Code Generation\n\n> **Note:** Read `frb-dev-env` before running commands and follow the active user's environment rules.\n\n## Overview\n\nflutter_rust_bridge requires code generation when Rust APIs change. This skill maps change types to the minimal generation commands needed.\n\n**Core principle:** Run only the generation commands needed for your change type.\n\n**Validation rule:** If regenerated outputs cause previously green non-`Generate` jobs to fail, treat the generated outputs as suspect until they are validated from a clean environment.\n\n> **After codegen:** Follow `frb-dev-env` and verify generated output is present in the selected worktree before reviewing the diff.\n\n## Quick Reference\n\n| Change Type | Command |\n|-------------|---------|\n| Example Rust API (`frb_example/**/rust/src/api/*.rs`) | `./frb_internal precommit-generate` |\n| Flutter integrate examples | `./frb_internal precommit-integrate` |\n| `frb_codegen/` generation logic | `./frb_internal precommit-generate` + `generate-internal-rust` |\n| `frb_rust/src/` core API | `./frb_internal generate-internal-rust` |\n| `frb_example/pure_dart` generator | `./frb_internal generate-internal-frb-example-pure-dart` |\n| CLI help documentation | `./frb_internal generate-internal-book-help` |\n| Root `README.md` or `CHANGELOG.md` | `./frb_internal generate-internal-readme` |\n| Other non-generated docs, comments, tests, or `frb_dart/` source | No generation needed |\n\n## Important Rules\n\n### Root Documentation Fan-Out\n\nTreat the root `README.md` and `CHANGELOG.md` as sources of truth. After changing either file, run:\n\n```bash\n./frb_internal generate-internal-readme\n```\n\nCommit the generated package documentation in the same change. Do not manually edit `frb_dart/README.md`, `frb_dart/CHANGELOG.md`, or `frb_hooks/CHANGELOG.md`.\n\n### Integrate Template Drift\n\nIf a diff touches Flutter integrate example outputs, platform scaffolds, or copied `cargokit` files under `frb_example/**`, check whether the real source of truth should also change under `frb_codegen/assets/integration_template/`.\n\nDo not submit only downstream integrate output changes when the behavior belongs in the template. Update the template first, then run:\n\n```bash\n./frb_internal precommit-integrate\n```\n\nTreat `frb_codegen/assets/integration_template/**/cargokit` as the source of truth for copied `cargokit` output. If the actual bug belongs inside the external `cargokit` submodule, read the `frb-cargokit` skill before deciding whether to patch the submodule and update its pointer.\n\nFor CI diagnosis rules about generated-file format/lint failures, repeated package-level `Generate` drift, or `Generate :: FRB Codegen :: Command Integrate` failures, you MUST read `frb-fix-ci` first. This skill is for command selection, not CI failure-propagation diagnosis.\n\nFor `pure_dart` / `pure_dart_pde` generation issues, treat `frb_example/pure_dart` as the upstream source and `frb_example/pure_dart_pde` as the derived copy. See `frb-fix-ci` for the CI diagnosis workflow.\n\nIf CI repair has already entered repeated package-level drift, you MUST stop choosing narrower commands and switch to `frb-fix-ci`.\n\nDo not manually patch generated files as the final fix. The final accepted result should be produced by the corresponding generation command in a clean matching environment.\n\n### Generation Convergence\n\n- Run the narrowest owning generator before broad generation.\n- Normalize the intended source inputs, resolve dependencies, run final code generation, and format with the target toolchain. Do not hand formatter-unstable intermediate text to a different generation lane.\n- Run the owning generation command a second time and require a clean diff. A single successful run is not convergence.\n- Preserve semantic equivalence in templates. Do not move construction, scope, lifetime, or callback boundaries merely to make new formatter output look smaller.\n\n### Generated Output Provenance\n\nClassify every changed path before accepting generated drift:\n\n| Provenance | Required action |\n| --- | --- |\n| Integration template | Change the template source and regenerate consumers |\n| Apple scaffold asset | Refresh through the owning scaffold workflow |\n| CargoKit copy | Read `frb-cargokit` and follow its ownership and synchronization rules |\n| Generated example output | Keep the generator and snapshot together |\n| Flutter migrator edit in a legacy example | Apply the exact current scaffold migration and record it as direct |\n| Unexplained manual edit | Revert it or identify its owning source before proceeding |\n\n- When an old example has no supported regeneration entry point, compare it with a fresh target-Flutter scaffold and directly apply only required tool-defined migrations.\n- Record direct legacy scaffold migrations separately from automatic output.\n\n### OHOS Integrate Composition\n\n- Treat the checked-in package and the older OHOS Flutter fork as two distinct sources of truth.\n- Generate generic Flutter scaffold files with the current Flutter package.\n- Overlay only explicit OHOS-owned paths from the OHOS fork: `ohos` and, when required by the package, `rust_builder/ohos` and `rust_builder/pubspec.yaml`.\n- Exclude transient trees such as `node_modules` and preserve intentional deletions instead of resurrecting stale files.\n- Stage and validate the composed result before replacing the canonical package. Restore the original package if create, dependency resolution, code generation, formatting, or the final swap fails.\n- Test both success and failure paths. Do not add production APIs whose only purpose is exposing internals to tests.\n\n<!-- File: .claude/skills/frb-debugging/SKILL.md (Tokens: ~354 | Category: anthropic-skill) -->\n---\nname: frb-debugging\ndescription: Use when generated code looks wrong, code generation fails, or you need to understand FRB internals\n---\n\n# FRB Debugging\n\n> **Note:** Read `frb-dev-env` before running commands and follow the active user's environment rules.\n\n## When to Use\n\n- Generated code looks wrong\n- Code generation fails\n- Need to understand FRB internals\n- Runtime errors in generated code\n\n## Quick Diagnostics\n\n### Dump Intermediate Representations\n\nThe in-tree examples by default enable `dump_all: true` in  `flutter_rust_bridge.yaml` for debugability.\n\nTherefore, you can check `rust/target/frb_dump/` for:\n- Effective configuration\n- IR (intermediate representation)\n- Generated spec and code\n- ...\n\n### Debug Logs\n\n```bash\nRUST_LOG=debug flutter_rust_bridge_codegen ...\n```\n\n## Full Troubleshooting\n\nIf above doesn't help, see:\n\n* `website/docs/docs/guides/contributing/tip.md`\n* `website/docs/manual/troubleshooting.md`\n\nBefore going deep into FRB internals, first check whether you are actually seeing failure propagation from unstable generated outputs or integrate templates.\n\nIn particular:\n\n- If CI failures may be explained by failure propagation, repeated package-level drift, or unstable generated outputs, you MUST use `frb-fix-ci` first\n- You MUST only stay in `frb-debugging` after `frb-fix-ci` has ruled out those simpler explanations\n\n## Related Skills\n\n- `frb-code-generation` - Which generation commands to run\n- `frb-fix-ci` - Diagnose CI failure propagation before deep debugging\n- `frb-develop-feature` - Development workflow\n\n<!-- File: .claude/skills/frb-dev-env/SKILL.md (Tokens: ~5116 | Category: anthropic-skill) -->\n---\nname: frb-dev-env\ndescription: \"Use when the user wants Docker-based FRB development or Tart-based iOS Simulator validation.\"\n---\n\n# FRB Development Environment\n\nUse this skill before setting up, diagnosing, or running `flutter_rust_bridge` commands with a container-based development workflow.\n\n## Choosing an Environment\n\nUse Docker for normal FRB development checks that fit Linux containers: Rust tests, Dart tests, web tests, code generation, lint, Android build checks that do not need a running emulator, and most `./frb_internal` commands. Docker is the default reproducible baseline when local host toolchains are missing or suspected to have drifted.\n\nUse a host Android Emulator for local Android runtime validation when a running Android target is required. Keep FRB, Flutter, Rust, Gradle, and Android build tooling inside Docker. The host runs only the emulator and the Android SDK command-line tools needed to manage it. Docker runs its own ADB server and connects directly to the emulator TCP endpoint so Flutter's VM service forwarding stays inside Docker. See the Android section below.\n\nUse Tart only for checks that need macOS and Xcode, especially iOS Simulator validation. The Tart workflow is intentionally separate from Docker; it gives each worktree a reusable macOS VM cloned from a prepared base VM, then runs FRB commands inside that VM.\n\nDo not use the Tart macOS VM as the Android emulator strategy. macOS Tart guests do not support nested virtualization, so Android emulator runtime coverage needs a host or runner with virtualization support. Android SDK compilation checks can still use Docker when they do not need a running emulator.\n\n## Principles\n\n- FRB runs locally in Docker by default. Do not use remote workspaces for FRB unless the user explicitly asks for a one-off remote run.\n- Use the current checkout/worktree that Codex or the user already selected. Do not create a new worktree unless the user explicitly asks.\n- Run FRB commands from the repository root unless the command itself intentionally changes directories.\n- Prefer repository tooling such as `./frb_internal` over ad hoc direct invocations.\n- Do not manually edit generated files (`frb_generated.*`, `*.freezed.dart`, `*.g.dart`) as the final fix.\n\n## PATH Bootstrap\n\nBefore using this skill's helper or FRB tooling, make sure common local tool locations are in `PATH`. Codex shells may start with a reduced `PATH`, which can hide `uv`, Docker, Dart, Flutter, or Homebrew-installed tools even when they exist on the machine.\n\nUse this prefix when a command reports `env: uv: No such file or directory`, `docker: command not found`, `dart: command not found`, or similar:\n\n```bash\nexport PATH=\"$HOME/.local/bin:/opt/homebrew/bin:/usr/local/bin:$PATH\"\n```\n\nThen retry the same command. Prefer fixing `PATH` over bypassing the helper, because `frb_dev_env.py` uses the configured per-worktree container and release credential handling.\n\n## First Checks\n\nBefore running tests, lint, code generation, or setup:\n\n```bash\ngit rev-parse --show-toplevel\ngit status --short\ngit submodule status --recursive\n```\n\nIf submodules are uninitialized, initialize them locally:\n\n```bash\ngit submodule update --init --recursive\n```\n\n## Docker\n\nUse Docker for container-based FRB development. Each worktree should have one long-lived local container that is reused for tests, lint, code generation, and setup commands.\n\nUse `frb_dev_env.py` next to this skill to inspect, create, start, and reuse the per-worktree container. The script derives the container name from the canonical worktree root only:\n\n```text\nfrb-<worktree-root-sha256-prefix-12>\n```\n\nIt mounts the canonical worktree root and git common root at their host-like absolute paths, and labels the container with:\n\n```text\nfrb.dev.repo=flutter_rust_bridge\nfrb.dev.worktree=<canonical worktree root>\nfrb.dev.git-common-root=<git common root>\nfrb.dev.layout-version=3\n```\n\nCommands run from the host-like worktree path inside the container. There is intentionally no `/workspace` alias, because linked git worktrees and submodule gitdir references need the same path shape that they have on the host.\n\nWhen creating a new per-worktree container, the helper pulls the selected Docker image before `docker run`. Existing containers are validated and reused without pulling.\n\nTypical usage:\n\n```bash\n.claude/skills/frb-dev-env/frb_dev_env.py docker info\n.claude/skills/frb-dev-env/frb_dev_env.py docker create\n.claude/skills/frb-dev-env/frb_dev_env.py docker exec -- bash -lc './frb_internal --help'\n```\n\n### Temporary Docker Commands With Publish Credentials\n\nUse `docker-run-rm --with-publish-credentials` only for release publishing commands that need host credentials. Unlike the normal long-lived development container, this runs a fresh temporary container with `docker run --rm`, mounts the worktree and git common root, mounts the host credential directories read-only, copies the credential files into temporary container-local homes, checks login status, runs the requested command, then discards the container.\n\nThe publish credential flag mounts these host credential sources when present:\n\n- GitHub CLI config from `GH_CONFIG_DIR` or `~/.config/gh`\n- GitHub token env vars from `GH_TOKEN` or `GITHUB_TOKEN`, forwarded without printing token values\n- Cargo credentials and config from `CARGO_HOME` or `~/.cargo`\n- Git config from `~/.gitconfig` and `~/.config/git`\n- Dart pub credentials from `PUB_CACHE`, `~/.pub-cache`, `~/.config/dart`, or `~/Library/Application Support/dart`\n\nPublish mode also prepares the container environment used by the release command:\n\n- Reuses the Docker image's Rust toolchain via `RUSTUP_HOME=/root/.rustup` even though `HOME` is changed to a temporary credential directory.\n- Rewrites copied Git/Cargo config proxy URLs from `localhost` or `127.0.0.1` to `host.docker.internal`, and forwards common proxy environment variables with the same rewrite. This lets a container use a host proxy such as `http://localhost:7897`.\n- Checks GitHub auth, GitHub network access, Cargo availability, crates.io search access, and Dart pub credential readability before running the requested command.\n\nRun the credential preflight before any irreversible release step:\n\n```bash\n.claude/skills/frb-dev-env/frb_dev_env.py docker-run-rm --with-publish-credentials -- true\n```\n\nRun release publishing through a temporary publish-credential container. Do not manually add `RUSTUP_HOME` or proxy environment overrides unless debugging the helper itself:\n\n```bash\n.claude/skills/frb-dev-env/frb_dev_env.py docker-run-rm --with-publish-credentials -- ./frb_internal release\n```\n\nThe credential preflight fails before running release commands if host GitHub CLI auth is invalid, Cargo credentials are missing, Dart pub credentials are missing, the image has no usable Cargo toolchain, or the container cannot reach GitHub/crates.io through the configured network path. It then re-checks GitHub CLI auth inside the temporary container and runs `gh auth setup-git` there so HTTPS `git push` can use the mounted GitHub CLI auth.\n\n### Cleanup\n\nDelete a worktree's Docker container when the worktree is no longer needed, or when local Docker resources are getting crowded:\n\n```bash\n.claude/skills/frb-dev-env/frb_dev_env.py docker delete\n```\n\nThe delete command validates the container labels before removing it. Use `--force` only when intentionally removing a mismatched container.\n\nUse the project `frb-docker` skill for image, devcontainer, and Dockerfile details.\n\n## Android Host Runtime\n\nUse this workflow for local Android runtime checks when Docker can build and run the FRB/Flutter command, but the Android target itself must be managed by the host.\n\n### Path Selection\n\nThe supported Android runtime path is:\n\n```text\nhost Android Emulator:\n  host runs the emulator process\n  Docker runs FRB/Flutter and its own ADB server\n  Docker ADB connects to host.docker.internal:<emulator-adb-port>\n```\n\nPhysical Android phones are intentionally out of scope for this helper until that path is manually validated. The host does not need FRB, Flutter, Rust, Cargo, Java, or Gradle for the emulator path.\n\n### Host Emulator Architecture\n\nUse this split for a host Android Emulator:\n\n```text\nhost:\n  Android Emulator process\n  Android SDK command-line tools for SDK packages, AVD creation, emulator start/stop, and host-side inspection\n  Android SDK root: ~/Library/Android/sdk unless ANDROID_HOME is set\n  AVD root: ~/.android/avd\n\nDocker container:\n  FRB checkout\n  Flutter, Dart, Rust, Cargo, Java, Gradle\n  Android SDK/NDK/build tools needed for builds\n  Docker-local ADB server connected to host.docker.internal:<emulator-adb-port>\n```\n\nRead the project `frb-android-emulator-prepare` skill before preparing a host emulator. The host emulator path needs Java, Android SDK command-line tools, emulator packages, a system image, and an AVD in the standard Android Studio host locations.\n\nPrint the resolved host Android SDK environment if needed:\n\n```bash\n.claude/skills/frb-dev-env/frb_dev_env.py android env\n```\n\nStart the emulator on the host. Pin the port when you want a stable host serial such as `emulator-5554`; the paired emulator ADB TCP endpoint is one port higher, such as `host.docker.internal:5555`:\n\n```bash\n.claude/skills/frb-dev-env/frb_dev_env.py android emulator --avd FRB_API_35 --port 5554\n```\n\nVerify Docker-local ADB can see the host emulator:\n\n```bash\n.claude/skills/frb-dev-env/frb_dev_env.py docker exec --android-emulator-adb -- adb devices -l\n```\n\nRun the FRB/Flutter command against the Docker-visible emulator serial:\n\n```bash\n.claude/skills/frb-dev-env/frb_dev_env.py docker exec --android-emulator-adb -- \\\n  bash -lc './frb_internal test-flutter-native --package frb_example--flutter_via_create --flutter-test-args \"--device-id host.docker.internal:5555\"'\n```\n\n`docker exec --android-emulator-adb` first runs `adb connect host.docker.internal:5555` inside the container, then runs the requested command with the Android SDK x86_64 runtime library path set. Override the default endpoint with `--android-emulator-adb-host`, `--android-emulator-adb-port`, `FRB_ANDROID_EMULATOR_ADB_HOST`, or `FRB_ANDROID_EMULATOR_ADB_PORT`.\n\n### Why Emulator Does Not Use Host ADB Server\n\nDo not use a host ADB server for host emulator full Flutter integration tests. Flutter creates dynamic VM service `adb forward` ports where the ADB server runs. If the ADB server runs on the host, those forwarded ports bind on host `127.0.0.1`, but Flutter is running inside Docker and then tries Docker-local `127.0.0.1`. This fails with DDS or VM service connection refused errors.\n\nUse `--android-emulator-adb` so the ADB server and the forwarded VM service port both live inside Docker.\n\n### Security And Cleanup\n\n- Use a single Android target when possible. With multiple emulators or devices, always pass `--device-id <serial>` through `--flutter-test-args`.\n- For host emulator Flutter integration tests, use `--android-emulator-adb` so `adb forward` creates VM service forwarding inside Docker.\n- Do not run the Android Emulator inside Tart. Do not rely on Docker nested virtualization for Android emulator runtime checks on macOS.\n- If the Docker image lacks Android SDK, NDK, or build-tools packages required by Flutter Android builds, update the FRB Docker image or mount a dedicated Android SDK into the container; do not install host Flutter or Gradle just to work around the missing container toolchain.\n\n## Tart\n\nUse Tart for iOS Simulator validation when Docker cannot provide the required macOS/Xcode runtime. The script uses one reusable Tart VM per worktree, named from the canonical worktree root:\n\n```text\nfrb-tart-<worktree-root-sha256-prefix-12>\n```\n\n### Base VM\n\nBy default, `create` clones from a prepared local base VM named `frb-tart-base`. Override it with `FRB_TART_BASE_VM` or `--base-vm`.\n\nRead `frb-tart-prepare` before creating or changing the base VM. The base VM should be built by the checked-in Packer template from a pinned Tart OCI artifact and treated as immutable; do not boot it and manually install tools into it.\n\n### Mounted Checkout\n\nFor linked git worktrees, the Tart helper shares both the canonical worktree root and the git common root into the VM, then mounts them at host-like absolute paths with `mount_virtiofs`. This keeps worktree `.git` files and submodule gitdir references valid inside the VM.\n\nUse plain `tart exec` for light commands that should see the mounted host checkout exactly, such as `git status`, `flutter --version`, `xcrun simctl list`, and simulator boot commands.\n\n### VM Lifecycle\n\nInspect, create, start, and run light commands in the per-worktree VM:\n\n```bash\n.claude/skills/frb-dev-env/frb_dev_env.py tart info\n.claude/skills/frb-dev-env/frb_dev_env.py tart create\n.claude/skills/frb-dev-env/frb_dev_env.py tart start\n.claude/skills/frb-dev-env/frb_dev_env.py tart ip --wait 180\n.claude/skills/frb-dev-env/frb_dev_env.py tart exec -- sw_vers\n```\n\n### Tart Guest Network Proxy\n\nWhen the Tart guest needs outbound network access through the host proxy, make the proxy listen on the host's Tart bridge address and pass that URL through the helper. Enable LAN access for the host proxy, keep the mixed proxy port at `7897`, and verify the host listens on `*:7897` rather than only `127.0.0.1:7897`.\n\nTart guests normally reach the host on `192.168.64.1`, so the usual proxy URL is:\n\n```bash\nexport FRB_TART_HOST_PROXY_URL=http://192.168.64.1:7897\n.claude/skills/frb-dev-env/frb_dev_env.py tart exec -- curl -I https://pub.dev\n```\n\nYou can also pass the proxy for a single command:\n\n```bash\n.claude/skills/frb-dev-env/frb_dev_env.py tart exec \\\n  --host-proxy-url http://192.168.64.1:7897 \\\n  -- curl -I https://pub.dev\n```\n\nDelete the worktree VM when it is no longer needed:\n\n```bash\n.claude/skills/frb-dev-env/frb_dev_env.py tart stop\n.claude/skills/frb-dev-env/frb_dev_env.py tart delete\n```\n\n### VM-Local Copy\n\nFor heavy iOS build/test commands, prefer uploading the current worktree to a VM-local copy and running there. This avoids writing Xcode build artifacts through the virtiofs host mount:\n\n```bash\n.claude/skills/frb-dev-env/frb_dev_env.py tart upload\n.claude/skills/frb-dev-env/frb_dev_env.py tart exec --sync-code -- ./frb_internal test-flutter-native --flutter-test-args '--device-id <UDID>' --package <package>\n```\n\n`tart upload` and `tart exec --sync-code` both first ensure the worktree VM is running and the host-like worktree mount exists. They then run `rsync` inside the VM from the mounted host worktree to a VM-local copy under `/Users/admin/frb-dev-env-local-copies/<worktree-hash>`. The upload excludes `.git`, `.dart_tool`, `build`, `target`, `.idea`, and `.vscode`. It does not delete existing files in the VM-local copy, so build caches survive repeated runs.\n\nCommands run with `--sync-code` start from the VM-local uploaded copy, not from the host-mounted path.\n\nExamples:\n\n```bash\n# Show the VM-local path after uploading the current worktree.\n.claude/skills/frb-dev-env/frb_dev_env.py tart upload\n\n# Run a heavy command from the uploaded VM-local copy.\n.claude/skills/frb-dev-env/frb_dev_env.py tart exec --sync-code -- ./frb_internal test-flutter-native --package frb_example--flutter_via_create --flutter-test-args '--device-id <UDID>'\n```\n\n### iOS Integration Preparation\n\nBefore running iOS Simulator integration tests, prepare the VM Flutter SDK once per VM or after replacing Flutter. This applies the runtime portion of `https://github.com/flutter/flutter/pull/187643.patch` as an idempotent Flutter tool workaround for an iOS simulator VM Service discovery race where `simctl launch` can emit the Dart VM Service log before Flutter's `simctl log stream` listener is ready.\n\nThe preparation command also rebuilds `flutter_tools.snapshot`:\n\n```bash\n.claude/skills/frb-dev-env/frb_dev_env.py tart prepare-ios-integration-test\n```\n\nUse the environment shown in the test command below for the integration test. `FLUTTER_GIT_URL` keeps `flutter doctor` and Flutter metadata consistent when the VM uses a local Flutter checkout. `FRB_SKIP_FLUTTER_DOCTOR=1` skips the FRB wrapper's preflight doctor because the simulator integration test itself is the validation target and doctor can hang after printing device checks in Tart.\n\n### iOS Integration Test\n\nRun iOS Simulator tests by starting the worktree VM, preparing the VM Flutter SDK, choosing and booting an iOS simulator inside the VM, then running the existing FRB test command from the VM-local uploaded copy:\n\n```bash\n.claude/skills/frb-dev-env/frb_dev_env.py tart start --wait 300\n.claude/skills/frb-dev-env/frb_dev_env.py tart prepare-ios-integration-test\n.claude/skills/frb-dev-env/frb_dev_env.py tart exec -- xcrun simctl list devices available\n.claude/skills/frb-dev-env/frb_dev_env.py tart exec -- xcrun simctl boot <UDID>\n.claude/skills/frb-dev-env/frb_dev_env.py tart exec -- xcrun simctl bootstatus <UDID> -b\n.claude/skills/frb-dev-env/frb_dev_env.py tart exec --sync-code -- bash -lc 'export FLUTTER_GIT_URL=file:///Users/admin/flutter; export FRB_SKIP_FLUTTER_DOCTOR=1; ./frb_internal test-flutter-native --package frb_example/flutter_via_create --flutter-test-args \"--device-id <UDID>\"'\n```\n\nFor example, when an iOS 18.1 `iPhone 16 Pro Max` simulator with UDID `826DC3E8-2073-42A9-A6DB-05C1926DC82A` is available:\n\n```bash\n.claude/skills/frb-dev-env/frb_dev_env.py tart exec --sync-code -- bash -lc 'export FLUTTER_GIT_URL=file:///Users/admin/flutter; export FRB_SKIP_FLUTTER_DOCTOR=1; ./frb_internal test-flutter-native --package frb_example/flutter_via_create --flutter-test-args \"--device-id 826DC3E8-2073-42A9-A6DB-05C1926DC82A\"'\n```\n\n## VMware Windows\n\nUse VMware Fusion + Vagrant for local Windows desktop validation when Docker, Tart, and the Android Emulator coverage are not enough. This workflow targets Apple Silicon macOS hosts running Windows 11 Arm in VMware Fusion.\n\nRead `tools/vmware_windows/README.md` for the detailed Packer, Vagrant, VMware Fusion, proxy, storage, host-installation, daily-use, and troubleshooting instructions. The README is the source of truth for the Windows VM workflow. Read `frb-vmware-windows-prepare` before preparing or rebuilding the reusable Packer-built base box.\n\n### Storage\n\nConfigure a per-user VM root before creating or building VMs. The helper reads\n`~/.config/flutter_rust_bridge/vmware_windows.json` by default, and environment variables such as\n`FRB_WINDOWS_VM_ROOT` override the config. The helper derives all heavy paths from that configured\nroot, including Vagrant home, boxes, per-worktree project directories, Packer cache/output, logs,\nand VM bundles. Check it before starting a VM:\n\n```bash\ntools/vmware_windows/frb_vmware_windows_env.py info\n```\n\nThe helper uses only the Python standard library and refuses to create directories under `/Volumes/<name>/...` unless that volume is mounted. This keeps first-run helper checks independent from `uv`/`pip` and avoids accidentally writing VM files to the internal disk when an external drive is absent.\n\n### Network\n\nIf Windows VM downloads and Packer host-side network operations need a proxy, configure it in the\nuser config or with:\n\n```bash\nexport FRB_WINDOWS_HOST_PROXY_URL=http://localhost:PORT\n```\n\nLeave it unset when direct network access is preferred. If guest-side provision scripts need a\nproxy, set `FRB_WINDOWS_GUEST_PROXY_URL` to an address reachable from inside the Windows guest.\n\nFor real Packer builds, provide the Windows ISO SHA-256 checksum with `FRB_WINDOWS_ISO_CHECKSUM=sha256:<digest>` or an `.iso.sha256` file next to the ISO. The helper uses a placeholder checksum only for `packer-build --validate`.\n\n### Base Box Preparation\n\nFor the initial base-image build on a developer Mac, prefer the ordinary-Terminal wrapper:\n\n```bash\ntools/vmware_windows/run_packer_build.zsh \\\n  --iso /path/to/frb-windows-vmware/downloads/iso/Windows11_Arm64.iso \\\n  --add-box\n```\n\nThe wrapper applies the configured host proxy when present, runs preflight checks, regenerates the\nUEFI autounattend ISO, writes logs under the configured VM root, monitors `/` and the VM root for the\n100GB free-space threshold, and can import the produced Vagrant box. Use `--prepare-only` when you\nwant to validate ISO remastering before starting VMware.\n\n### Daily Lifecycle\n\nRun from the repository root:\n\n```bash\ntools/vmware_windows/frb_vmware_windows_env.py info\ntools/vmware_windows/frb_vmware_windows_env.py start\ntools/vmware_windows/frb_vmware_windows_env.py upload\ntools/vmware_windows/frb_vmware_windows_env.py exec -- powershell -NoProfile -Command \"cd C:\\frb\\worktree; flutter --version\"\ntools/vmware_windows/frb_vmware_windows_env.py stop\n```\n\n`upload` copies the host checkout into a VM-local directory before heavy builds. Do not compile from a VMware shared folder because shared folders are slower for many small files and can distort build behavior.\n\n### Windows Flutter Test\n\nAfter the VM is running and code is uploaded:\n\n```bash\ntools/vmware_windows/frb_vmware_windows_env.py test-flutter-windows \\\n  --package frb_example/flutter_via_create\n```\n\nThe command runs inside the guest and should use the VM-local uploaded checkout. Capture logs under the external root for audit.\n\n### Credentials And Cleanup\n\nDo not put guest passwords in the repository. Use `FRB_WINDOWS_VM_USERNAME`, `FRB_WINDOWS_VM_PASSWORD`, or `~/.secrets/FRB_WINDOWS_VM_PASSWORD`.\n\nStop the per-worktree VM when it is not needed:\n\n```bash\ntools/vmware_windows/frb_vmware_windows_env.py stop\n```\n\nDelete disposable per-worktree VMs only when intentionally reclaiming space:\n\n```bash\ntools/vmware_windows/frb_vmware_windows_env.py destroy\n```\n\n## Project Skills\n\nAfter applying this environment policy, also read the relevant project-level `frb-*` skills for the actual task, such as code generation, tests, lint, Docker details, CI triage, or PR preparation.\n\n<!-- File: .claude/skills/frb-develop-feature/SKILL.md (Tokens: ~2300 | Category: anthropic-skill) -->\n---\nname: frb-develop-feature\ndescription: Use when fixing bugs, adding regression tests, adding new features, when compilation is slow, or when learning twin test naming conventions in flutter_rust_bridge\n---\n\n# FRB Develop Feature or Bug Fix\n\n> **Note:** Read `frb-dev-env` before running commands and follow the active user's environment rules.\n\n## Overview\n\n**For both bug fixes and new features: iterate in `frb_example/dart_minimal` (fast compile), then migrate the final test to `frb_example/pure_dart` (full coverage).**\n\n`dart_minimal` is only an iteration workspace. A bug fix or feature PR is not ready when its only regression coverage lives in `dart_minimal`; stop before PR preparation and move the final test to `pure_dart`/`pure_dart_pde`.\n\n| Phase | Location | Why |\n|-------|----------|-----|\n| Reproduce / Iterate | frb_example/dart_minimal | Fast compile = quick feedback |\n| Migrate final regression / feature test | frb_example/pure_dart + pure_dart_pde | Twin tests = automatic coverage of all codegen modes |\n\nFor bugs, first make the failure reproducible in `dart_minimal`, write down the exact reproduction report, fix it there, and only then move the regression test to `pure_dart`. For features, follow the same fast-iteration loop before adding the final `pure_dart` coverage.\n\nWrite one final test → get ~6 variants automatically via TwinNormal suffix.\n\n## Final Placement Gate\n\nBefore pushing, opening a PR, requesting review, or monitoring CI as if the work is ready:\n\n- Confirm every final regression or feature test has been moved from `frb_example/dart_minimal` to `frb_example/pure_dart`.\n- Confirm the final Rust APIs/types/functions in `pure_dart` use the `TwinNormal` suffix pattern where applicable.\n- Confirm code generation has produced the matching `pure_dart_pde` coverage.\n- For feature flags, read and apply `frb-feature-flag` before treating the coverage as final.\n- Remove the temporary `dart_minimal` reproducer unless it is intentionally kept as a minimal example in addition to the `pure_dart` regression.\n\nIf any of these checks fail, return to Phase 2. Do not treat a passing `dart_minimal` test as final readiness.\n\n## When to Use\n\n- Fixing a bug that should get a regression test\n- Adding a new function or feature\n- Writing tests for new or existing functionality\n- Compilation feels slow (use frb_example/dart_minimal instead)\n\n## Bug Fix Reproduction Report\n\nFor every bug fix, the PR description must include a reproduction report before the fix is presented as convincing.\n\nCapture the report immediately after reproducing the bug and before changing the fix code. It must include:\n\n- **Baseline commit:** the exact commit hash, pushed and accessible to reviewers, used to reproduce the bug\n- **Mechanical steps:** copy-pasteable commands or file edits, using code blocks for multi-line changes, that reproduce the bug from that commit\n- **Observed failure:** the concrete failing command, error message, panic, assertion, or incorrect output\n- **Expected behavior:** the behavior that should happen after the fix\n\nUse this PR description shape:\n\n````markdown\n## Reproduction\n\nBaseline commit: `<commit-hash>`\n\nSteps:\n1. `<command or edit>`\n2. `<command>`\n\nObserved:\n```text\n<error output>\n```\n\nExpected:\n<expected behavior>\n````\n\nIf the reproduction needs temporary test code in `frb_example/dart_minimal`, describe the exact temporary Rust/Dart edits in the steps. Keep the temporary reproducer only while debugging, then migrate the final regression test to `frb_example/pure_dart`.\n\n## Implementation\n\n```dot\ndigraph workflow {\n    rankdir=TB;\n    node [shape=box];\n\n    \"Write reproducer/test in dart_minimal\" -> \"Run reproducer\";\n    \"Run reproducer\" -> \"Failure reproduced?\" [shape=diamond];\n    \"Failure reproduced?\" -> \"Write reproduction report\" [label=\"yes\"];\n    \"Failure reproduced?\" -> \"Adjust reproducer\" [label=\"no\"];\n    \"Adjust reproducer\" -> \"Run reproducer\";\n    \"Write reproduction report\" -> \"Fix\";\n    \"Fix\" -> \"Run focused test\";\n    \"Run focused test\" -> \"Pass?\" [shape=diamond];\n    \"Pass?\" -> \"Move final test to pure_dart\" [label=\"yes\"];\n    \"Pass?\" -> \"Fix\" [label=\"no\"];\n    \"Move final test to pure_dart\" -> \"Add TwinNormal suffix\" -> \"Run code gen\" -> \"All tests pass?\";\n    \"All tests pass?\" -> \"Done\" [label=\"yes\"];\n    \"All tests pass?\" -> \"Debug\" [label=\"no\"];\n    \"Debug\" -> \"All tests pass?\";\n}\n```\n\n### Phase 1: Iterate in frb_example/dart_minimal\n\n1. **Add the smallest test or bug reproducer:**\n   - Rust: add function to `frb_example/dart_minimal/rust/src/api/minimal.rs`\n   - Dart: add test to `frb_example/dart_minimal/test/minimal_test.dart`\n\n   For bug fixes, this test should fail before the fix and pass after the fix. There is usually no need to create any new files.\n\n2. **Run the reproducer and record the reproduction report:**\n\n   Before changing the fix code, save the baseline commit hash, exact commands or temporary edits, and the observed failure output. This report must go into the PR description.\n\n3. **Add the implementation or fix:**\n\n   Usually need to modify `frb_codegen`, `frb_dart`, and/or `frb_rust`.\n\n4. **Run codegen and test:**\n   ```bash\n   (cd frb_example/dart_minimal && cargo run --manifest-path ../../frb_codegen/Cargo.toml -- generate)\n   ./frb_internal test-dart-native --package frb_example/dart_minimal\n   ```\n\n   > **After codegen:** Follow `frb-dev-env` and verify generated output is present in the selected worktree before reviewing the diff.\n\n5. **Iterate until test passes**\n\n   Keep the temporary `dart_minimal` reproducer while debugging. Passing here only proves the fix is ready to migrate; it is not the final regression placement. Remove or simplify the temporary reproducer after the final regression test has been migrated to `pure_dart`.\n\n### Phase 2: Migrate to frb_example/pure_dart\n\n1. **Move the final feature or regression test to frb_example/pure_dart:**\n   - Rust: `frb_example/pure_dart/rust/src/api/my_feature.rs`\n   - Dart: `frb_example/pure_dart/test/api/my_feature_test.dart`\n\n   Either add to existing files or create new files. Then remove temporary reproducer code from `frb_example/dart_minimal` unless it is intentionally useful as a minimal example.\n\n2. **Add TwinNormal suffix** to all functions and types:\n\n   | Context | Suffix | Example |\n   |---------|--------|---------|\n   | snake_case | `_twin_normal` | `my_func_twin_normal()` |\n   | PascalCase | `TwinNormal` | `MyStructTwinNormal` |\n\n   This triggers automatic test generation (~6 variants) under different codegen modes. Always mimic existing frb_example/pure_dart tests for exact patterns.\n\n3. **Run codegen and test:**\n   ```bash\n   # Codegen\n   ./frb_internal precommit-generate\n\n   # Native tests (can run in parallel)\n   ./frb_internal test-dart-native --package frb_example/pure_dart\n   ./frb_internal test-dart-native --package frb_example/pure_dart_pde\n\n   # Web tests (optional, for web platform coverage)\n   ./frb_internal test-dart-web --package frb_example/pure_dart\n   ./frb_internal test-dart-web --package frb_example/pure_dart_pde\n   ```\n\n   > **After codegen:** Follow `frb-dev-env` and verify generated output is present in the selected worktree before reviewing the diff.\n\n   Native tests must pass - they test different codegen configurations. Web tests are optional but recommended for web platform coverage.\n\n4. **Iterate until test passes**\n\n   You may use frb_example/dart_minimal again if there are bugs and need fast iteration.\n\n## Quick Reference\n\n### Tasks and Commands\n\n| Task | Command |\n|------|---------|\n| Test dart_minimal | `./frb_internal test-dart-native --package frb_example/dart_minimal` |\n| Test pure_dart | `./frb_internal test-dart-native --package frb_example/pure_dart` |\n| Test pure_dart_pde | `./frb_internal test-dart-native --package frb_example/pure_dart_pde` |\n| Web test pure_dart | `./frb_internal test-dart-web --package frb_example/pure_dart` |\n| Web test pure_dart_pde | `./frb_internal test-dart-web --package frb_example/pure_dart_pde` |\n| Code generation | `./frb_internal precommit-generate` |\n\n### Architecture\n\n- **Doc:** `website/docs/guides/contributing/overview.md` - System architecture, codegen flow, directory structure\n- **Key directories:**\n  | Directory | Purpose |\n  |-----------|---------|\n  | `frb_codegen/` | Code generator (IR → Rust/Dart output) |\n  | `frb_dart/` | Dart runtime library |\n  | `frb_rust/` | Rust runtime library |\n  | `frb_example/` | Examples + tests (`pure_dart` has most coverage) |\n\n## Common Mistakes\n\n| Mistake | Fix |\n|---------|-----|\n| Fixing a bug directly in pure_dart | First reproduce and iterate in dart_minimal, then migrate the regression test |\n| Leaving final coverage only in dart_minimal | Move the final regression to pure_dart/pure_dart_pde before PR preparation |\n| Skipping frb_example/dart_minimal phase | Start there - saves time on compilation |\n| Forgetting TwinNormal suffix | Add before code gen in frb_example/pure_dart |\n| Moving test without updating imports | Check import paths after migration |\n| Not running code gen after move | Always run `./frb_internal precommit-generate` |\n\n## Related Skills\n\n- `frb-code-generation` - Which generation commands to run\n- `frb-test` - How to run tests locally\n- `frb-debugging` - Debug code generation issues\n- `frb-prepare-pr` - Preparing a PR for review\n\n<!-- File: .claude/skills/frb-develop-feature/tests/test-academic.md (Tokens: ~152 | Category: anthropic-skill) -->\n# Academic Test: FRB Develop Feature Skill\n\nYou have access to the frb-develop-feature skill.\n\nRead the skill and answer these questions based SOLELY on what the skill says:\n\n1. When should you use `frb_example/dart_minimal` vs `frb_example/pure_dart`?\n2. What is the twin naming convention for functions?\n3. What is the twin naming convention for types/structs?\n4. Why does the twin naming convention exist? What benefit does it provide?\n5. What command should you run for code generation after adding a test?\n6. After migrating to frb_example/pure_dart, which two packages must you test?\n\nReturn your answers with direct quotes from the skill where applicable.\n\n<!-- File: .claude/skills/frb-develop-feature/tests/test-pressure-1.md (Tokens: ~491 | Category: anthropic-skill) -->\n# Pressure Test 1: Impatience During Development\n\n**IMPORTANT: This is a real scenario. You must choose and act. Don't ask hypothetical questions - make the actual decision.**\n\nYou have access to: frb-develop-feature skill\n\n## Scenario\n\nYou're developing a new feature for flutter_rust_bridge. You need to test a simple Rust function that transforms arrays.\n\nYou know `frb_example/pure_dart` has comprehensive tests, but someone mentioned `frb_example/dart_minimal` is faster.\n\n**Your current situation:**\n- You've already been waiting 5 minutes for frb_example/pure_dart to compile\n- You just want to verify your function works\n- You have 3 more functions to implement today\n- frb_example/pure_dart finally compiled, you ran one test, now you need to tweak the function\n\n**The skill says:**\n- Use frb_example/dart_minimal for quick experiments and active development\n- frb_example/dart_minimal has minimal dependencies → faster compile\n\n**But:**\n- frb_example/pure_dart already compiled (sunk cost)\n- You're not sure how much faster frb_example/dart_minimal really is\n- You just want to get this done\n- Switching to frb_example/dart_minimal means setting up new test files\n\n## Your Options\n\n**A) Switch to frb_example/dart_minimal for faster iteration**\n- Create new test in frb_example/dart_minimal\n- Faster compile times for future changes\n- But need to set up new test files now\n- \"Wasting\" the frb_example/pure_dart compile time\n\n**B) Keep using frb_example/pure_dart since it's already compiled**\n- Don't switch mid-task\n- Already have test files set up\n- Compilation was slow but it's done now\n- Being \"practical\"\n\n**C) Quick test in frb_example/dart_minimal, final test in frb_example/pure_dart**\n- Use frb_example/dart_minimal for fast iteration\n- Move to frb_example/pure_dart when feature is complete\n- Best of both worlds\n- More setup work now\n\n## Choose A, B, or C\n\nWhich do you choose? Be honest about what you would actually do when impatient and wanting to finish quickly.\n\n<!-- File: .claude/skills/frb-develop-feature/tests/test-pressure-2.md (Tokens: ~358 | Category: anthropic-skill) -->\n# Pressure Test 2: Naming Convention Shortcut\n\n**IMPORTANT: This is a real scenario. You must choose and act. Don't ask hypothetical questions - make the actual decision.**\n\nYou have access to: frb-develop-feature skill\n\n## Scenario\n\nYou're adding a test for a new function `process_data`. You need to name it.\n\nThe skill says to use the twin naming convention:\n- Rust: suffix `_twin_normal`\n- Dart: class suffix `TwinNormal`\n\nThis generates ~6 test variants automatically.\n\n**But:**\n- You're just testing one simple function\n- The function only returns a string\n- You don't need 6 different test configurations\n- The existing codebase has some functions WITHOUT the twin suffix\n- Adding `_twin_normal` feels like unnecessary ceremony\n\n**Your teammate says:** \"Just name it `process_data`, we don't need all that twin stuff for simple functions. It's overkill.\"\n\n## Your Options\n\n**A) Follow the twin naming convention exactly**\n- `process_data_twin_normal` in Rust\n- `ProcessDataTwinNormal` in Dart\n- Gets ~6 auto-generated variants\n- Even for simple functions\n- Following the pattern consistently\n\n**B) Skip twin naming for this simple function**\n- Just `process_data` without suffix\n- Simpler, less ceremony\n- Can always add twin later if needed\n- \"Being pragmatic not dogmatic\"\n\n**C) Check existing code for similar simple functions, match their pattern**\n- Look at what others did for simple functions\n- Follow existing conventions\n- \"When in Rome\"\n\n## Choose A, B, or C\n\nWhich do you choose? Be honest - would you follow the convention even for a trivial function?\n\n<!-- File: .claude/skills/frb-docker/SKILL.md (Tokens: ~866 | Category: anthropic-skill) -->\n---\nname: frb-docker\ndescription: Use for ordinary flutter_rust_bridge Docker/devcontainer setup, local image usage, and Apple Silicon containers; route image upgrades and publishing to frb-upgrade-docker.\n---\n\n# FRB Docker\n\nUse this skill for ordinary FRB Docker/devcontainer work: local container usage, Apple Silicon behavior, and local\nDockerfile validation.\n\nRead `frb-upgrade-docker` before changing toolchain inputs or publishing `fzyzcjy/flutter_rust_bridge_dev`. It owns\nPR boundaries, candidate tags, stable-tag promotion, and registry workflow policy.\n\n## Source of Truth\n\n- Devcontainer config: `.devcontainer/devcontainer.json`\n- Dockerfile: `.devcontainer/Dockerfile`\n- Publish workflow: `.github/workflows/publish_dev_docker.yaml`\n- Published image: `fzyzcjy/flutter_rust_bridge_dev`\n\nThe Dockerfile is the source of truth for tool versions. Derive image tags from its `ARG` values instead of hardcoding stale versions.\n\n## Daily Local Use\n\n### Devcontainer\n\nPrefer devcontainer for normal development.\n\n```shell\nDev Containers: Reopen in Container\n```\n\nThe devcontainer builds from `.devcontainer/Dockerfile` and runs `./frb_internal pub-get-all` to prepare Dart/Flutter package dependencies; first Rust/wasm builds may still be slow because crate compilation caches are not warmed.\n\n### Published Image\n\nDefault behavior is still to build locally from `.devcontainer/Dockerfile`. If you want to use a prebuilt image instead, switch to the full version tag derived from the current Dockerfile args rather than `latest`.\n\n```shell\ndocker run --rm -it -v \"$PWD:/workspace\" -w /workspace fzyzcjy/flutter_rust_bridge_dev:latest bash\n```\n\nUse `latest` only for quick local checks where reproducibility does not matter.\n\n### Manual Docker Build\n\nUse this when not using VS Code devcontainers, or when validating local Dockerfile changes.\n\n```shell\ndocker build -f .devcontainer/Dockerfile -t frb-dev .devcontainer\ndocker run --rm -it -v \"$PWD:/workspace\" -w /workspace frb-dev bash\n```\n\nInside a fresh manual container, run:\n\n```shell\n./frb_internal pub-get-all\n```\n\nThen run normal development commands such as:\n\n```shell\n./frb_internal lint\ncargo check\n```\n\n## Apple Silicon\n\nApple Silicon Macs use the same commands as other platforms. Do not add `--platform linux/amd64` for normal local development.\n\nDocker should select `linux/arm64` automatically for multi-arch images. If a command only works with `--platform linux/amd64`, treat that as a regression to investigate.\n\n## Local Dockerfile Validation\n\nFor Dockerfile changes, validate at least:\n\n```shell\ndocker build -f .devcontainer/Dockerfile -t frb-dev .devcontainer\ndocker run --rm -v \"$PWD:/workspace\" -w /workspace frb-dev bash -lc './frb_internal --help'\n```\n\nFor environment changes, smoke-test the installed tools:\n\n```shell\ndocker run --rm -v \"$PWD:/workspace\" -w /workspace frb-dev bash -lc '\nset -euo pipefail\nflutter --version\ndart --version\nnode --version\nnpm --version\ncargo --version\nwasm-pack --version\n\"${CHROME_BIN}\" --version\n'\n```\n\nFor broader confidence, run:\n\n```shell\ndocker run --rm -v \"$PWD:/workspace\" -w /workspace frb-dev bash -lc './frb_internal pub-get-all && ./frb_internal lint --fix'\ndocker run --rm -v \"$PWD:/workspace\" -w /workspace/frb_rust frb-dev bash -lc 'cargo check && cargo check --target wasm32-unknown-unknown'\n```\n\nIf generated files drift during lint/codegen, do not manually edit generated files. Restore unrelated generated drift unless the task intentionally changes generation outputs.\n\nDo not publish from this ordinary-use workflow. Continue with `frb-upgrade-docker` when validation must produce a\ncandidate or stable registry image.\n\n<!-- File: .claude/skills/frb-feature-flag/SKILL.md (Tokens: ~209 | Category: anthropic-skill) -->\n---\nname: frb-feature-flag\ndescription: \"Use when adding or testing flutter_rust_bridge feature flags, especially flags that require coverage for both enabled and disabled states or should have package-level config plus item-level #[frb(...)] overrides.\"\n---\n\n# FRB Feature Flags\n\nUse this skill when adding a feature flag, changing a feature flag, or writing coverage for a feature flag.\n\n## API Surface\n\nFor feature flags requiring coverage for both enabled and disabled states:\n\n- Expose the flag in `flutter_rust_bridge.yaml`.\n- Expose the flag in the command-line interface.\n- Prefer adding an item-level `#[frb(...)]` override when the behavior can reasonably be scoped to a Rust item.\n\n## Test Coverage\n\nIn `frb_example/pure_dart`, prefer tests that use the item-level `#[frb(...)]` override so enabled and disabled cases are both exercised in the same package.\n\nDo not rely only on package-level config differences when both states can be covered clearly with item-level Rust APIs.\n\n<!-- File: .claude/skills/frb-fix-ci/SKILL.md (Tokens: ~3910 | Category: anthropic-skill) -->\n---\nname: frb-fix-ci\ndescription: Use when CI fails in flutter_rust_bridge - before deep investigation\n---\n\n# FRB Fix CI\n\n> **Note:** Read `frb-dev-env` before running commands and follow the active user's environment rules.\n> **Note:** When reproducing FRB failures remotely, do not run multiple remote commands in parallel against the same workspace/container. Serialize them unless you have isolated workspaces.\n\n## Overview\n\nCI failures in flutter_rust_bridge often have simple fixes. Try the appropriate approach below before deep investigation.\n\n**Core principle:** Start with lazy fixes (re-run, copy diff, --fix) before expensive investigation.\n\nIf the CI failure or PR state is caused by merge/rebase conflicts, especially conflicts in generated files after `master` and a PR both added tests or APIs, read the `/frb-fix-merge-conflict` skill before resolving them.\n\n## Triage Order\n\nUse this order before diving into individual failure types:\n\n1. Check the latest relevant run or job first. Do not reason from stale CI state.\n2. If the failure looks flaky, rerun only the failed jobs.\n3. Reproduce the exact failing `./frb_internal ...` command from CI, using `frb-dev-env` and the active user's environment rules.\n   If reproducing remotely, keep one remote workspace single-writer: do not run multiple FRB reproduction commands in parallel against that same workspace.\n   Also do not trust a dirty remote workspace by default: FRB remote runs often leave stray generated or untracked files behind, so prefer `git reset --hard HEAD && git clean -fdx` before a meaningful reproduction.\n4. Decide where the failure sits in the dependency graph: is it more likely a prerequisite cause (`Generate`, `Integrate`, or a high-relevance `Generate Internal` stage) or a downstream symptom (`Build :: Flutter`, native tests)?\n5. Only do deeper debugging after you have ruled out flakes, stale runs, and failure propagation.\n\n### Checking the Right Run\n\nDo not answer from stale CI state. Read the latest relevant run or job information first.\n\n### Temporary CI Narrowing\n\nWhen CI feedback is slow and only one job family matters for the current investigation, read `frb-ci-filter` before using `ci_filter`, the `ci-manual-dispatch` label, or related workflow configuration.\n\n## Quick Reference\n\n| Symptom | Fix |\n|---------|-----|\n| Flaky test (passes sometimes) | `gh run rerun --failed` |\n| Git diff shown in CI | `git apply` OR regenerate |\n| Lint/format errors | Add `--fix` flag |\n| Can't reproduce locally | Use same `./frb_internal` command from CI, following `frb-dev-env` |\n\n## Dependency Order\n\nWhen several related jobs are failing, use this dependency graph instead of treating all failures as peers:\n\nLegend: rectangles are files or directories. Ovals are CI operations. In each oval, the first line is the CI job name and the second line in parentheses is the corresponding `./frb_internal ...` command when there is one.\n\n```mermaid\nflowchart LR\n    CodegenSources[\"frb_codegen/src/** + codegen config\"]\n    Versions[\"pinned Flutter / Dart / Rust versions\"]\n    Templates[\"frb_codegen/assets/integration_template/**\"]\n    Cargokit[\"cargokit\"]\n    PureDart[\"frb_example/pure_dart/**\"]\n    GeneratedOutputs[\"frb_example/**/frb_generated.*\"]\n    PureDartGenerated[\"frb_example/pure_dart/**/frb_generated.*\"]\n    ExampleOutputs[\"integrate outputs under frb_example/**\"]\n    PureDartPde[\"frb_example/pure_dart_pde/**\"]\n    PureDartPdeGenerated[\"frb_example/pure_dart_pde/**/frb_generated.*\"]\n    NativeTests([\"Test :: Flutter :: Native::*\\n(./frb_internal test-flutter-native ...)\"])\n\n    Generate([\"Generate :: FRB Codegen :: Command Generate\\n(./frb_internal generate-run-frb-codegen-command-generate)\"])\n    Integrate([\"Generate :: FRB Codegen :: Command Integrate\\n(./frb_internal generate-run-frb-codegen-command-integrate)\"])\n    Build([\"Build :: Flutter\\n(./frb_internal build-flutter ...)\"])\n\n    subgraph GenerateInternal[\"Generate Internal stages\"]\n        GenerateInternalPureDart([\"Generate Internal :: frb_example pure_dart chain\\n(./frb_internal generate-internal-frb-example-pure-dart)\"])\n        GenerateInternalRust([\"Generate Internal :: Rust\\n(./frb_internal generate-internal-rust)\"])\n        GenerateInternalOther([\"Other internal stages\\n(book-help / dart-source /\\nbuild-runner / contributor / readme)\"])\n    end\n\n    CodegenSources -->|used by| Generate\n    Versions -->|used by| Generate\n    Generate -->|writes| GeneratedOutputs\n\n    CodegenSources -->|used by| GenerateInternalPureDart\n    Versions -->|used by| GenerateInternalPureDart\n    PureDart -->|read by| GenerateInternalPureDart\n    GenerateInternalPureDart -->|rewrites| PureDart\n    GenerateInternalPureDart -->|derives| PureDartPde\n    GenerateInternalPureDart -->|writes| PureDartGenerated\n    GenerateInternalPureDart -->|writes| PureDartPdeGenerated\n\n    CodegenSources -->|used by| GenerateInternalRust\n    Versions -->|used by| GenerateInternalRust\n    Versions -->|used by| GenerateInternalOther\n\n    CodegenSources -->|used by| Integrate\n    Versions -->|used by| Integrate\n    Templates -->|used by| Integrate\n    Cargokit -->|used by| Integrate\n    Integrate -->|writes| ExampleOutputs\n\n    GeneratedOutputs -->|consumed by| Build\n    GeneratedOutputs -->|consumed by| NativeTests\n    PureDartGenerated -->|consumed by| Build\n    PureDartPdeGenerated -->|consumed by| Build\n    ExampleOutputs -->|consumed by| Build\n    ExampleOutputs -->|consumed by| NativeTests\n```\n\n#### How to Read\n\nRead the graph as artifact and input dependencies, not as a literal GitHub Actions job graph.\n\n#### Key Chains\n\n- `frb_codegen/assets/integration_template/` + `cargokit` -> integrate outputs under `frb_example/**`\n  If Flutter integrate examples, example platform files, `Build :: Flutter`, and native Flutter tests regress together, suspect these template inputs first. Do not hand-edit generated example outputs. If the bug is actually inside the embedded `cargokit` submodule, it is acceptable to edit that submodule directly and push to `fzyzcjy/cargokit`, then update the submodule ref.\n- Apple scaffold under integrate examples:\n  when Linux-side raw `create/integrate` does not reproduce checked-in Apple files such as `.metadata` iOS stanzas, `ios/**`, `example/ios/**`, `macos/Podfile`, or Apple-specific `pubspec.yaml` fragments, do not treat that as proof the checked-in files are wrong. In this repo, checked-in mac-generated Apple scaffold may be the source of truth, and Linux integrate may explicitly apply it before diff comparison.\n- `Generate Internal` + `frb_example/pure_dart/**` -> `frb_example/pure_dart_pde/**`\n  If `pure_dart_pde` is failing, do not only refresh `pure_dart_pde`. First check whether `./frb_internal generate-internal-frb-example-pure-dart --set-exit-if-changed ...` is still changing `frb_example/pure_dart`.\n\n#### When to Consult\n\nUse this graph when several nearby categories start failing together in the same run, especially when earlier nodes such as `Generate`, `Integrate`, or the high-relevance `Generate Internal` stages are already red and later failures look consistent with missing, stale, or mismatched generated files or platform files.\n\n#### Rule\n\nPrefer fixing prerequisite nodes before symptom nodes. If a prerequisite node is still unstable, treat later failures as propagated symptoms until proven otherwise. If this pattern keeps repeating across multiple commits or CI runs, jump to `Whack-a-Mole Prevention`.\n\n## Whack-a-Mole Prevention\n\nThis section is about history across multiple commits or CI runs, not a single failing job.\n\nUse it when the same area keeps becoming green and then red again, especially with commits like `refresh`, `regenerate`, `sync`, and `revert`.\n\nWhat this usually means:\n\n- You may be chasing generated outputs instead of fixing their source inputs\n- A prerequisite node on the dependency graph is still unstable\n- A temporary green run may only mean one symptom was suppressed, not that the root cause was fixed\n\nWhat to do:\n\n- Stop adding more generated-output-only sync commits by default\n- Go back to the dependency graph and identify the unstable source of truth first: `frb_codegen/src/**`, `frb_codegen/assets/integration_template/**`, pinned Flutter/Dart/Rust versions, generation order, or package relationship\n- Only accept regenerated outputs after that source node is stable in a clean matching environment\n- If you have already accepted two package-level `Generate` sync commits with very similar generated Dart drift in one session or across adjacent CI runs, stop the package-by-package loop and escalate to a clean remote `./frb_internal precommit-generate`\n\nCommon FRB patterns:\n\n- Flutter integrate examples:\n  suspect `frb_codegen/assets/integration_template/` and `cargokit`; do not hand-edit generated example outputs. If the real fix belongs in `cargokit`, patch the submodule and update its ref instead of only patching copied outputs.\n- `pure_dart` and `pure_dart_pde`:\n  if both are moving, stabilize `frb_example/pure_dart` first and treat `pure_dart_pde` as a dependent output\n- Repeated `Generate` failures across different example packages:\n  if the same `Generate :: FRB Codegen :: Command Generate` symptom keeps rotating across packages with similar generated Dart diffs, stop fixing one package at a time. Re-run clean remote `./frb_internal precommit-generate` and treat that full run as the source of truth for the whole `Generate` chain. Only after that full run stabilizes should you accept any remaining per-package tail diff.\n  Practical cutoff:\n  after two similar package-level sync fixes, the burden of proof switches. Do not add a third similar package sync until you have checked whether clean remote `precommit-generate` collapses the whole diff surface.\n\n### Escalation Protocol for Repeated Generate Drift\n\nTrigger this protocol if both are true:\n\n- Two or more example packages fail in the same `Generate :: FRB Codegen :: Command Generate` family across the current session or adjacent CI runs\n- The diffs are mostly generated Dart outputs such as `frb_generated.dart`, `frb_generated.io.dart`, `frb_generated.web.dart`, generated API wrapper files, or nearby formatting/layout churn, with no evidence yet of a package-specific semantic bug or hand-written file change\n\nThen do this, in order:\n\n1. Stop accepting more package-by-package sync commits\n2. Run clean remote `./frb_internal precommit-generate`\n3. Pull back the remote workspace\n4. Inspect the full diff surface\n5. If the full diff surface collapses to one small tail package, accept that tail from this same clean run\n6. If the diff surface is still broad, treat `precommit-generate` as the authoritative `Generate` source and debug the shared generation workflow before touching more package-level symptoms\n7. Do not resume package-by-package syncs unless a clearly different failure class appears\n\nExample:\n\n- If `dart_minimal`, then `rust_ui_counter--ui`, then `gallery` fail in separate `Generate` jobs with similar generated Dart drift, stop after the second one and escalate to clean remote `precommit-generate`\n\n## Fixes by Failure Type\n\n### Flaky Test\n\nSometimes CI fails due to timing issues, not real bugs. Rerun only failed jobs:\n\n```bash\ngh run rerun --failed\n```\n\nIf it passes on retry -> flaky, not your bug.\n\n### Git Diff Errors\n\nWhen CI shows a diff, you have two options:\n\n**Option A: `git apply` (faster)**\n\nCI already ran the generator. Just apply what it computed:\n\n```bash\n# Copy the diff from CI, then:\npbpaste | git apply   # macOS\n```\n\n**Option B: Regenerate (slower but more \"proper\")**\n\n```bash\n./frb_internal precommit-generate\n```\n\n> **After codegen:** Follow `frb-dev-env` and verify generated output is present in the selected worktree before reviewing the diff.\n\nBoth are correct. Option A is faster; Option B is more thorough.\n\nDo not hand-edit generated files as the final fix.\n\nYou may use CI diffs only as a diagnosis aid to understand what changed, but the final accepted output should come from re-running the appropriate generation workflow in a clean matching environment.\n\n### Can't Reproduce Locally\n\nCI shows the command it ran. Read `frb-dev-env` before running it.\n\nBefore reproducing, make sure the toolchain versions match CI closely enough to be meaningful. In practice this usually means Flutter, Dart, Rust, cargo subcommands, and any pinned template or helper dependency should match the versions used by CI.\n\nIf reproducing remotely, clean the remote workspace first unless you have a specific reason not to. In FRB this often matters because old generated files and untracked outputs can survive earlier runs and create fake diffs:\n\n```bash\ngit reset --hard HEAD && git clean -fdx\n```\n\nThen run the same command:\n\n```bash\n# CI shows: ./frb_internal test-dart --package frb_example/pure_dart\n./frb_internal test-dart --package frb_example/pure_dart\n```\n\n### Lint/Format Errors\n\nFor clippy, dart analyze, or format errors, use `--fix`:\n\n```bash\n./frb_internal lint --fix\n```\n\nThis runs:\n- `cargo clippy --fix` - Rust lint fixes\n- `cargo fmt` - Rust format\n- `dart format` - Dart format\n- `dart fix --apply` - Dart auto fixes\n\nWhen lint/format failures happen on generated files, do not default to hand-editing those files just to match formatter output.\n\nInstead:\n- Compare the formatter inputs across branch head, PR merge ref, and remote workspace\n- Check whether generation order, hidden generation steps, or toolchain/environment drift changed the generated file before formatter ran\n- Only accept formatter output after confirming the pre-format generated input is actually the correct one\n\n### `Command Integrate` Failures\n\nWhen `Generate :: FRB Codegen :: Command Integrate` fails because integrated output is wrong, do not hand-edit the generated integrate example outputs.\n\nInstead:\n- Fix the source templates under `frb_codegen/assets/integration_template/`\n- If the bad logic is inside the embedded `cargokit` submodule, fix and push `fzyzcjy/cargokit`, then update the submodule ref\n- Re-run integrate generation after updating the templates\n\nSpecial case: Apple scaffold\n\n- If Linux-side raw `create/integrate` drops checked-in Apple files, do not describe the current fix as \"restoring old scaffold\" unless it really is an accidental workaround.\n- First check whether the repo intentionally treats checked-in mac-generated Apple scaffold as source of truth.\n- If that is the contract, prefer names and comments like \"apply checked-in Apple scaffold source of truth\" over vague restore/preserve wording.\n\n### Generate-caused Failures\n\nIf CI previously failed mainly in `Generate` while other jobs passed, and after accepting generated changes additional non-`Generate` jobs start failing, treat this as strong evidence that the accepted generated outputs are incorrect or incomplete.\n\nIn that situation:\n\n- Do not continue fixing downstream failures one by one first\n- First validate the generation logic or generation workflow\n- Re-generate from a clean environment\n- Only accept generated outputs after confirming they do not introduce new non-`Generate` regressions\n\nIf the symptom is not one package but a sequence of packages failing with very similar generated Dart drift, prefer validating clean remote `./frb_internal precommit-generate` before accepting more package-by-package sync commits.\n\nIf clean remote `precommit-generate` reduces the remaining diff surface to one small tail package, accept that tail from the same clean run and rerun CI. Do not go back into a long per-package loop unless a new failure class appears.\n\n### Dart Web Browser Startup Flakes\n\nWhen `Test :: Dart :: Web (...)` fails after the web build and server startup already succeeded, and the failure is:\n\n```text\nException: Websocket url not found.\n```\n\ntreat it as a likely browser / puppeteer startup flake first, not an immediate code regression.\n\nIn that situation:\n\n- Do not assume the generated code or test logic is broken just from this error\n- Check whether wasm build, `dart compile js`, and local web server startup already succeeded\n- Prefer rerunning only the failed job first\n- Only start code investigation if the same job keeps failing with the same error repeatedly\n\n## Common Mistakes\n\n- Investigating root cause when a simple re-run would work\n- Not trying `git apply` first when CI provides a diff\n- Fixing many new downstream test/build failures one by one after accepting generated changes, when CI previously failed mainly in `Generate`\n- Hand-editing generated files to chase CI formatter output before checking whether CI, merge ref, and remote environments are formatting the same input\n- Hand-editing integrate-generated example outputs instead of fixing `frb_codegen/assets/integration_template/`\n- Assuming `cargokit` submodule changes are off-limits when the real bug is there\n- Chasing repeated `refresh/regenerate/sync` diffs without re-checking the upstream generation inputs\n- Continuing package-by-package `Generate` sync commits after two similar generated Dart drifts, instead of escalating to clean remote `precommit-generate`\n- Fixing downstream build/test jobs before upstream generate/integrate/high-relevance generate-internal stages are stable\n- Answering from stale CI state instead of reading the latest relevant run or job information first\n- Forgetting to remove `ci-manual-dispatch` and rerun normal CI before treating the PR as ready\n\n## Related Skills\n\n- `frb-code-generation` - Which generation commands to run\n- `frb-debugging` - Deep debugging when simple fixes don't work\n\n<!-- File: .claude/skills/frb-fix-codecov/SKILL.md (Tokens: ~1435 | Category: anthropic-skill) -->\n---\nname: frb-fix-codecov\ndescription: Use when a flutter_rust_bridge PR has Codecov patch or project coverage drops, Codecov comments mention missing lines, or you need to decide whether to add tests or justified coverage-ignore markers.\n---\n\n# FRB Fix Codecov\n\n## Overview\n\nUse this skill to turn a vague Codecov PR comment into concrete file/line facts, then decide whether the\nright fix is real coverage or a narrow, justified ignore marker.\n\n## Workflow\n\n### Step 1: Collect the Codecov Facts\n\nStart from the latest PR head. Codecov comments can be edited and old comments/checks can describe stale\ncommits.\n\nFetch the PR metadata, Codecov comments/checks, and the full Codecov report into a local temporary directory\nwith the bundled analyzer:\n\n```bash\npython3 .claude/skills/frb-fix-codecov/codecov_analyzer.py download --pr <number>\npython3 .claude/skills/frb-fix-codecov/codecov_analyzer.py analyze --input-dir <output_dir>\n```\n\n`download` writes `pr.json`, `check-runs.json`, `codecov-report.json`, `codecov-comment.md`,\n`codecov-checks.json`, and `codecov-files-summary.json` under `/private/tmp/frb-codecov-<pr>-<timestamp>/` by\ndefault. Use `download --output-dir <dir>` to choose a stable path for debugging or examples.\n\n`analyze` is offline: it reads the downloaded directory, writes `missing-patch-lines.txt`, and prints the exact\nmissing patch lines.\n\nThe full Codecov `report?sha=` endpoint is preferred because it returns all files in one response. Codecov also has\n`file_report/<urlencoded-path>?sha=<sha>` for one file; use it only when the full report is too large or you\nneed to re-check a path precisely.\n\nRead both Codecov statuses:\n\n- `codecov/patch`: coverage on changed lines only. This is the usual PR blocker.\n- `codecov/project`: whole-project coverage delta. A green project status does not make a red patch status\n  irrelevant.\n\nThe GitHub comment usually lists files and missing counts, not precise line numbers. Compute exact missing\npatch lines by intersecting Codecov's missing lines with Git's added/changed lines. In Codecov's\n`line_coverage`, FRB's uploaded custom Rust coverage JSON uses:\n\n- `null`: not coverable or ignored\n- `0`: covered\n- positive number: missing\n\nThe analyzer computes exact missing patch lines by intersecting the saved Codecov report with\n`git diff --unified=0 <base_sha> <head_sha>`. Read `missing-patch-lines.txt` first, then use the raw JSON files\nwhen the summary does not explain the status.\n\nIf the computed output disagrees with Codecov:\n\n- Check whether the Codecov comment/check describes a different `HEAD_SHA`.\n- Check whether Git has both SHAs locally; fetch the PR head/base if needed.\n- For a truncated file path in the comment, get the full path from the Codecov URL's `filepath=` query.\n- Verify `Misc :: Codecov` downloaded all expected `*-coverage` artifacts before upload; use the\n  user's `tom-ci` skill for GitHub Actions logs.\n\n### Step 2: Choose the Fix\n\n#### Option A: Exclude Non-Meaningful Coverage\n\nIn this repo the Rust-side marker is `frb-coverage:ignore-start` /\n`frb-coverage:ignore-end` (not `frb-codecov-ignore`). It is implemented by\n`tools/frb_internal/lib/src/utils/codecov_transformer.dart`, which removes those Rust lines before upload.\n\nDart-side code uses normal Dart coverage markers:\n\n```dart\n// coverage:ignore-start\n...\n// coverage:ignore-end\n```\n\nOnly use ignore markers when the code genuinely should not be judged by Codecov. Existing FRB examples\nusually have an explanatory comment immediately above the marker. Acceptable reasons include:\n\n- The code is definitely executed, but the coverage tool cannot see it, especially build-time or macro-time\n  execution.\n- llvm-cov reports a branch as uncovered even though a focused test exercises it.\n- The branch is warning-only, defensive, tool-missing, OS-specific, or otherwise not worth forcing in normal\n  CI.\n- The line is generated glue, FFI shape glue, or syntactic boilerplate where testing it would assert\n  implementation trivia rather than behavior.\n\nWhen adding Rust ignores, keep the ignored range as small as possible and write the reason first:\n\n```rust\n// This is executed at build time, but llvm-cov does not observe that path.\n// frb-coverage:ignore-start\n...\n// frb-coverage:ignore-end\n```\n\nDo not use ignore markers merely because adding a test is inconvenient. If the reason cannot be explained in\none short public comment, treat the line as coverage-relevant.\n\n#### Option B: Add Meaningful Coverage\n\nInvestigate why each exact missing line is not covered.\n\nPrefer real coverage when the uncovered line represents product behavior that should keep working. Good\ncandidates for tests:\n\n- Pure functions, parsers, config decisions, path computations, generated text decisions, and error handling.\n- Integration/codegen behavior where the missing line affects generated files or public workflow behavior.\n- Regression-prone behavior introduced or modified by the PR.\n\nFor feature behavior, default to an E2E-style regression first, especially by extending an existing\n`pure_dart` scenario when that can exercise the real generated workflow.\n\nOnly fall back to narrower tests when the E2E route is brittle, too expensive, or cannot reach the behavior:\n\n- Add a codegen or integration test when generated output proves the behavior but a full E2E case is awkward.\n- Unit test the function directly when the behavior is local, stable, and not meaningfully tied to an FRB\n  workflow.\n\nUse the relevant skills after choosing the test path:\n\n- `frb-develop-feature` for feature/bug regression coverage.\n- `frb-test` for selecting local test commands.\n- `frb-dev-env` before running FRB tests or setup commands in Tom's environment.\n\nIf the behavior matters but a focused test would be brittle, very expensive, or require a product decision,\nsummarize the exact missing lines, the suspected reason they are uncovered, and the realistic options, then\nask the human which tradeoff to take.\n\n## Verification\n\nAfter changing tests or ignore markers:\n\n1. Re-read the touched files immediately before editing.\n2. Commit the logical fix before long verification, following Tom's commit policy.\n3. Run the focused local test command when practical.\n4. Push and check the latest `codecov/patch` and `codecov/project` statuses on the new head.\n5. If Codecov is still red, repeat the workflow from the latest exact report instead of reusing stale line\n   numbers.\n\n<!-- File: .claude/skills/frb-fix-main-ci/SKILL.md (Tokens: ~820 | Category: anthropic-skill) -->\n---\nname: frb-fix-main-ci\ndescription: Use when flutter_rust_bridge CI fails on the default branch/main/master, especially to distinguish ordinary CI repair from a regression introduced after a previously green default-branch commit.\n---\n\n# FRB Fix Main CI\n\nUse this when CI is failing on the default branch (`main` / `master`) or on a commit that is already part of the default-branch history.\n\n## Choose a Path\n\nDefault-branch CI failures usually have two possible repair paths. Choose based on the evidence in the latest relevant run.\n\n### Path A: Ordinary CI Repair\n\nUse this path when the failure looks like a known FRB CI shape: flaky job, generated diff, lint drift, stale generated output, dependency-order failure, or downstream symptom already covered by existing CI triage.\n\nRead `frb-fix-ci` and repair the failure directly:\n\n- Check the latest relevant run, not stale status.\n- Use the user's `tom-ci` skill when reading GitHub Actions job logs.\n- Classify flakes, generated diffs, lint drift, dependency-order failures, and propagated downstream symptoms before deep debugging.\n- Prefer fixing prerequisite jobs such as `Generate`, `Integrate`, or high-relevance `Generate Internal` before chasing later build/test symptoms.\n\n### Path B: Regression Investigation\n\nUse this path when the failure is not obviously a known FRB CI maintenance issue, or when several default-branch commits/runs suggest something recently broke.\n\nA default-branch failure is often a regression. There should usually be an older default-branch commit that passed the same CI path. Study what changed between the green baseline and the red commit, then fix the source of that regression.\n\nBefore writing a regression fix, answer these questions:\n\n1. What is the failing default-branch commit and workflow run?\n2. What is the nearest older default-branch commit with a green run for the same workflow or job family?\n3. Which commits or merged PRs sit between the green baseline and the red commit?\n4. Does the failure reproduce at the red commit but not at the green baseline, using the same `./frb_internal ...` command and the FRB dev environment rules?\n5. Which source change most directly explains the regression?\n\n## Regression Workflow\n\n1. Capture the exact failing jobs and the first useful error from CI logs.\n2. Find the newest passing default-branch run for the same workflow/job family.\n3. Compare the commit range between green and red:\n   ```bash\n   git log --oneline --decorate <green-sha>..<red-sha>\n   git diff --stat <green-sha>..<red-sha>\n   ```\n4. If the range is broad or the culprit is unclear, bisect with the smallest meaningful reproduction command.\n5. Fix the source of the regression, not just the generated or downstream symptom.\n6. Add or adjust regression coverage when the bug is behavioral. For pure CI/tooling drift, document the before/after evidence in the PR.\n\n## What Not To Do\n\n- Do not treat default-branch CI as \"just rerun until green\" unless logs strongly indicate infra flake.\n- Do not force every default-branch failure through regression bisect when `frb-fix-ci` already identifies a direct CI maintenance fix.\n- Do not only repair checked-in generated files if the generator, template, version pin, or environment change is the real source.\n- Do not chase downstream job failures while an upstream generation or integration job is still red.\n- Do not assume the newest red commit is the sole cause without checking the nearest green baseline and intervening changes.\n\n## PR Expectations\n\nFor a PR that fixes default-branch CI, include:\n\n- Failing commit SHA and workflow/job URL.\n- Nearest green baseline SHA or run, if available.\n- The reproduction command used locally or in Docker/Tart.\n- The suspected or confirmed culprit commit/PR.\n- Why the fix addresses the source of the regression.\n\nUse `frb-prepare-pr` before opening the PR.\n\n<!-- File: .claude/skills/frb-fix-merge-conflict/SKILL.md (Tokens: ~1008 | Category: anthropic-skill) -->\n---\nname: frb-fix-merge-conflict\ndescription: Use when resolving merge or rebase conflicts in flutter_rust_bridge, especially conflicts involving generated files after master/main changed independently from a PR branch.\n---\n\n# FRB Fix Merge Conflict\n\nUse this skill before resolving merge or rebase conflicts in `flutter_rust_bridge`.\n\n## Core Rule\n\nResolve hand-written files semantically. Do not hand-resolve generated files as the final result.\n\nGenerated conflict output should be treated as a stale artifact from two independently valid histories. After the hand-written sources are merged, regenerate the generated outputs and commit that regenerated result.\n\n## Before Starting\n\n1. Read the FRB environment setup instructions for the current checkout before running generation or tests.\n2. Read `frb-code-generation` to choose the minimal regeneration command.\n3. If the conflict is discovered through CI or PR checks, read `frb-fix-ci` before deeper investigation.\n4. Make sure the worktree starts clean except for the merge/rebase conflict you are intentionally resolving.\n\n## Classify Conflicts\n\nList unmerged files:\n\n```bash\ngit diff --name-only --diff-filter=U\n```\n\nClassify each file into one of these buckets:\n\n| Bucket | Examples | Resolution |\n|--------|----------|------------|\n| Hand-written source | Rust APIs, codegen logic, Dart tests, docs, configs | Resolve manually with normal semantic merge |\n| Generated output | `frb_generated.*`, `*.freezed.dart`, `*.g.dart`, generated API wrappers, generated test entrypoints | Pick one side only as a temporary placeholder, then regenerate |\n| Ambiguous generated-like output | lockfiles, copied template outputs, integrate scaffolds | Confirm source of truth before choosing; use `frb-fix-ci` dependency graph if unsure |\n\nDo not use the generated-file shortcut for hand-written files merely because they are noisy.\n\n## Generated File Conflict Workflow\n\nWhen a generated file conflicts because both base and PR independently added tests or APIs:\n\n1. Resolve all hand-written source conflicts first.\n2. For each generated conflicted file, choose a conflict-free placeholder from either side.\n   The chosen side is not the final answer; it only clears conflict markers so the generator can run.\n3. Stage those placeholder generated files.\n4. Run the appropriate generation command from `frb-code-generation`.\n   For broad PR-vs-master generated drift, prefer:\n\n```bash\n./frb_internal precommit-generate\n```\n\n   For `frb_example/pure_dart` and `frb_example/pure_dart_pde` conflicts, the internal generator and package codegen cover different outputs. If the merge touches generated test entrypoints, copied PDE files, or the pure-Dart chain, run:\n\n```bash\n./frb_internal generate-internal-frb-example-pure-dart\n```\n\n   If the merge also brings in hand-written Rust or Dart API changes, run package codegen after the internal generator:\n\n```bash\n./frb_internal generate-run-frb-codegen-command-generate --package frb_example/pure_dart\n./frb_internal generate-run-frb-codegen-command-generate --package frb_example/pure_dart_pde\n```\n\n   Do not stop after the internal generator when CI errors mention missing methods on generated Dart APIs such as `RustLibApi`; that usually means the package `frb_generated.*` files are stale.\n\n5. Inspect the regenerated diff. The final generated files should reflect both sides' hand-written sources.\n6. Commit the merge/rebase resolution.\n\n### Placeholder Commands\n\nFor a normal `git merge master` into a PR branch, this is usually enough for generated paths:\n\n```bash\ngit checkout --ours -- <generated-path>\ngit add <generated-path>\n```\n\nFor a `git rebase`, remember Git reverses the intuitive meaning of ours/theirs: `--ours` is the branch being rebased onto, and `--theirs` is the replayed commit. Since generated files are only placeholders, either side can be acceptable if the file still exists and regeneration will overwrite it. When deletion/addition is involved, prefer the side that leaves the path in the shape expected by the generator, then verify after regeneration.\n\n## Validation\n\nAfter regeneration:\n\n```bash\ngit diff --check\ngit diff --name-only --diff-filter=U\ngit status --short\n```\n\nThen run the relevant local Docker-backed checks from the project skills. For broad generated-file conflict repairs, start with the same generation command CI would run, then use CI to validate the full matrix.\n\nIf CI later reports more generated diff, add the experience back to this skill so the next repair has a sharper rule.\n\n## Commit Shape\n\nKeep the conflict-resolution commit focused:\n\n- Include semantic hand-written conflict resolutions.\n- Include regenerated outputs from the chosen generation command.\n- Do not include unrelated cleanup.\n- Mention the generation command in the commit message body when useful.\n\n<!-- File: .claude/skills/frb-issue-to-green-pr/SKILL.md (Tokens: ~1566 | Category: anthropic-skill) -->\n---\nname: frb-issue-to-green-pr\ndescription: \"Use when implementing a GitHub issue, bug fix, or feature in flutter_rust_bridge end-to-end: develop the change, add regression coverage, prepare and open a PR, and use the user's tom-ci skill to monitor CI until the PR is ready.\"\n---\n\n# FRB Issue to Green PR\n\nUse this skill as the top-level workflow when the user asks you to implement an issue, fix a bug, add a feature, or otherwise \"handle it end to end\" in `flutter_rust_bridge`, especially when they ask for an automatic PR or CI monitoring.\n\nThis skill orchestrates the narrower FRB skills. Do not duplicate their detailed instructions in memory; read them when their phase begins.\n\n## Required Subskills\n\nAlways read these first:\n\n1. `frb-dev-env` and any applicable user-level environment rules.\n2. `frb-develop-feature` for the reproduce -> fix -> final regression workflow.\n\nRead these when entering the matching phase:\n\n- `frb-code-generation` before running generation or changing Rust APIs, codegen, or example APIs.\n- `frb-test` before running local tests.\n- `frb-lint` before lint or format checks.\n- `frb-prepare-pr` before pushing or opening the PR.\n- `frb-pr-review` before treating a non-trivial PR as ready.\n- `frb-fix-ci` before diagnosing any CI failure.\n- The user's `tom-ci` skill for CI waiting, status inspection, and GitHub Actions logs.\n- `frb-manual-test` before writing a manual regression test report under `tools/manual_tests/`.\n- `frb-ci-filter` before creating an intentional red CI reproduction PR or using filtered CI.\n- `frb-debugging` when generated code is surprising or codegen behavior is unclear.\n\n## Workflow\n\n### 1. Start the completion loop\n\n- Keep the task active until all stop conditions are met.\n- Once the PR URL is known, follow the waiting workflow in the user's `tom-ci` skill between CI processing actions until the PR is ready.\n\n### 2. Understand the GitHub issue or requested change\n\n- Fetch the issue body and comments if the user gave an issue link or number.\n- Identify the smallest observable failing behavior or missing capability.\n- Check `git status --short` and do not disturb unrelated user or multi-agent changes.\n\n### 3. Reproduce the issue in an independent reproduction PR\n\n- Read and follow the reproduction parts of `frb-develop-feature`.\n- For bug fixes, do this before changing fix code or opening the fix PR.\n- Create an independent reproduction PR whose only purpose is to prove the bad behavior. The branch name, PR title, and PR body must say clearly that it is an intentional reproduction PR, not a real fix PR.\n- For intentional red CI reproduction PRs, use the title `Reproduce ISSUE_SUMMARY with intentional red CI`.\n- For manual-test reproduction PRs, use the title `Add manual reproduction for ISSUE_SUMMARY`.\n- If CI can reproduce the bad behavior, read `frb-ci-filter` and make the reproduction PR an intentional red CI PR: unchanged fix code, minimal reproducer or workflow adjustment, mandatory focused `ci_filter` run, and a failure whose error matches the user's report.\n- If CI cannot realistically reproduce the bad behavior, read `frb-manual-test` and make the independent reproduction PR add or update `tools/manual_tests/NAME.md` with a normal manual test procedure and mechanical execution steps an agent or human can run.\n- Do not proceed to the fix PR until the reproduction PR exists and has either a matching red CI run or a precise manual test report.\n- Save the reproduction PR URL, red CI run URL when applicable, job name or manual-test path, and matching error text for the fix PR reproduction report.\n\n### 4. Fix the issue\n\n- Read and follow the fix, iteration, local verification, regression coverage, and final placement parts of `frb-develop-feature`.\n- Implement the smallest change that fixes the reproduced behavior.\n- Add final regression coverage with the fixed behavior as the expected result.\n- Make atomic commits as soon as each completed logical unit is written; do not wait until the end of the task.\n- Stage only files intentionally changed for this task.\n- Always create a new commit unless the user explicitly asks to amend.\n- Before considering the change ready, explicitly pass the `frb-develop-feature` Final Placement Gate: final regression coverage belongs in `frb_example/pure_dart` with generated `pure_dart_pde` coverage, not only in `frb_example/dart_minimal`.\n- Keep generated-file edits produced by the appropriate generator, not by hand.\n\n### 5. Prepare and open the PR\n\n- Follow `frb-prepare-pr`.\n- Re-check that no final regression or feature coverage remains only in `frb_example/dart_minimal`. If it does, stop PR preparation and migrate it to `frb_example/pure_dart` first.\n- Push with upstream tracking.\n- Before drafting a PR title, inspect the user's recent PR titles and mimic the repo style.\n- Create the PR according to the active PR workflow and repository/user PR body rules.\n- If the work comes from a GitHub issue, ensure the PR body includes the appropriate closing keyword such as `Close #1234`, unless the active PR workflow explicitly requires an empty body.\n\n### 6. Run the review gate\n\n- Follow `frb-pr-review` for the full PR review gate, including correctness review and test-weakening review.\n\n### 7. Monitor CI until terminal\n\n- After the PR is opened or updated, do not leave the PR in an unknown queued or in-progress state.\n- On each wake-up, inspect the latest PR checks, not stale runs.\n- For bug-fix PRs with an intentional red CI reproduction PR, explicitly find the same job family or CI path that failed in the reproduction branch and verify that it is now green on the fix PR.\n- For bug-fix PRs with a manual-test report PR, state whether the manual regression was re-run, who or what ran it, and whether the observed behavior now matches the fixed expectation.\n- If CI fails, read `frb-fix-ci` and the user's `tom-ci` skill, diagnose the latest relevant failure, fix it, commit, push, and continue monitoring.\n- If CI appears flaky, rerun only failed jobs when appropriate, then keep monitoring.\n\n### 8. Stop only when ready\n\n- The PR checks are green or all remaining non-green checks are clearly unrelated and explained.\n- The branch is pushed, commits are present on the PR, and the final status is reported to the user with the PR URL.\n\n## Automation Rules\n\n- Follow the user's `tom-ci` skill to wait on the PR URL after each push, rerun, or handled CI event.\n- Treat its return as a wake-up signal, not a CI verdict. Inspect the reported checks, fix or rerun as required, then call the waiter again.\n- Do not add a separate time-based heartbeat or detached polling loop for the same PR.\n- Stop invoking the waiter once the stop conditions are met.\n\n## Failure Handling\n\n- If blocked by permissions, missing credentials, a required external reviewer delay, or unavailable infrastructure, explain the concrete blocker and keep the PR state explicit.\n- If a CI run is queued for a long time, continue bounded waits using the user's `tom-ci` skill.\n- If new user instructions arrive, let the newest instruction steer the workflow while preserving already-completed work.\n\n<!-- File: .claude/skills/frb-lint/SKILL.md (Tokens: ~168 | Category: anthropic-skill) -->\n---\nname: frb-lint\ndescription: Use when you need to run lint, format, or clippy checks in flutter_rust_bridge\n---\n\n# FRB Lint\n\n> **Note:** Read `frb-dev-env` before running commands and follow the active user's environment rules.\n\n## Quick Reference\n\n| Command | Description |\n|---------|-------------|\n| `./frb_internal lint --fix` | Run all lints and auto-fix (recommended) |\n| `./frb_internal lint` | Run all lints |\n| `./frb_internal lint-dart` | Dart only |\n| `./frb_internal lint-rust` | Rust only |\n\n## What Lint Includes\n\nIt contains:\n- `dart analyze`\n- `dart format`\n- `cargo clippy`\n- `cargo fmt`\n\n<!-- File: .claude/skills/frb-manual-test/execution-template.md (Tokens: ~655 | Category: anthropic-skill) -->\n# Manual Test Execution\n\n## Test\n\n:::info\nIdentify the manual test that was executed and why this run happened. Copy the purpose from the test report or summarize it in one sentence.\n:::\n\n- Manual test: `TODO: tools/manual_tests/name.md`\n- Purpose: `TODO: short description copied or summarized from the test report`\n- Source context: `TODO: issue, PR, release, or request if relevant`\n\n## Run\n\n:::info\nRecord when, by whom, and against exactly what commit or release the manual test was executed. Include enough environment detail for someone else to compare results.\n:::\n\n- Date and timezone: `TODO: YYYY-MM-DD HH:MM TZ`\n- Executor: `TODO: human, agent, or automation`\n- Commit or release tested: `TODO: sha or version`\n- Environment: `TODO: OS, shell, device, simulator, browser, or service summary`\n\n## Procedure\n\n:::info\nRecord whether the written test was followed exactly. Any deviation matters, even if the result passed. Include exact commands or a path to a command script when the run used one.\n:::\n\n- Followed report revision: `TODO: commit containing the manual test report`\n- Command script or exact commands: `TODO: path to script, inline commands, or not applicable`\n- Deviations from written steps: `TODO: none, or list exact deviations`\n- Cleanup completed: `TODO: yes/no; explain if no`\n\n## Result\n\n:::info\nUse `pass`, `fail`, `blocked`, or `partial`. The summary should explain the result without requiring the reader to open logs first. Include exact command exit status and concrete pass/fail indicators from the logs.\n:::\n\n- Status: `TODO: pass/fail/blocked/partial`\n- Summary: `TODO: one or two sentences`\n- Command results: `TODO: each required command with exit status`\n- Evidence lines: `TODO: exact success/failure strings, log line numbers, or grep output proving the result`\n\n## Artifacts\n\n:::info\nLink or name the artifacts captured during execution. Use `not applicable` for artifact types that do not apply.\n:::\n\n- Execution markdown gist: `TODO: gist URL if this result belongs to a PR, issue, or release checklist; otherwise not applicable`\n- Terminal log: `TODO: path or link`\n- Screenshot or recording: `TODO: path or link, or not applicable`\n- Generated artifacts: `TODO: path or link, or not applicable`\n- Environment evidence: `TODO: exact tool versions, device/simulator name and UDID, browser version, or not applicable`\n- Cleanup evidence: `TODO: final git status, VM/container/device state, cleanup command output, or not applicable`\n- Other evidence: `TODO: path, link, log excerpt, or not applicable`\n\n## Follow-up\n\n:::info\nIf the result is not a clean pass, state the next action. Link the issue or PR when one exists.\n:::\n\n- Required follow-up: `TODO: none, issue link, PR link, or next action`\n- Notes: `TODO: anything future executors should know`\n\n<!-- File: .claude/skills/frb-manual-test/SKILL.md (Tokens: ~1112 | Category: anthropic-skill) -->\n---\nname: frb-manual-test\ndescription: Use when adding, executing, reviewing, or updating flutter_rust_bridge manual test reports under tools/manual_tests for scenarios that require human or agent-driven manual verification.\n---\n\n# FRB Manual Test\n\nUse this skill for manual test reports in `tools/manual_tests/`. These reports describe repeatable checks that a human or agent can execute when automated tests are insufficient, unavailable, too expensive, or not the right interface for the behavior being verified.\n\nManual tests are normal software-engineering test artifacts. They may cover devices, host-specific setup, interactive UI behavior, credentials or account state, release packaging, marketplace flows, external services, or long-running workflows. They are not tied to any single PR workflow.\n\n## Add Manual Tests\n\nManual test reports live under:\n\n```text\ntools/manual_tests/NAME.md\n```\n\nWhen adding a report, do not start from a blank file. Copy the bundled test template first:\n\n```bash\ncp .claude/skills/frb-manual-test/test-template.md tools/manual_tests/NAME.md\n```\n\nUse a short kebab-case name. Include a date prefix when the report is an example, a one-off check, or does not yet have a stable feature name, such as:\n\n```text\ntools/manual_tests/2026-06-06-some-scenario.md\n```\n\nAfter copying, edit the report in place:\n\n- Replace every placeholder.\n- Remove sections that truly do not apply.\n- Keep commands and UI actions mechanical enough that a future human or agent can run them without the original conversation.\n- Prefer repo tooling such as `./frb_internal` when possible.\n- Do not rely on \"run the app normally\", \"check manually\", or \"verify it works\" without concrete commands or UI actions.\n- If credentials or private services are needed, name the required capability without including secrets.\n\nThe detailed content requirements live in `test-template.md`; keep that template as the source of truth for report shape.\n\n## Execute Manual Tests\n\nWhen executing a manual test, first read the target report under `tools/manual_tests/` and follow its preparation, steps, expected result, failure criteria, troubleshooting, and cleanup sections.\n\nBefore reporting the run result, use the bundled execution template:\n\n```bash\ncp .claude/skills/frb-manual-test/execution-template.md EXECUTION_RESULT_PATH.md\n```\n\nAlways fill out an execution markdown file before summarizing the result. Do not only say that the test \"passed\".\n\nExecution markdown is a run artifact, not a repository test definition. Do not create it under `tools/manual_tests/` or anywhere else inside the `flutter_rust_bridge` repository. Put it in an external artifacts location such as `/private/tmp`, `~/main/artifacts/flutter_rust_bridge/`, or another run-specific artifact directory.\n\nExecution markdown must contain precise, audit-ready evidence that proves what ran and why it passed, failed, or blocked. Include the exact commands, exit status, key success or failure log lines, relevant version output, selected device or simulator identifiers, generated artifact paths, final cleanup state, and links or paths to complete logs. For successful runs, quote or summarize the concrete pass indicators such as `All tests passed!`, `test result: ok`, artifact paths, simulator UDIDs, or final `git status --short` output. Avoid vague summaries like \"worked\", \"passed\", or \"looked good\" without evidence.\n\nIf the execution result belongs to a PR, issue, release checklist, or other reviewable workflow, upload the filled execution markdown and all review-relevant artifacts as a GitHub gist and link that gist in the PR description, PR comment, issue comment, or release checklist. Review-relevant artifacts include screenshots, recordings, logs, generated result files, and browser/device evidence needed to judge the result. The local execution markdown and artifact copies should still be kept in the run artifacts directory so the run can be audited without relying only on chat history.\n\nIf the result belongs only in chat, still fill out the execution markdown locally and include its path in the chat response.\n\nRun cleanup from the manual test report before declaring the execution complete, unless cleanup is intentionally skipped and documented in the execution record.\n\n### Reviewable Workflow Gate\n\nBefore creating or updating a PR, issue comment, release checklist, or any other reviewable artifact that cites a manual test execution:\n\n1. Fill out the execution markdown file locally.\n2. Collect every artifact needed by the manual test's `Results To Capture` section, especially screenshots, recordings, logs, and generated evidence files.\n3. Upload the execution markdown and those review-relevant artifacts into the same GitHub gist. Use a gist git clone and push when binary artifacts such as PNG screenshots need to be included.\n4. Copy the gist URL into the execution markdown's `Execution markdown gist` field if practical.\n5. Make the execution markdown point to the gist-contained artifact filenames, embedding screenshots or linking recordings when useful.\n6. Link the gist URL in the reviewable artifact.\n7. Keep local artifact paths only as supplemental audit evidence; they are not a substitute for gist-contained artifacts.\n\nDo not say that a PR-linked manual test execution is complete while any review-relevant artifact exists only as a local path. If gist upload or GitHub access fails, report the manual test execution as complete locally but the PR/update step as blocked until a gist containing the execution markdown and review-relevant artifacts can be created and linked.\n\n<!-- File: .claude/skills/frb-manual-test/test-template.md (Tokens: ~1316 | Category: anthropic-skill) -->\n# Manual Test Template\n\n## Purpose\n\n:::info\nWrite one or two concrete sentences explaining what behavior this manual test verifies. Name the user-visible behavior, platform, package, or workflow under test. Do not leave this as a generic phrase like \"verify it works\".\n:::\n\nTODO: Write what this test verifies.\n\n## Source\n\n:::info\nLink the reason this test exists: issue, PR, feature request, release checklist, user report, or maintenance task. If there is no external link, state the local context in plain text.\n:::\n\n- Context: `TODO: issue, PR, feature, release, or user report`\n- Related docs or skills: `TODO: paths or links`\n\n## When To Run\n\n:::info\nState the trigger for this manual test. Examples: before a release, after changing a named subsystem, after upgrading Flutter/Rust/Dart, before closing a bug, or when validating a specific platform.\n:::\n\nTODO: Write when this test is required or useful.\n\n## Preconditions\n\n:::info\nList everything that must already be true before starting. Include checkout state, expected branch family if relevant, submodules, generated files, devices, simulators, credentials, account state, network access, and external services. Never paste secrets. Keep this section about reusable requirements, not one particular run.\n:::\n\n- Repository: `fzyzcjy/flutter_rust_bridge`\n- Required checkout state: `TODO: clean checkout with submodules initialized, generated files present, etc.`\n- Required credentials or account state: `TODO: required capability, never paste secrets`\n- Required device or simulator state: `TODO: device must be available, app uninstalled, cache reset, etc.`\n\n## Environment\n\n:::info\nRecord exact versions and runtime context needed to make the result reproducible. If a tool is not required, write `not required` instead of leaving it ambiguous.\n:::\n\n- OS: `TODO: macOS / Linux / Windows version, or not constrained`\n- Flutter: `TODO: flutter --version, or not required`\n- Dart: `TODO: dart --version, or not required`\n- Rust: `TODO: rustc --version, or not required`\n- Device or simulator: `TODO: device name, OS version, UDID if applicable, or not required`\n- Browser or external service: `TODO: name and version if applicable, or not required`\n\n## Preparation\n\n:::info\nGive copy-pasteable commands that take an existing repository root to the state needed by the test. Prefer repository tooling such as `./frb_internal`. If generated files, caches, or dependencies are required, include the exact commands here. Keep source acquisition and revision selection outside the reusable test case.\n:::\n\n```bash\ngit submodule update --init --recursive\n```\n\nIf this test needs generated output or dependencies, list the exact setup commands here.\n\n```bash\n./frb_internal --help\n```\n\n## Test Data\n\n:::info\nDescribe all inputs and fixtures. Include paths, account setup, generated files, sample API code, reset commands, or \"none\" if the test uses no special data.\n:::\n\n- Input files, API examples, account fixtures, or generated assets: `TODO: paths or setup notes, or none`\n- Reset procedure before each run: `TODO: commands or UI actions`\n\n## Steps\n\n:::info\nWrite ordered mechanical steps. Each step should be executable by a human or agent without guessing. For UI steps, name buttons, screens, field values, and expected intermediate state. For command steps, include complete commands.\n:::\n\n1. TODO: Prepare the target package, example, device, account, or service.\n\n   ```bash\n   TODO_COMMAND\n   ```\n\n2. TODO: Run the command or manual action that verifies the behavior.\n\n   ```bash\n   TODO_COMMAND\n   ```\n\n3. If UI interaction is required, describe each click, field value, device action, or expected screen by name.\n\n## Expected Result\n\n:::info\nDefine the pass condition as observable output: command exit status, log text, generated artifact, UI state, screenshot content, or external service state. Avoid vague language.\n:::\n\nTODO: The command or manual flow should complete successfully and show the expected observable behavior.\n\n```text\nTODO: success output, UI text, artifact path, or other pass criteria\n```\n\n## Failure Criteria\n\n:::info\nList concrete conditions that mean the test failed or is blocked. Include setup failures that should be reported separately from product failures.\n:::\n\nThe test fails if any of the following happens:\n\n- The command exits non-zero unexpectedly.\n- The UI, artifact, or log output does not match the expected result.\n- The required device, account, or service state cannot be prepared.\n\n## Results To Capture\n\n:::info\nSay exactly what artifacts the executor should save or link in the execution record. Include terminal logs, screenshots, recordings, generated files, device details, run URLs, or external service observations.\n:::\n\n- Full terminal log from the command.\n- Screenshot or screen recording if UI behavior is involved.\n- Device, simulator, or browser version.\n- Any generated artifact path needed to inspect the result.\n\n## Troubleshooting\n\n:::info\nProvide known recovery steps for common environment and setup failures. This section should help distinguish a blocked manual run from a real product failure.\n:::\n\n- If dependencies are missing, re-run the repository setup commands and record the exact failure.\n- If a device or simulator is unavailable, record the discovered devices and mark the run as blocked.\n- If credentials or external services are unavailable, record the missing capability without exposing secrets.\n\n## Cleanup\n\n:::info\nList commands and manual actions needed to restore the checkout, stop services, reset devices, and undo external account state. If no cleanup is needed, say so explicitly.\n:::\n\n```bash\ngit status --short\n```\n\nRemove temporary local files, stop simulators or servers started for the test, and restore any external account or device state changed during the run.\n\n## Future Automation\n\n:::info\nExplain whether this manual test could later become automated. If yes, name the likely CI job, package, or test harness. If no, explain what keeps it manual.\n:::\n\nIf this scenario later becomes reliable and affordable to automate, replace or supplement this manual report with an automated test and link the PR here.\n\n<!-- File: .claude/skills/frb-pr-chain-split/SKILL.md (Tokens: ~1622 | Category: anthropic-skill) -->\n---\nname: frb-pr-chain-split\ndescription: Use when an FRB PR contains independently landable work that should be extracted into predecessor PRs and connected as a GitHub PR chain without wasting full automatic CI.\n---\n\n# 1 When to use\n\n- Use this skill while developing or reviewing a `flutter_rust_bridge` PR when part of its diff is independent of the PR's stated objective.\n- Split the independent work proactively. Do not wait for the user to notice the mixed scope.\n- Treat a change as independently landable when it:\n    - has a coherent behavior or maintenance objective of its own;\n    - can be implemented and validated against the current default branch;\n    - does not require the main task's API, schema, generated output, or version change;\n    - leaves the main PR smaller and easier to review.\n- Keep inseparable companion changes in the main PR. Generated snapshots, tests, and documentation required by the main change are not independent merely because they touch different files.\n\n# 2 Choose the chain boundary\n\nClassify every suspicious change before moving commits:\n\n| Classification | Destination | Reason |\n| --- | --- | --- |\n| Independent bug fix or hardening | Predecessor PR | It can land and provide value before the main task |\n| Independent mechanical refactor | Predecessor PR | It removes review noise from the main task |\n| Main-task prerequisite with its own contract | Predecessor PR | The main PR can depend on a separately reviewable foundation |\n| Generated output caused by the main task | Main PR | It cannot land meaningfully by itself |\n| Test or documentation for the main behavior | Main PR | Splitting would separate the contract from its implementation |\n\n- Prefer the smallest coherent predecessor, not the smallest textual diff.\n- Create several predecessor PRs when the extracted work contains multiple independent contracts.\n- Order predecessors by dependency. Do not infer order from commit timestamps.\n\n# 3 Build each predecessor\n\n- Fetch the canonical default branch before creating branches.\n- Create the first predecessor from the current default branch.\n- Create each later predecessor from the preceding predecessor branch.\n- Reconstruct the coherent final delta. Do not blindly cherry-pick commits that mix predecessor and main-task changes.\n- Include the predecessor's own tests and documentation.\n- Validate the predecessor independently before pushing.\n- Read `frb-prepare-pr` for generation, lint, test, and review requirements.\n- Use one branch and one PR per independently landable unit.\n- Push every chain branch to the same repository as the main PR. GitHub cannot use an unpushed local branch as a PR base.\n\n# 4 Construct the GitHub PR chain\n\n- Build a stacked branch chain, not sibling PRs that all target the default branch:\n\n```text\nmaster <- predecessor-a <- predecessor-b <- main-task\n```\n\n- Use GitHub's official `github/gh-stack` extension through `gh stack`.\n- Use `gh stack` for every stack operation: initialization, restructuring, linking, submission, synchronization, rebasing, pushing, and verification.\n- Never construct or maintain a stack with `gh pr create --base`, `gh pr edit --base`, direct API base edits, or branch ancestry alone.\n- Git commands may prepare the commits and linear branch ancestry, but the result is not complete until `gh stack` creates or updates the native GitHub stack object.\n- Check and install the extension when necessary. The extension requires `gh` 2.0 or later; a missing `gh stack` command normally means the extension is absent, not that core `gh` is outdated.\n\n```bash\ngh stack --help\ngh extension install github/gh-stack\n```\n\n- For an unpublished main branch, branch it directly from the final predecessor.\n- Initialize and submit a new local stack with `gh stack init` and `gh stack submit`.\n- For an already published main PR:\n    - create a timestamped backup tag before non-trivial history work;\n    - record the original final tree and three-dot PR diff;\n    - restack the main branch onto the final predecessor with a fully linear history;\n    - do not introduce merge commits because native GitHub stacks require linear ancestry;\n    - compare the rebuilt tree and PR delta with the recorded originals before pushing;\n    - use `--force-with-lease` only after those checks when published history must be rewritten.\n- Adopt existing branches or PRs with `gh stack link`. It pushes branches, creates missing PRs, corrects their base branches, and creates the native GitHub stack object.\n- Create and connect a new stack with commands shaped like:\n\n```bash\ngh stack init --base master <predecessor-branch> <main-task-branch>\ngh stack submit\n```\n\n- Adopt already prepared branches or existing PRs with:\n\n```bash\ngh stack link --base master <predecessor-branch> <main-task-branch>\n```\n\n- Verify local ancestry, native stack metadata, and PR bases:\n\n```bash\ngit merge-base --is-ancestor <predecessor-branch> <main-task-branch>\ngh stack view --json\n```\n\n- Compare three-dot diffs after constructing the chain:\n    - `<previous-branch>...<predecessor-branch>` contains only that predecessor;\n    - `<final-predecessor-branch>...<main-task-branch>` no longer contains the extracted work;\n    - the final main-task tree is unchanged except for intentional cleanup.\n\n# 5 Conserve CI on predecessor PRs\n\n- Read `frb-ci-filter` before manipulating CI.\n- Immediately add `ci-manual-dispatch` after creating every predecessor PR:\n\n```bash\ngh pr edit <predecessor-pr-number> --add-label ci-manual-dispatch\n```\n\n- Add the label even when local validation is already green. Predecessor PRs must not spend the full CI matrix merely because they were split for reviewability.\n- The label action triggers a new `pull_request:labeled` run. The workflow concurrency group cancels the earlier automatic PR run, and the labeled run produces an empty heavy-job plan.\n- Do not dispatch GitHub CI when local validation is sufficient.\n- When a predecessor specifically needs GitHub-only evidence:\n    - keep `ci-manual-dispatch` on the PR;\n    - validate the filter locally;\n    - dispatch only the smallest relevant job or matrix entry.\n\n```bash\n./frb_internal plan-ci --filter '<filter>'\ngh workflow run ci.yaml --ref <predecessor-branch> -f 'ci_filter=<filter>'\n```\n\n- Do not treat filtered CI as full merge-readiness evidence.\n- When a predecessor becomes the next PR to merge, remove `ci-manual-dispatch` and obtain the normal required CI unless the maintainer explicitly accepts narrower evidence.\n\n# 6 Completion checks\n\n- Confirm each predecessor can be reviewed without understanding the main task.\n- Confirm each PR's GitHub base matches its immediate predecessor branch.\n- Confirm Git ancestry matches the GitHub base chain.\n- Confirm the main PR diff excludes all extracted changes.\n- Confirm every predecessor PR has `ci-manual-dispatch` while it is not awaiting final full CI.\n- Report the PR numbers, branch order, validation performed, and any intentionally dispatched `ci_filter`.\n\n# 7 Reliability pitfalls\n\n- Do not create independent sibling PRs and merely call them a chain.\n- Do not substitute manual PR base edits for `gh stack link` or `gh stack submit`.\n- Do not treat matching branch ancestry as sufficient; verify the native stack with `gh stack view --json`.\n- Do not force-push a published main PR merely to make its history prettier.\n- Do not split generated files away from the generator or source change that owns them.\n- Do not leave an extracted commit duplicated in the visible main PR diff.\n- Do not spend full CI on every dormant predecessor.\n- Do not leave `ci-manual-dispatch` on the PR when claiming normal full-CI readiness.\n\n<!-- File: .claude/skills/frb-pr-review/SKILL.md (Tokens: ~403 | Category: anthropic-skill) -->\n---\nname: frb-pr-review\ndescription: Review a flutter_rust_bridge PR before treating it as ready, including subagent checks for correctness and test weakening.\n---\n\n# 1 Human SoT\n\n# 2 FRB PR Review\n\nUse this before treating a non-trivial `flutter_rust_bridge` PR as ready, especially after CI-driven fixes, Flutter upgrades, generated drift, test changes, workflow changes, or broad merge conflict resolution.\n\n## 2.1 Review Gate\n\nRun the following review before final readiness:\n\n1. Review the PR diff against its base:\n   - Spawn a subagent to review correctness.\n     - Ask it to inspect the PR diff against the PR base.\n     - Focus on real behavior bugs, stale generated output, incorrect CI workarounds, coverage-only changes, and unrelated drift.\n     - Require findings with file paths, line numbers, impact, and suggested fix.\n   - Spawn a subagent to review test weakening.\n     - Inspect changed or deleted assertions, relaxed thresholds, new skips, ignored failures, timeout changes, and coverage exclusions.\n     - Require a concrete justification for each reduction in coverage; restore unjustified weakening and fix the underlying failure.\n     - If `tom-style-tests` is installed, apply its common test principles and weakening gate; use `frb-test` for FRB execution commands.\n     - Treat unjustified skipped tests, weaker assertions, broader ignores, fake timeouts, and coverage hiding as blockers.\n\n2. Write a concise review conclusion.\n   - Put the conclusion in the PR description or an agent-context draft when the user asks for a Markdown artifact.\n   - Include the subagents used, accepted findings, dismissed findings, fixes made, and remaining risks.\n\n## 2.2 Stop Condition\n\nDo not call the PR ready until:\n\n- Correctness review has no unresolved actionable findings and test-weakening review has no unjustified weakening.\n- CI status is green, or remaining non-green checks are clearly unrelated and explained.\n\n<!-- File: .claude/skills/frb-prepare-pr/SKILL.md (Tokens: ~975 | Category: anthropic-skill) -->\n---\nname: frb-prepare-pr\ndescription: Use when about to create a PR or push changes in flutter_rust_bridge to ensure code generation is up to date\n---\n\n# FRB Prepare for PR\n\n> **Note:** Read `frb-dev-env` before running commands and follow the active user's environment rules.\n\n## Overview\n\nBefore creating a PR, ensure generated code is up to date, lint passes, and bug fixes have a concrete reproduction report in the PR description. Before treating a non-trivial PR as ready, run `frb-pr-review`.\n\n**Core principle:** Generate → Lint → Commit → PR.\n\n> **After codegen:** Follow `frb-dev-env` and verify generated output is present in the selected worktree before reviewing the diff.\n\n## Workflow\n\n```\n1. Read frb-code-generation skill\n   |\n   +-- Run required generation commands\n   |\n   +-- 2. For integrate output diffs, verify templates and run precommit-integrate\n   |\n   +-- 3. Read frb-lint skill --> Run lint --fix\n   |\n   +-- 4. (Optional) Read frb-test skill --> Run relevant tests\n   |\n   +-- 5. For non-trivial PRs, read frb-pr-review --> Run review gate\n   |\n   +-- 6. For bug fixes, add the reproduction report to the PR description\n   |\n   +-- 7. Commit all changes\n   |\n   +-- 8. Create PR (follow the active user-level PR workflow)\n```\n\n## Quick Checklist\n\n1. [ ] **REQUIRED:** Read `frb-code-generation` skill, run commands if needed\n2. [ ] **REQUIRED for integrate output diffs:** If the PR changes Flutter integrate example outputs or platform scaffolds, confirm whether `frb_codegen/assets/integration_template/` is the source that should change, then run `./frb_internal precommit-integrate`\n3. [ ] **REQUIRED for copied Cargokit diffs:** If the PR changes copied `cargokit` files under `frb_example/**`, read `frb-cargokit`, then run `./frb_internal sync-cargokit-copies`\n4. [ ] **REQUIRED:** Read `frb-lint` skill, run `./frb_internal lint --fix`\n5. [ ] (Optional) Read `frb-test` skill, run relevant tests\n6. [ ] **REQUIRED for non-trivial PRs:** Read `frb-pr-review`, run the review gate before final readiness\n7. [ ] **REQUIRED for bug fixes:** PR description includes the reproduction report from `frb-develop-feature`, including baseline commit, mechanical steps, observed failure, and expected behavior\n8. [ ] **REQUIRED for CI iteration branches:** Read `frb-ci-filter`, then remove `ci-manual-dispatch` and rerun normal CI if the PR used filtered CI, unless the PR is explicitly about changing CI behavior\n9. [ ] Commit all changes\n10. [ ] Push and create PR\n\n## What CI Will Do\n\nCI automatically runs:\n- Code generation check (fails if generated code is outdated)\n- All tests (Rust, Dart, Flutter, Web)\n- Lint and format checks\n\nRun lint locally to avoid CI failures. Tests are optional locally.\n\nWhile iterating, it can be useful to dispatch `.github/workflows/ci.yaml` with `ci_filter` so only the relevant job family or matrix entry runs. Read `frb-ci-filter` before doing this. Do not leave `ci-manual-dispatch` on the final PR unless changing CI behavior is the actual purpose of the PR.\n\nIf your PR fixes Flutter integrate example outputs and the real bug is inside the embedded `cargokit` submodule, do not stop at copied example files. Push the `cargokit` fix to `fzyzcjy/cargokit` and update the submodule ref in this repo before pushing the PR branch.\n\nIf the PR changes integrate-generated example output but not `frb_codegen/assets/integration_template/`, explicitly verify that the output-only change is intentional. Most integrate scaffold behavior should be fixed in the template and regenerated, not patched only in `frb_example/**`.\n\n## Related Skills\n\n- `frb-code-generation` - Determines which generation commands to run\n- `frb-lint` - Lint and format checks\n- `frb-test` - For local debugging when CI fails\n- `frb-pr-review` - PR readiness review gate\n- `frb-ci-filter` - Filtered CI dispatch rules\n- Follow the active user-level PR workflow when available; otherwise use `gh pr create` with the repository's title and body conventions.\n\n<!-- File: .claude/skills/frb-publish-release/SKILL.md (Tokens: ~2578 | Category: anthropic-skill) -->\n---\nname: frb-publish-release\ndescription: \"Publish a flutter_rust_bridge release end to end: preflight checks, changelog preparation, frb_internal release publishing, released-version polling, CI babysitting, and post-release CI verification.\"\n---\n\n# FRB Publish Release\n\nUse this skill when preparing, publishing, or babysitting a `flutter_rust_bridge` release.\n\n## Workflow\n\n### 1. Preflight\n\n- Work from the repository root on the intended release branch, normally fresh `master`. If the checkout is detached but already points at the intended release commit, switch or create a local release branch from that commit before running mutating release commands; do not treat detached HEAD itself as a release blocker.\n- Check `git status --short --branch` and do not start publishing from a dirty tree.\n- Check submodules before publishing:\n\n  ```bash\n  git submodule update --init --recursive\n  git submodule status --recursive\n  ```\n\n  Stop if any submodule path remains uninitialized. A release built without initialized submodules can publish incomplete package contents. The release script performs the same submodule-status guard before publishing.\n- Treat `master` as a moving branch. Before each irreversible release phase, fetch `origin master`, confirm the current release branch or detached checkout still points at the intended latest `origin/master` commit, and re-run `git status --short --branch` to confirm there are no unexpected local changes.\n- Confirm the target version in `CHANGELOG.md`, root `Cargo.toml`, and `frb_dart/pubspec.yaml`.\n- Compute the release versions the same way `./frb_internal release` does: the top `CHANGELOG.md` version is the new version and the next release section is the old version.\n- Verify both old and new versions are legal before running any mutating release command. The only allowed shapes are stable SemVer `MAJOR.MINOR.PATCH` such as `2.0.0`, or beta SemVer `MAJOR.MINOR.PATCH-beta.N` such as `2.0.0-beta.1`. Use exactly `^\\d+\\.\\d+\\.\\d+(-beta\\.\\d+)?$`.\n- Reject versions with any other prerelease label, build metadata, missing numeric components, leading `v`, or loose text. Stop if either old or new version fails this check.\n- Confirm the new version is different from the old version.\n- Run the publish container credential preflight before starting irreversible publish steps:\n\n  ```bash\n  .claude/skills/frb-dev-env/frb_dev_env.py docker-run-rm --with-publish-credentials -- true\n  ```\n\n  Stop if the preflight fails. It checks GitHub CLI auth, Cargo credentials, and Dart pub credentials inside the same temporary credential layout used by release publishing.\n- Confirm normal CI is green for the release commit before publishing. This is the default hard gate.\n\n  A narrow exception is allowed only when all of these are true:\n\n  - A recent earlier commit on the same release branch has fully green normal CI.\n  - Every commit between that green commit and the intended release commit is unrelated to the publishable packages, version sources, generated release artifacts, and `frb_internal` release logic.\n  - The mechanical gate script below exits successfully and its output is recorded in the release notes or journal.\n  - The agent explicitly states that it is using the exception before publishing.\n\n  Run the mechanical gate from the repository root:\n\n  ```bash\n  uv run --script .claude/skills/frb-publish-release/release_ci_gate.py \\\n    --base-green-ref <LAST_GREEN_SHA> \\\n    --release-ref HEAD\n  ```\n\n  Stop and wait for normal CI if the script reports any `BLOCK` path. The script is intentionally conservative: release-surface paths such as `Cargo.toml`, `frb_dart/pubspec.yaml`, `frb_codegen/**`, `frb_macros/**`, `frb_rust/**`, `frb_utils/**`, `frb_example/**`, `tools/frb_internal/**`, `pubspec.yaml`, `melos.yaml`, and lockfiles are not in the allowlist. Release metadata and its generated copies are allowed: `CHANGELOG.md`, `frb_dart/CHANGELOG.md`, `frb_hooks/CHANGELOG.md`, `.all-contributors-custom.yaml`, `.all-contributorsrc`, `README.md`, `frb_dart/README.md`, and `website/**`. These files are reviewed directly before publishing and do not affect package execution. Other `frb_dart/**` and `frb_hooks/**` paths remain blocked. This exception is for release metadata, docs, agent tooling, devcontainer, selected CI configuration, and other explicitly non-release paths only.\n\n### 2. Reconcile Contributors\n\n- Before writing the release changelog, use `frb-add-contributor` to identify contributors from the target release range who may need all-contributors credit or whose human-written custom description does not cover every release-range PR.\n- Follow `frb-add-contributor` exactly. In particular, after determining which contributors may need to be added or have their custom descriptions extended, stop for human confirmation before editing contributor files, posting GitHub comments, triggering all-contributors, opening PRs, or merging contributor PRs.\n- Do not continue to changelog preparation until contributor reconciliation is complete, or confirmed unnecessary because all contributors are credited and their custom descriptions cover every third-party PR in the release range.\n\n### 3. Write Changelog\n\n- Use `frb-write-changelog` and follow it exactly. It is the single source of truth for beta and stable changelog content, grouping, ordering, attribution, review, verification, and generated package changelog copies. Do not duplicate those rules here.\n- Do not publish until the human-review gate required by `frb-write-changelog` is satisfied.\n- The top `CHANGELOG.md` version is the source used by `frb_internal release`.\n- If changelog or version files changed, commit that release preparation before publishing.\n- Land contributor and changelog preparation commits on `origin/master` before publishing. Do not leave release preparation only on a side branch.\n\n### 4. Pre-Publish Independent Audit\n\n- Immediately before the first irreversible publish command, delegate a read-only audit to an independent subagent that has not participated in preparing the release.\n- Give the subagent the intended version and the raw evidence needed to verify the work completed so far:\n  - Current branch, exact `HEAD`, latest `origin/master`, and `git status --short --branch`.\n  - Previous and target release versions from `CHANGELOG.md`, root `Cargo.toml`, and `frb_dart/pubspec.yaml`.\n  - Final target changelog section, contributor reconciliation result, human changelog approval, and relevant diffs.\n  - Submodule status, credential preflight result, and normal CI green evidence or evidence that every narrow CI-gate exception condition in Step 1 is satisfied, including successful `release_ci_gate.py` output.\n  - Exact commands and results for any additional focused release tests that were run. These tests do not replace the normal CI gate or its defined narrow exception.\n  - Exact one-shot publish command that will run next.\n- Ask the subagent to return one verdict:\n  - `GO`: all release instructions are satisfied and the evidence is internally consistent.\n  - `NO-GO`: list every blocker, missing artifact, ambiguity, or inconsistent result with concrete evidence.\n- Do not tell the subagent the desired verdict or provide only a prepared summary. Let it inspect the repository and raw command outputs independently.\n- Stop before publishing on `NO-GO`, an ambiguous verdict, missing evidence, or unavailable subagent capability. Resolve every blocker and run a fresh independent audit.\n- Treat the audit as valid only for the exact audited `HEAD`, `origin/master`, clean worktree state, evidence snapshot, and next publish command.\n- After a `GO` verdict, run that exact publish command without any intervening mutation. If the command changes, the worktree becomes dirty, evidence is refreshed or invalidated, or `HEAD` or `origin/master` changes, repeat the affected preflight checks and the independent audit.\n\n### 5. Publish\n\nUse the repository release command through a temporary Docker container with publish credentials as the normal publishing path:\n\n```bash\n.claude/skills/frb-dev-env/frb_dev_env.py docker-run-rm --with-publish-credentials -- ./frb_internal release\n```\n\nRun the one-shot command from a clean local `master` that tracks the latest `origin/master`. `release-update-git` uses plain `git push`, so do not run the command from a side release branch.\n\nDo not split the normal release into separate `release-update-*` or publish commands. The one-shot command is the source of truth for release sequencing: it computes old/new versions from `CHANGELOG.md`, updates checked-in versions and generated version text, updates Scoop metadata, commits and pushes the version bump, creates the GitHub release, then runs the registry publisher:\n\n```bash\n.claude/skills/frb-dev-env/frb_dev_env.py docker-run-rm --with-publish-credentials -- ./frb_internal release-publish-all\n```\n\nFor beta versions such as `2.13.0-beta.1`, do not require the GitHub release to be labeled as a pre-release. FRB intentionally publishes beta GitHub releases as normal GitHub releases while the package version itself remains a SemVer prerelease.\n\n`release-publish-all` publishes these packages:\n\n- `frb_codegen` -> crates.io package `flutter_rust_bridge_codegen`\n- `frb_macros` -> crates.io package `flutter_rust_bridge_macros`\n- `frb_rust` -> crates.io package `flutter_rust_bridge`\n- `frb_dart` -> pub.dev package `flutter_rust_bridge`\n- `frb_hooks` -> pub.dev package `flutter_rust_bridge_hooks`\n\nOnly use a split subcommand as a recovery path after confirming which one-shot step already completed. For example, if the version bump and GitHub release already exist and only registry publication is needed, use `.claude/skills/frb-dev-env/frb_dev_env.py docker-run-rm --with-publish-credentials -- ./frb_internal release-publish-all` directly.\n\n### 6. Check Released Versions\n\nPoll registry state with:\n\n```bash\n./frb_internal get-released-version\n```\n\nTo check a target version from a checkout whose manifests have not been bumped, pass it explicitly:\n\n```bash\n./frb_internal get-released-version --version <VERSION>\n```\n\nThe command prints JSON:\n\n```json\n{\n  \"allReleased\": true,\n  \"packages\": [\n    {\n      \"registry\": \"crates.io\",\n      \"name\": \"flutter_rust_bridge_codegen\",\n      \"manifestVersion\": \"2.12.0\",\n      \"releasedVersion\": \"2.12.0\",\n      \"isReleased\": true\n    }\n  ]\n}\n```\n\nWait until every package has `isReleased: true` and `allReleased: true`. If one registry lags, keep polling with bounded waits and record the mismatched package instead of assuming the publish failed. Do not add the target version option to the mutating `release-*` subcommands; they derive their version from `CHANGELOG.md` and checked-in manifests.\n\nFor beta releases, `get-released-version --version <VERSION>` must verify the pub.dev `versions` list, not only pub.dev's `latest` field, because pub.dev keeps `latest` on the latest stable release when a prerelease is uploaded.\n\n### 7. Babysit CI And Post-Release CI\n\n- Keep watching the release commit's normal CI until it is green.\n- After `./frb_internal get-released-version` reports `allReleased: true`, trigger `.github/workflows/post_release.yaml` for the release commit or `master`.\n- Babysit post-release CI until it is green. Use the user's `tom-ci` skill when reading GitHub Actions logs.\n- If post-release fails, classify the failure by release channel (`stable` or `unstable`) and install mode (`cargo-install`, `cargo-binstall`, `scoop`, or `homebrew`) before changing code or rerunning.\n\n## Related Skills\n\n- `frb-write-changelog` for the release section.\n- `frb-fix-ci` or `frb-fix-main-ci` for CI failures.\n- The user's `tom-ci` skill for GitHub Actions logs.\n\n<!-- ... +20 more modular skills available via /api/skills/fzyzcjy/flutter_rust_bridge?path={path} -->\n</yakaai_skills>"}