| type | Reference | |||||
|---|---|---|---|---|---|---|
| title | Agent command policy | |||||
| description | Canonical allowlist for agent shell commands — install, prepare, validation, e2e, Expo documented-path iOS link, and RN CLI prebuilt RNCore iOS build. Supersedes improvised diagnostics. | |||||
| tags |
|
|||||
| timestamp | 2026-09-03 00:00:00 UTC |
Single source for which shell commands agents may run in this repo. E2e yarn tests:* detail lives in running e2e (agent rule). The workspace Expo documented-path iOS link fixture (test-expo/) and the RN CLI prebuilt RNCore iOS build fixture (test-rn-bare/) are not Detox e2e; each command is only its registry row below. Ad-hoc pod / xcodebuild stay never-use (with or without those rows).
If a command is not listed here (or linked from here as canonical), do not run it — including “diagnostic probes” suggested by log output, package READMEs, or Yarn CLI help.
- Run only commands in the registry below (repo root unless noted).
yarn/yarn lerna:preparemust finish before anything else — see prepare must finish first. Do not parallelize install/prepare with e2e, Metro, builds, or other shell commands.- Before any native
:build: rootyarnexit 0 and patched fmt ≥ 12.1.0 — install / patch / fmt gate. Do not invent Podfile/fmt workarounds. - When a canonical command fails: read the full output, fix product code (or re-run root
yarnfor a patch miss), re-run the same command. Do not switch invocation style. - Do not infer alternate commands from error strings (
command not found: genversion,Couldn't find a script named "jet", etc.) — see known traps. - The command constraints below apply to every shell session in this repo.
| Intent | Command | Never use instead |
|---|---|---|
| Install / refresh deps | yarn (repo root; postinstallDev → yarn prepare && yarn lerna:prepare && yarn ruby:install) |
yarn workspace …, npm install, npm install in a package, yarn install / yarn in tests/ alone for root deps, skipping root yarn before e2e/build |
| Root Gemfile gems (CocoaPods, RuboCop, etc.) | yarn ruby:install (runs bundle check || bundle install when bundle is on PATH; skips with exit 0 when not — JS-only checkouts / CI without setup-ruby); included in root yarn via postinstallDev. CI: BUNDLE_FROZEN=true bundle install before yarn in e2e/publish workflows |
bare bundle install at repo root |
Transpile lib/** → dist/module/** (all packages) |
yarn lerna:prepare |
yarn workspace @react-native-firebase/* prepare, cd packages/<pkg> && yarn prepare, cd packages/<pkg> && yarn run build |
| Transpile one package | yarn lerna run prepare --scope @react-native-firebase/<pkg> |
yarn workspace @react-native-firebase/<pkg> prepare |
After packages/*/lib/** edits (Metro serves dist/module/**) |
yarn lerna:prepare; Metro restart when already running (running e2e § prepare completion gate) — platform :build only when running e2e § Rules #3 requires native/codegen/instrumentation, not for JS alone |
ad-hoc bob, babel, or package-scoped prepare |
| TS/JS validation sequence | validation checklist | ad-hoc tsc in package dirs unless listed there |
| JS lint (implementation / review gate) | yarn lint:js, yarn lint:js --fix |
package-scoped eslint, npx eslint |
| Android Java format / lint | yarn lint:android |
yarn google-java-format, bare google-java-format, google-java-format -i, npx google-java-format, any invented format script |
| Docs lint | yarn lint:markdown, yarn lint:spellcheck — when: validation checklist § lint and formatting (docs/** only; OKF-only skips) |
ad-hoc prettier/eslint on single files |
| iOS Ruby lint (RuboCop) | yarn lint:ruby (also runs inside yarn tests:ios:ruby) |
ad-hoc rubocop, bundle exec rubocop without the Gemfile/config |
| Android JVM unit tests | yarn tests:android:unit |
ad-hoc ./gradlew … outside this yarn script; bare Robolectric/JUnit IDE-only as the agent gate |
| iOS XCTest unit tests (in-package) | yarn tests:ios:unit |
ad-hoc xcodebuild test; CocoaPods test_spec; tests/ios/testingTests host UI tests |
| iOS Ruby unit tests (SPM / CocoaPods helpers) | yarn lint:ruby / yarn tests:ios:ruby (after root yarn or yarn ruby:install when gems are missing) |
ad-hoc ruby packages/app/__tests__/…_test.rb, bare ruby …/run_with_coverage.rb without the yarn script as the agent gate |
| iOS CocoaPods provisioning before shared build | yarn tests:ios:pod:install (after root yarn or yarn ruby:install when gems are missing; required order below) |
bare pod install, cd tests/ios && pod install, or assuming yarn tests:ios:build creates CocoaPods support files |
Expo documented-path iOS link (workspace test-expo/) |
yarn test-expo:ios:link (repo root; script .github/workflows/scripts/test-expo-ios-link.sh) |
ad-hoc expo prebuild / xcodebuild outside that script; cd test-expo && … as the agent gate; yarn test-rn-bare:ios:build as this closer |
RN CLI prebuilt RNCore iOS build (workspace test-rn-bare/) |
yarn test-rn-bare:ios:build (repo root; script .github/workflows/scripts/test-rn-bare-ios-build.sh) |
ad-hoc pod / xcodebuild; cd test-rn-bare && …; tests/ e2e / yarn tests:*; yarn test-expo:ios:link as this closer |
| Android merged Jacoco (unit + e2e) | yarn tests:android:post-e2e-coverage (after e2e); yarn tests:android:test:jacoco-report when regenerating the merge report |
./gradlew jacocoAndroidTestReport as Codecov path; inventing other jacoco yarn scripts |
| Native coverage presence guard (silent-empty fail) | yarn tests:coverage:assert-presence (also invoked from tests:android:post-e2e-coverage and tests:ios:test:process-coverage; exit 2 on empty/missing when strict) |
Ignoring empty LCOV/Jacoco; treating missing .ec / packagesHits=0 as soft success in CI |
| Native coverage baseline capture (repeatability) | yarn tests:coverage:capture-baseline (after Law :test-cover + process; see tests/coverage-artifacts/README.md) |
Hand-editing coverage-baseline.json; inventing parallel capture scripts |
| Native coverage config generate (Gradle properties) | yarn tests:coverage:generate-native-config |
Hand-editing generated tests/android/coverage.properties as the source of truth |
| E2e + coverage | running e2e — only yarn tests:* |
jet, npx jet, yarn jet, detox test, bare detox, cd tests && …, cd tests-macos && …, direct Metro/emulator starts |
| iOS Detox framework cache rebuild | yarn tests:ios:detox-framework-cache:rebuild |
cd tests && yarn detox clean-framework-cache, cd tests && yarn detox build-framework-cache, bare detox … |
Host pre-flight (before each :test-cover) |
running e2e § pre-flight — host-clear + services ready + checkout ownership + harness tier. yarn tests:e2e:check / yarn tests:e2e:release (host-clear probes) |
Port/HTTP checks alone when Metro/emulators belong to another worktree; pgrep/spawn probes of Jet/Detox as completion signals; ad-hoc pgrep / hardcoded :8090 only; improvised kill lists; --all-slots while another owner is live |
| Slotted parallel | running e2e § slot lifecycle — eval "$(yarn tests:e2e:export-slot-env <platform> N)" then the same yarn tests:packager:* / yarn tests:emulator:start / pod:install / :build / :test-cover as serial. macOS packager is yarn tests:macos:packager:* (tests-macos/); mobile is yarn tests:packager:* (tests/) |
ad-hoc port math; second lifecycle helpers as the runbook; rsync between local worktrees; host flock; Metro-after-build; disabling Swift explicit modules; Debug RCT_NO_LAUNCH_PACKAGER as a Metro fix; using the mobile packager for macOS Jet (or vice versa) |
| TurboModule codegen (all migrated / CI) | yarn codegen:verify (wipe + regen + diff); yarn codegen:all for local regen via package scripts |
ad-hoc CLI without wipe; inventing alternate codegen yarn scripts — see TurboModule codegen |
yarn lerna:prepare runs each package's prepare script (build → compile via react-native-builder-bob). That is what produces dist/module/** consumed by Metro in debug e2e — only **release** builds pre-bundle/embed JS (running e2e § Rules #3).
yarn compile(package script) is not a standalone agent entrypoint — it is invoked insidepreparevia lerna. Do not runcd packages/<pkg> && yarn compilefor handoff unless validation checklist explicitly adds an exception (none today).yarnat repo root runspostinstallDev→yarn prepare && yarn lerna:prepare && yarn ruby:install; a fresh install already transpiles and installs root Gemfile gems. Re-runyarn lerna:prepareafterlib/**edits without reinstalling.
yarn, yarn lerna:prepare, and yarn lerna run prepare --scope … are blocking foreground commands. Wait for the shell to return exit code 0 before starting any other command — including in the same agent turn via parallel tool calls.
| Do not start until prepare exits 0 | Why |
|---|---|
yarn tests:* (e2e, packager, build) |
Metro (debug JS) reads dist/module/**, not lib/\*\* — partial prepare → missing modules, stale bundles |
yarn tests:packager:jet-reset-cache / yarn tests:macos:packager:jet-reset-cache |
Reset after prepare, not during it. Free :8081 first — running e2e § packager reset-cache. Mobile packager is tests/; macOS packager is tests-macos/ |
yarn tsc:compile, Jest, compare:types |
May read transpiled output or assume dist/ is current |
Another yarn / scoped prepare |
Overlapping Nx/Lerna runs race on dist/ |
Agent rule: one prepare invocation per message batch; wait for completion; then run the next step (Metro restart if needed → pre-flight → :test-cover). Running e2e § prepare completion gate is the e2e-side mirror of this rule.
Symptoms when violated: Cannot find module '…/dist/module/…', Metro 500 on bundle, e2e failures before tests run, or green Metro /status while the app loads a half-written dist/.
Canonical owner for install + patch freshness before Detox native builds. E2e docs link here — do not restate this procedure elsewhere.
Before any yarn tests:ios:build, yarn tests:android:build, or other Detox native build path:
- Root
yarnMUST have run and exited 0 in this checkout. Required on a fresh checkout, after deletingnode_modules, after pulling patch changes, and whenever patches may be stale. Do not start native:builduntil that install finished successfully. - Root
yarnapplies.yarn/patches(jet, detox, mocha-remote) and workspacepatch-packagepatches. macOS still appliestests-macos/patches/react-native+0.78.3.patch(fmt 12.1.0). Mobile RN 0.86.2 ships fmt 12.1.0 upstream (notests/patches/react-native+*.patchfmt bump).tests/tests-macosprepareispatch-packageand must not be Nx-cache-skipped (MonoTool-AD-12). - Verify the React Native fmt podspec reports version ≥ 12.1.0 (Xcode 26 / Apple Clang 21-safe floor for this pin). Yarn exit 0 alone is not sufficient — always run the check below before native
:build(a prepare cache-skip historically left fmt at 11.0.2 despite a green install):
# Mobile toolchain (tests/) — required before ios/android :build
rg 'spec\.version|:tag' tests/node_modules/react-native/third-party-podspecs/fmt.podspec
# macOS app (tests-macos/) — required before macos :build (path may be workspace-local or hoisted)
rg 'spec\.version|:tag' tests-macos/node_modules/react-native/third-party-podspecs/fmt.podspec \
|| rg 'spec\.version|:tag' node_modules/react-native/third-party-podspecs/fmt.podspecExpect 12.1.0 (or higher) on both spec.version and :tag.
- If fmt is still 11.0.2 (or anything < 12.1.0): STOP. Re-run root
yarn/ fix patch application for macOS (including Nxtests-macos:preparecache policy — MonoTool-AD-12). On mobile 0.86+, fmt should already be ≥12.1.0 without a patch; if not, investigate the resolvedreact-nativeversion. Do not invent Podfilepost_installfmt hacks,FMT_USE_CONSTEVAL/base.hpatches, c++17-for-fmt-only, or web-search workarounds. - Before
yarn tests:ios:buildon a clean checkout: run rootyarn(which includesyarn ruby:install), then runyarn tests:ios:pod:installand wait for exit 0. Re-run the Pod install aftertests/ios/Podfile/Podfile.lock, package podspec, or native dependency-resolution changes, and whenevertests/ios/Podsis absent. Rootyarndoes not create the CocoaPods xcconfig or file-list support files; the shared build invokes Detox/Xcode and does not provision them. CI installs the root bundle before yarn, then uses this same Pods-before-build order in.github/workflows/tests_e2e_ios.yml.
Symptoms when violated: Apple Clang 21 consteval errors compiling unpatched fmt 11.0.2; missing tests/ios/Pods xcconfig or file-list support files; agents inventing Podfile/fmt workarounds instead of re-running root yarn / fixing prepare cache policy.
- Re-run from repo root:
yarnoryarn lerna:prepare(full log — do not truncate). - Note the first Nx/Lerna project that failed (e.g.
@react-native-firebase/functions:prepare). - Fix product code in that package (TypeScript errors, missing exports, etc.).
- Re-run
yarn lerna:prepare— same command, same cwd. - Do not “verify tooling” with
yarn workspace … prepare,yarn bin …, or package-scopedyarn run build— Yarn 4 uses different PATH for those invocations (genversion trap).
| Command | Why |
|---|---|
yarn workspace @react-native-firebase/* prepare (and variants) |
Not canonical; breaks root devDependency binary resolution |
cd packages/<pkg> && yarn prepare / yarn run build |
Same trap; not the postinstall / lerna code path |
yarn google-java-format, bare google-java-format, npx google-java-format, google-java-format -i |
Invented format entrypoints — only yarn lint:android |
npm install (any cwd) / yarn / yarn install only in tests/ for monorepo deps |
Root yarn applies patches and workspace links; tests-only install is insufficient |
Ad-hoc ./gradlew … outside allowlisted yarn scripts (tests:android:unit, tests:android:build, tests:android:post-e2e-coverage, tests:android:test:jacoco-report, etc.) |
Wrong task / cwd / report path; invents CI that does not match Codecov |
Ad-hoc xcodebuild test / CocoaPods test_spec / tests/ios/testingTests as the iOS unit gate |
Misses LCOV merge — only yarn tests:ios:unit (IosTest-AD-1) |
Ad-hoc ruby packages/app/__tests__/…_test.rb (or bare runner) as the validation gate |
Misses SimpleCov / suite discovery — only yarn tests:ios:ruby |
Ad-hoc expo prebuild, xcodebuild, or cd test-expo && … as the Expo iOS link gate |
Only yarn test-expo:ios:link from repo root — not Detox / yarn tests:* / yarn test-rn-bare:ios:build |
Ad-hoc pod, xcodebuild, or cd test-rn-bare && … as the RN CLI prebuilt RNCore iOS build gate |
Only yarn test-rn-bare:ios:build from repo root — not Detox / yarn tests:* / yarn test-expo:ios:link. Ad-hoc pod / xcodebuild stay never-use |
react-native init, npx @react-native-community/cli init, npx react-native init |
Not on the allowlist — seed checked-in RN CLI trees via template gotcha |
yarn jet, npx jet, cd tests && yarn jet … |
E2e agent rule |
detox test, bare detox, cd tests && detox … |
E2e agent rule |
bare bundle install at repo root |
Use yarn ruby:install or root yarn (postinstallDev includes ruby:install) |
| Ad-hoc Metro / emulator start | Use yarn tests:packager:jet (iOS/Android) or yarn tests:macos:packager:jet (macOS), yarn tests:emulator:start (running e2e) |
| Spawn / PATH probes to “test” Jet or genversion | Log triage only; fix product code and re-run canonical command |
When a Shell command returns with no exit status (e.g. "execution backend unavailable") under default sandbox permissions, retry the same canonical command with required_permissions: ["all"] — do not invent an alternate command because the sandboxed attempt failed to start.
Local e2e (yarn tests:*:test-cover), the packager, emulator start, native builds, and host pre-flight probes that need real devices/simulators typically need unrestricted permissions on this host. A "no exit status" result on those commands is a sandbox artifact, not evidence the run failed or is incomplete — see running e2e § running one iteration for checking the tee log footer before concluding anything from a missing exit code. Startup-fail markers on the tee are immediate hard infra — startup fail-fast poll (TELNET / emulator-16 / ReactContext is null / serial leftover :12007+5554; idle APP_STATUS is healthy; currentStatus / status-query timeout is latency, not a wave-kill).
${PIPESTATUS[0]} is bash. zsh spells it ${pipestatus[1]} (1-indexed), and the bash form expands to the empty string — yarn <target> 2>&1 | tee /tmp/x.log; echo "EXIT=${PIPESTATUS[0]}" prints EXIT= whether the target passed or failed. Tee-to-log is the normal pattern for long-running targets and every gate wants a per-command exit code, so a real exit 1 can be recorded as a pass and turn into a false finding. Use ${pipestatus[1]}, or run the command bare and read $?.
genversionexists at rootnode_modules/.binafteryarn.yarn lerna:prepare(andyarn install→postinstallDev) runs prepare via Nx with root toolchain on PATH → baregenversionin package"build"scripts works.yarn workspace … prepareorcd packages/foo && yarn run builddoes not expose root-only devDependencies →command not found: genversion. That is not corruptnode_modules; do not patch scripts withyarn run -T genversionunless deliberately changing repo policy onmain.
yarn jet --helpworking or failing intests/is not a valid e2e or install gate.- Jet is started internally by
yarn tests:<platform>:test-cover. Stale:8090→ pre-flight recovery, then re-run the same:test-covercommand. - Metro
EADDRINUSEon:8081fromyarn tests:packager:jet-reset-cacheoryarn tests:macos:packager:jet-reset-cache→ packager reset-cache (free:8081, then the same yarn target). Not the:8090pre-flight kill. Do not use the mobile packager for macOS Jet (or vice versa).
- There is no
yarn google-java-formatscript. Inventedgoogle-java-format/npx google-java-formatinvocations are forbidden. - Canonical:
yarn lint:android(repo root) — wrapsgoogle-java-format --set-exit-if-changed --replaceonpackages/*/android/srcand fails if the tree would change.
- Do not invent
cd tests && yarn install, then bare./gradlewfrom an arbitrary cwd. - Unit:
yarn tests:android:unitonly. Runner choice and@Config/sdkpolicy: AndroidTest-AD-1. - Merged coverage after e2e:
yarn tests:android:post-e2e-coverage(Codecov path isjacocoTestReport, not e2e-onlyjacocoAndroidTestReport) — coverage design. - Optional explicit merge:
yarn tests:android:test:jacoco-report.
- Canonical:
yarn tests:ios:unit— discoverspackages/*/ios/*UnitTests/*.xcodeproj, macOS destination, writescoverage/ios-unit/lcov.infoand merges intocoverage/ios-native/lcov.info(IosTest-AD-1; coverage design). - Forbidden as the agent gate: ad-hoc
xcodebuild test, CocoaPodstest_spec,tests/ios/testingTestshost UI tests.
yarn lint:jsiseslint packages/* --max-warnings=0. That glob coverspackages/app/__tests__/.scripts/version.jsis not in it.- After
bundle install --gemfile=packages/app/__tests__/Gemfile, Bundler follows root.bundle/configBUNDLE_PATH: vendor/bundleand drops a gitignored tree atpackages/app/__tests__/vendor/. ESLintglobalIgnoresdoes not list that path, so lint reports thousands of vendor findings. - That is local checkout noise, not a product lint failure. CI without that tree stays green.
- Do not invent a delete-vendor command as the lint gate. Do not patch
eslint.config.mjsto hide it. Rootyarn ruby:installor rootyarnis the canonical install path above.
- Canonical:
yarn tests:ios:ruby— discovers allpackages/app/__tests__/*_test.rb, SimpleCov →coverage/ios-ruby/lcov.info, Codecov flagios-ruby. - CI home:
tests_e2e_ios.yml(debug + spm) only — not Jest /tests_e2e_other.yml. - Forbidden as the agent gate:
ruby packages/app/__tests__/firebase_spm_test.rb(or any single-suite / bare-ruby invocation). One-off debugging may use bare ruby locally; gate close / handoff evidence must cite the yarn target. - First-time / Gemfile change:
yarn ruby:install(or rootyarn, which runs it viapostinstallDev). Path via committed.bundle/config. Whenbundleis not on PATH,yarn ruby:installskips with exit 0 (no setup-ruby required in lint/Jest/Android CI). CI usesBUNDLE_FROZEN=true bundle installbefore yarn in e2e/publish workflows; after yarn,yarn ruby:installis a no-op (bundle checksucceeds). Do notgem update cocoapods xcodeproj. - Host Ruby floor:
>= 3.3.1. Ruby 3.3.0 cannot load lockfilesimplecov1.1.1 (anonymous block parameter is also used within block, CRuby #20090). Do not downgrade simplecov to paper over it. With rbenv, pin a newer patch (e.g.RBENV_VERSION=3.3.3). - Never
bundle install --gemfile=packages/app/__tests__/Gemfile. That writes a gitignored vendor tree underpackages/app/__tests__/vendor/and thenyarn lint:jsexplodes. See JS lint / Bundler vendor. - Blocking when Ruby sources or
*_test.rbtouched: validation checklist § iOS Ruby.
@react-native-community/templateis not installed by rootyarn. It is not areact-nativedependency, so a green install does not put the community template undernode_modules.- Seeding or refreshing a checked-in RN CLI
ios/tree (fixture app under the monorepo) after yarn cannot assume that package exists. The checked-in vanilla CLI compile fixture istest-rn-bare/(closeryarn test-rn-bare:ios:build). Do not re-seed it. - Workaround: one-shot pin
@react-native-community/template@<RN line>on the fixture package, copyios/+ JS entry files from the template into the fixture, then remove the pin. Do not leave the template as a durable dependency. - Never
react-native init/npx @react-native-community/cli init/npx react-native init— not on the agent allowlist (see Forbidden).
cd packages/<pkg> && yarn ios:codegen(oryarn android:codegen) often fails withunknown command 'codegen'after a cleanyarn—@react-native-community/cliresolves from the test app workspace.- Package scripts wipe then regen the configured
--outputPath(NewArch-AD-22). Prefer those yarn scripts when CLI resolution works. - Canonical (mobile toolchain from
tests/): use each package'syarn android:codegen/yarn ios:codegenscript, which delegates toscripts/codegen-package.mjs. The shared runner wipes the configured output path and invokes the pinned mobile CLI fromtests/; do not run the CLI manually. RN 0.86 emitsResultTnatively, so the former inject script is retired (NewArch-AD-21). - CI / library packages:
yarn codegen:verify. Library vs test-app codegen (committed trees,includesGeneratedCode, git-diff guard, test-app dump gitignore): NewArch-AD-5. Test-app iOS CLI--outputPathisios(project base); wipe isios/build/generated/iosplustests/ios/Package.swiftand sibling app dumps. - After library regen: commit the generated trees named in NewArch-AD-5, then
:build+ Metro reset-cache before:test-cover.
- Unpatched RN 0.78 ships fmt 11.0.2. On Xcode 26 / Apple Clang 21 that fails consteval builds.
- Canonical fix (macOS 0.78): root
yarnapplyingtests-macos/patches/react-native+0.78.3.patch→ fmt 12.1.0. Mobile 0.86.2 already ships fmt 12.1.0. See install / patch / fmt gate. - Trap: yarn exit 0 does not prove a macOS patch landed. If Nx cache-skips
react-native-firebase-tests-macos:prepare(patch-package), fmt stays at 11.0.2. Durable policy: MonoTool-AD-12. Always run the fmtrgverification before native:build. - Never invent Podfile
post_installfmt hacks,FMT_USE_CONSTEVAL,base.hpatches, or c++17-for-fmt-only as a substitute for a missed install/patch.
RNFB agent command policy: okf-bundle/testing/agent-command-policy.md ONLY.
E2e: okf-bundle/testing/running-e2e.md yarn tests:* ONLY.
Expo documented-path iOS link (not Detox): yarn test-expo:ios:link ONLY — never ad-hoc expo prebuild / xcodebuild / cd test-expo; never yarn test-rn-bare:ios:build as that closer.
RN CLI prebuilt RNCore iOS build (not Detox): yarn test-rn-bare:ios:build ONLY — never ad-hoc pod / xcodebuild / cd test-rn-bare; never tests/ e2e; never yarn test-expo:ios:link as that closer. Ad-hoc pod / xcodebuild stay never-use.
Never react-native init / npx @react-native-community/cli init — @react-native-community/template is not installed by root yarn; one-shot pin + copy ios/ + JS, then remove pin — #react-native-community-template-checked-in-rn-cli-ios.
Never: yarn workspace prepare, yarn jet, npx jet, cd packages/* && yarn prepare/build for diagnostics.
Never invent format/install: yarn google-java-format, bare/npx google-java-format, npm install, yarn install in tests/ alone — use root yarn first; Java format = yarn lint:android ONLY.
Never invent Android Gradle: ad-hoc ./gradlew outside yarn tests:android:unit / :build / :post-e2e-coverage / :test:jacoco-report; bare detox/jet/metro.
Prepare/install: yarn or yarn lerna:prepare must exit 0 before ANY other command — never parallelize with e2e/Metro/build.
Before native :build: root yarn exit 0 + verify tests/node_modules/react-native/third-party-podspecs/fmt.podspec (and tests-macos copy when building macOS) ≥ 12.1.0 — okf-bundle/testing/agent-command-policy.md#install-patch-fmt-gate-blocking. Before iOS build on a clean checkout: root yarn, then yarn tests:ios:pod:install exit 0. If fmt < 12.1.0: STOP and re-run yarn; never invent Podfile/FMT_USE_CONSTEVAL/c++17 fmt hacks.
Area harness: okf-bundle/testing/running-e2e.md#local-harness-overrides-harnessoverridesjs — copy harness.overrides.example.js to gitignored harness.overrides.js; set modules + RNFBDebug; delete overrides after run.
TurboModule contract test (NewArch-AD-17.1): packages/app/__tests__/nativeModuleContract.test.ts — yarn tests:jest -- packages/app/__tests__/nativeModuleContract.test.ts
Android JVM unit (AndroidTest-AD-1, JUnit-first; omit @Config/sdk unless proven): yarn tests:android:unit — not a substitute for platform e2e.
iOS Ruby (SPM helpers): yarn tests:ios:ruby — never ad-hoc ruby packages/app/__tests__/…_test.rb as the gate. Never bundle install --gemfile=packages/app/__tests__/Gemfile. Host Ruby >= 3.3.1 (not 3.3.0); do not downgrade simplecov.
JS lint vendor flood under packages/app/__tests__/vendor/: local Bundler tree, not product lint. Never invent delete-vendor as the lint gate. See #js-lint-bundler-vendor.
Agent shell is zsh: capture exit codes with ${pipestatus[1]} or bare $? — ${PIPESTATUS[0]} is bash-only and expands to empty. See #agent-shell-is-zsh.
On failure: fix product code (or re-run yarn for patch miss), re-run the same canonical command.
Gate close / push: return [validation evidence package](validation-checklist.md#validation-evidence-package) and [coverage evidence package](coverage-design.md#coverage-evidence-package) when lib/native/Ruby helpers touched — required before commit or publication ([change authoring § validation evidence](change-authoring-workflow.md#validation-evidence-blocking)).
| Topic | Owner |
|---|---|
| E2e commands, pre-flight, tiers | running-e2e.md |
| Expo documented-path iOS link (not Detox) | This file — registry row yarn test-expo:ios:link; app index packages/app |
| RN CLI prebuilt RNCore iOS build (not Detox) | This file — registry row yarn test-rn-bare:ios:build; app index packages/app |
Install / patch / fmt / iOS Pods before :build |
§ install / patch / fmt gate |
| Test-app RN / CLI pins (mobile + Expo/RN CLI fixtures share the mobile line; macOS separate) | test-app-dependency-pins.md |
| Validation sequence | validation-checklist.md |
| Android JVM unit ADR | AndroidTest-AD-1 |
| iOS XCTest unit ADR | IosTest-AD-1 |
| iOS Ruby unit / SimpleCov | coverage design § iOS Ruby; validation checklist § iOS Ruby |
| JS lint vs local Bundler vendor | § JS lint / Bundler vendor |
| Work types and gates | change-authoring-workflow.md |
| Doc / commit policy | documentation-policy.md |