Project
Changelog
Release history for QaReN
Plugin
2.1.1
Patch Changes
- cc77d10: Use strict live hit evidence for iOS waits, scroll completion and Fill focus under inferred overlays while preserving static drag safety cuts.
- d73019f: Give the first Tap or Fill after launch, and after a login replay, up to 15 seconds for an absent quoted target to render before failing; a target that never appears fails
TARGET_NOT_FOUND_AFTER_LAUNCHwith a Wait suggestion, and the row recordsimplicitWaitMs. - 2118fd6: Recover an app whose advertised page never proves development mode with one bounded relaunch when eligible, then refuse as
dev_runtime_unprovenwith port-wide Metro bundle observations and value-free advertised-device counts. - e8ca8d0: Serve the runtime version the installed iOS app embeds to QaReN's own dev server, so apps using the expo-updates
fingerprintruntimeVersion policy no longer recompute the project fingerprint on every manifest request; the receipt records the embedded policy and pin state, and a manifest warm-up refusal names that policy when the pin was not served. - 753885d: Allow Fill to target an unnamed empty input by its React placeholder when complete native acquisition and React evidence prove a sole native input and a sole React input host, re-confirm unnamed positional inputs on iOS at dispatch, and explain when incomplete React coverage prevents pairing.
- 3714687: Pass a cleanly tapped choice chip that shows itself selected under a new element reference or beside a React-only entry with the same testID, and say plainly when a tapped control could not be re-identified instead of blaming its list.
- 5a2e4bb: Remove the unused navigation-graph, navigate, store, component-state, dispatch, console, error, readiness and app-info injected helpers, stop installing console, error-handler and promise-rejection tracking, and empty and disable recording into legacy buffers while leaving previously installed wrappers and hooks in place in a running app.
- e24930d: Rebuild injected helper calls from validated methods and code-safe JSON arguments.
- 92d498a: Retry bounded simulator inventory reads and distinguish unavailable inventory from failed boot proofs while sharing reply validation with doctor.
- 651d7ae: Note suspected video recorder stalls from kept plan screenshots while excluding withheld spans and line numbers, and record detector completion in the receipt.
2.1.0
Minor Changes
- fbf949c: Skip iOS Fill steps whose fields already hold the exact stable value, with an
(always retype)plan label to preserve retyping when needed.
Patch Changes
- 146862c: Refuse unavailable development targets within the attach budget and report a precise recovery action.
- d02d71f: Recover iOS Fill on an already-focused field with a hidden keyboard by blurring and re-finding it before one bounded re-tap.
- d9d415c: Patch tinypool, js-yaml, Vite, PostCSS, Browserslist, baseline-browser-mapping, and source-map-js security vulnerabilities.
- a10e640: Refuse stalled Watchman cookie syncs before manifest retries with a specific cause and recovery steps.
- 973b3c1: Include measured client settle timing and native preparation counts in step ledger rows.
- aa68a0f: Start published videos at the first plan step while preserving admission and login boundaries, withhold copies without a positive window, and record the window in the receipt.
- cde5e8a: Exclude untracked watchman sync cookie files from candidate provenance and worktree drift diagnostics.
2.0.8
Patch Changes
- 26eda98: Fix iOS presses falsely blocked by inferred overlays by requiring a proven live hit and native element activation after clearance, including when a narrow band prevents scrolling, and explain overlay refusals without exposing labels, identifiers or values.
2.0.7
Patch Changes
- f242818: Recognize iOS RedBox and LogBox and Android native RedBox overlays during walks, record bounded attempts to dismiss them with their own control, and refuse
DEV_REDBOXwhen safe progress cannot be proven without exposing error text. - c09643d: Plain scrolls that leave the screen unchanged after two clean attempts now pass with the typed reason
SCROLL_NO_VISIBLE_CHANGE, while dispatch errors, uncertain effects, context changes, and missing scroll-until targets still fail with diagnostic causes and existing guidance. - 709124f: Never dismiss a screen, modal or consent screen that the plan's own previous press opened; a missing next target now fails with
previous-plan-pressinstead of tapping Skip or Close. - adee5e4: Make screenshots follow the existing video privacy mode while continuing to withhold protected fields and values.
2.0.6
Patch Changes
- 03b30c0: Dismiss optional sheets through their fixed decline control when they cover the waited target.
- 73a76fb: Warn about iOS precompiled-module compiler incompatibility and provide the next step for build failures caused by it.
- 0dd3a26: On iOS, taps on unselected native row-choice candidates attempt keyboard dismissal before settling and tapping the target once, record successful clearance in the receipt, and refuse when selection cannot be verified.
- 04d1bb0: A text label inside a pressable that is disabled or not hittable now resolves to that one control instead of refusing the quoted press as
TARGET_AMBIGUOUS; separate same-label controls stay ambiguous. - e2325a2: A step that fails on its target now reports the last observed screen's controls with their age, a suggested plan edit as the next step, and an earlier identical failure on the same plan and revision.
- 16d2bbe: Start Metro-only Expo commands offline by default and bound manifest warm-up with actionable diagnostics for slow request handlers and stalled watchers or slow volumes.
- 5f61a5a: Mark the Metro readiness stage in
qaren checkprogress instead of theexpo-startprocess exit, so stopping Metro at cleanup no longer shows a failedexpo-startafter the stage passed.
2.0.5
Patch Changes
- dd11744: A tap on a control that was already selected and stays selected now passes with the evidence
already selected, no changeinstead of failing as an unchanged screen; a toggle still passes on its new state. - c5f63a7: Retry uncertain dev-overlay reads within the startup budget and report recovery guidance.
- 83b7448: Retry transient fresh-install identity uncertainty once within the existing admission allowance and report the terminal probe evidence.
- 02ca999: Failed iOS
checkwalks and target refusals after admission now retain Metro, the simulator and the device lease for inspection untilqaren cleanup <run-id>or the next run on that device; check-only--holdand--no-holdoverride the defaults, whileCI, a## Cleanupplan section,--shutdown-device-on-exitandQAREN_HOLD_ON_FAIL=0preserve normal teardown by default. - 02ca999: iOS runs now start Metro bound to localhost, so a Metro kept for inspection after a failed check is not reachable from the local network.
- a31233e: Recover owned iOS runner drivers and simulator hosts during explicit cleanup while preserving automatic teardown safety.
- dd11744: Prevent unrelated screen changes from passing unverified selection-control taps and avoid repeating a press whose selection effect cannot be observed.
2.0.4
Patch Changes
- b55a5dc: Make a copyable agent setup prompt the primary installation path in the docs and README while keeping per-host plugin commands and manual source installation.
- e075ccb: Show the copyable agent install prompt on the docs landing page and share it with the installation guide.
- 05d6a08: Add an explicit non-strict video policy for test data while preserving detected SECRET withholding and strict publication defaults.
- 3570f2c: Enforce resource teardown for post-record check failures and verify cleanup for every core refusal code.
- a90f8e7: Keep explicit runtime linking within the current HOME and report the written link and potential PATH shadows.
- 1765403: Preserve GitHub tokens for QaReN-owned GitHub commands while keeping other subprocesses isolated and refusing unavailable authentication with setup guidance.
- 7151b4c: Use each crash report's capture time for iOS lane-health attribution and gracefully cancel native-suite and recovery drivers without force-killing initializing XCTest helpers.
- f97a39b: iOS preparation now waits within its build budget and releases the native build lock after verified cache publication, with safe holder diagnostics.
- 529e1d8: Fix native iOS suite log-helper dispatch and safely recover simulator leases after a failed spawn.
- 6cb42d2: A tap on an option that exposes no accessibility state now passes when the tapped control visibly changes on screen within its own bounds.
- fb253ea: Prevent false passing option taps after delayed layout shifts by rechecking target geometry after comparison screenshots.
- ccd43fc: Recover iOS dev-client attach stalls with bounded manifest-probe backoff and up to two additional relaunches when no bundle request arrives and a probe exceeds the client timeout.
- a9a4457: Refuse incompatible pnpm bootstrap launchers before resource acquisition with an actionable typed reason and report pnpm's source in doctor.
- 3b080c3: Retry a timed-out pnpm process-group probe with a doubled bound inside the dependency-install budget, and refuse a persistent timeout as
PNPM_PROBE_TIMED_OUTinstead of reporting a supported pnpm as unsupported. - 7b2c3b0: Default iOS
qaren preparenow reuses a free QaReN-created simulator from a per-home pool capped byios.max_devices, and--newkeeps the previous fresh temporary simulator. - 09db4d3: Add the proofInDescription config key: qaren publish also writes the verdict, tested commit, comment link and the uploaded video into a QaReN-owned section of the pull request description, replacing it on a new head and preserving the author's text.
- cd4c6ab: Prevent host readback of TEST-PII inputs whose values collide with a SECRET input.
- 72d511c: Runtime inputs now follow SECRET, TEST-PII and PUBLIC tiers: TEST-PII Fill readback, failure screen text and core.log are redacted by declared values while subprocess logs and media stay withheld, secure fields promote inputs to SECRET, and SECRET refusals report only value-free mismatch facts.
- ff50db8: A quoted wait whose only match is mounted in React but absent from the native screen now ends within its own deadline with
TARGET_REACT_ONLYand a next step, refusing early or scrolling once when the field's layout shows why, instead of reporting visibility uncertainty. - cb7d66e: Rename the Android runner directory and release zip to
qaren-android-runnerand label the emulator appQaReN Android Runner; the package id is unchanged, so the next run replaces an installed runner in place, and the oldpackages/rn-android-runner/build output can be deleted. - 11f497f: Rename the iOS runner directory and release zip to
qaren-ios-runner, label the simulator appsQaReNandQaReN-Runner, allow removal of the oldpackages/rn-fast-runner/build/directory, and useRN_RUNNER_BUILD=localon source checkouts until 2.0.4 is published. - 98dce90: Allow explicitly declared TEST-PII fills to use ordinary readback on readable non-secure fields with private native labels while retaining SECRET protections and recording value-free privacy causes.
- 7d45ccb: A Fill into a code field that submits itself and leaves the screen no longer waits about ten seconds reading back the vanished field; its result is unchanged.
- 2b7af1d: An iOS simulator a run explicitly owns now has its locale and keyboard pinned before launch,
checkthen proves its first screen before row 1 and refuses asLANE_NOT_WARMotherwise, the receipt records the lane once aslane, andqaren doctor --device <UUID>checks the pinned settings.
2.0.3
Patch Changes
- 4197438: Make a copyable agent setup prompt the primary installation path in the docs and README while keeping per-host plugin commands and manual source installation.
- 4197438: Show the copyable agent install prompt on the docs landing page and share it with the installation guide.
- 4197438: Add an explicit non-strict video policy for test data while preserving detected SECRET withholding and strict publication defaults.
- 4197438: Use each crash report's capture time for iOS lane-health attribution and gracefully cancel native-suite and recovery drivers without force-killing initializing XCTest helpers.
- 4197438: Fix native iOS suite log-helper dispatch and safely recover simulator leases after a failed spawn.
- 4197438: A tap on an option that exposes no accessibility state now passes when the tapped control visibly changes on screen within its own bounds.
- 4197438: Prevent false passing option taps after delayed layout shifts by rechecking target geometry after comparison screenshots.
- 4197438: Refuse incompatible pnpm bootstrap launchers before resource acquisition with an actionable typed reason and report pnpm's source in doctor.
- 4197438: Default iOS
qaren preparenow reuses a free QaReN-created simulator from a per-home pool capped byios.max_devices, and--newkeeps the previous fresh temporary simulator. - 4197438: Prevent host readback of TEST-PII inputs whose values collide with a SECRET input.
- 4197438: Runtime inputs now follow SECRET, TEST-PII and PUBLIC tiers: TEST-PII Fill readback, failure screen text and core.log are redacted by declared values while subprocess logs and media stay withheld, secure fields promote inputs to SECRET, and SECRET refusals report only value-free mismatch facts.
- 4197438: Allow explicitly declared TEST-PII fills to use ordinary readback on readable non-secure fields with private native labels while retaining SECRET protections and recording value-free privacy causes.
- 4197438: A Fill into a code field that submits itself and leaves the screen no longer waits about ten seconds reading back the vanished field; its result is unchanged.
- 4197438: An iOS simulator a run explicitly owns now has its locale and keyboard pinned before launch,
checkthen proves its first screen before row 1 and refuses asLANE_NOT_WARMotherwise, the receipt records the lane once aslane, andqaren doctor --device <UUID>checks the pinned settings.
2.0.2
Patch Changes
- ae42998: iOS teardown now asks the XCUITest runner to shut down before falling back to signals, and checks record matching SpringBoard crash reports as lane-health notes without changing the run result.
- 5520dcf: Present QaReN in the README and docs site as a QA engineer for your coding agent, with the qaren CLI reference grouped under How it works.
- 670083e: A released qaren and a source build now install side by side:
QAREN_HOMEmoves runs, state, runtimes and the runner cache, source builds are versioned<version>-dev.<commit>, and the installer no longer relinks the other line'sqarenwithout--link.
2.0.1
Patch Changes
- ff0d5a5: Point runner downloads, the installer, plugin manifests and documentation links at the renamed Lykhoyda/QaReN repository and its https://lykhoyda.github.io/QaReN/ docs site.
2.0.0
Major Changes
- b832ecb: Rename the plugin to QaReN and cut it to three packages: the
qarenCLI, aqaren-corescreen layer that builds and tests without the MCP server or the session stack, and aqaren-pluginhost package with five skills; the shared-knowledge mirror, the Codex package, the committed host bundles and their sync gates are removed.
Minor Changes
- dc494f1: Let
qaren checkandqaren prrun on Android by borrowing one booted emulator on the host's running adb server, building the debug APK with Gradle (prebuilding an absentandroid/) and installing it by serial. - 9eddb3b: Android phrase steps now resolve on screens whose only covers are transparent, non-interactive chrome, such as the dev client's overlay, React Native's debugging overlay or a parked bottom sheet. A control counts as observed only when both the Android runner and React Native's own hit test agree that a touch at its centre reaches it. A control whose touch a React overlay takes keeps its identity but cannot be pressed or filled in that capture, so a phrase step naming it refuses while the rest of the screen stays usable. The Android runner must be rebuilt for this release; an earlier runner is refused as stale. iOS, literal plans and controls under the keyboard are unchanged.
- 7dcf000:
qaren checksaves a fully passing plan into.qaren/plans/and flags or clears a saved plan as broken for a platform when it fails or passes on an unchanged default-branch commit,qaren planslists saved plans, and the using-qaren skill has the agent reuse a matching saved plan fromqaren planswhile leaving saving and flagging to QaReN. - e7d9b71:
qaren checkpersists eligible passing blocks and replays compatible saved actions before discovery, recovering from selector misses before mutation authorization as described in the CLI guide's Saved blocks section. - dc494f1: An unavailable device (
DEVICE_UNAVAILABLE) is now a refusal that exits 4 on every platform instead of a failure that exits 1. - 3d3b341: Allow named runtime inputs to opt into non-secret fixture hashing while preserving strict defaults, redaction, media withholding and symbolic replay.
- 31b18b4:
qaren checkfinishes a quoted iOS fill whose field is not an observable native input by tapping the field's one element and typing through the keyboard once, recording the step as an unverified pass instead of refusing it. - 61d63f4: Add named runtime input slots:
Fill "field" with input NAMEtypesQAREN_INPUT_NAMEwithout storing, logging or hashing it, lets a plan declareExpect input NAME on screenwhere the app shows a value, saves login blocks by input name, and refusesPLAN_INPUT_MISSINGbefore device selection when a value is missing. - 520749e: QaReN refuses an exact target whenever two distinct elements share its name, maps a field to its wrapper only when both are observed, replaces focused fills instead of appending, and reports masked or unreadable fills as unverified instead of passed, requiring a runner rebuilt with
FILL_EVIDENCE_V1. - 547d3e3: A step that does not go to plan gets one recovery (system dialog, dev menu, or the configured login block) before it fails, a React Native red box fails at that step with its message, and
.qaren/config.yamlgainsloginBlockandloginMarker. - 1304943: Add
qaren doctor, a read-only check of the host toolchain, the installed qaren, the app config and rn-dev-agent 1.x leftovers. - a01bdf5: Use the app's Expo fingerprint alongside native inputs and Xcode for iOS build reuse, with a recorded native-input fallback for static configs when Expo evaluation is unavailable.
- c750bb2: Each release ships a verified qaren tarball for Apple silicon Macs, installed explicitly with
ensure-qaren.sh --installinto~/.qaren/runtime/<version>/while SessionStart checks stay offline. - b7b4f5e: qaren reuses a verified iOS native build from a per-user machine store, keyed by the native fingerprint plus the Xcode version, so a fresh clone or
qaren prworktree with unchanged native inputs installs it instead of rebuilding. - 6156956: Add
qaren check, which leases the booted simulator, builds and launches the working tree, spawns the core walker over a one-line-per-JSON wire, walks a literal plan with exact targets and quoted ✓ lines, and writes a ledger and report under~/.qaren/runs/<run-id>/. - f13667a:
qaren prruns the plan on a PR head with a local screen recording, andqaren publishposts the reviewer comment with eligible video and saves discovered blocks back to the PR branch. - 0621740: Add
qaren update, which fast-forwards the rn-dev-agent checkout it is run from, rebuilds and reinstalls qaren, and reports whether the build is already current, updated or newly installed. - ca9a48c: Add a read-only terminal viewer for value-free live QA stages, step timings, cleanup and verdicts with plain and JSON modes.
- 4666c24: Resolve phrase targets and checks with pinned Jev judgments, preflight credentials before device claims, and report bounded model calls with masked inputs.
- 5b7eab4: CLI inspection and pull-request block preservation now accept verified main-copy saved-block links, and receipts, run reports and pull-request comments report how many blocks replayed.
- 61d63f4: Save and replay a runtime-input fill typed through the keyboard fallback or proven focus by the field testID the focus proof used.
- 2979b0a: Screen privacy now comes from the native snapshot plus typed and previously seen values, so large apps no longer refuse at step one, a missing React digest only reduces semantics, and text outside the native viewport no longer satisfies literal checks.
Patch Changes
-
99b0805: Prevent cancelled walks from continuing setup or saving actions, bound attachment by its remaining budget, and refuse reuse for unscanned native dependencies.
-
9c83b42: Phrase steps ignore React elements under a view hidden from accessibility, such as an inactive navigation tab, when accounting for on-screen controls and evidence.
-
963a75f: A failed Android farm key fetch no longer records any key bytes, and any command output or evidence that mentions a private key is withheld whole from run records, receipts and logs.
-
046b14c: Stop iOS fresh-install admission from refusing when an agent CLI running under a script interpreter carries driver names, paths or the simulator ID in its prompt arguments.
-
e8996e1: Retain value-free admission facts in private diagnostics at each attach retry and on cancellation or refusal, report failed iOS liveness probes as
probe_failed, and record an unanswered pre-attach runner-host probe in the receipt. -
00a8296: Classify pre-admission deadlines as environment refusals and withhold publication handoffs when cancellation arrives during terminal persistence.
-
4cb6e35: Stream iOS admission process scans so large host process tables no longer cause fresh-install refusal.
-
bd3f941: Core CDP attachment refusals now attempt one value-free record in the run's core log naming the last admission boundary, the typed error class and any available platform-inference evidence, with refusal codes and exit behavior unchanged.
-
1041a4d: Stop iOS fresh-install admission from refusing when a shell, including macOS
/bin/sh, runs a-ccommand string mentioning runner names, runner paths or the simulator ID. -
babe9e3: Keep the iOS screen reader running when an advisory settle snapshot times out, leaving recovery to the next real command.
-
7944172: Confirm unique aggregate-labelled scroll targets on ordinary captures without granting press authority or weakening privacy and contradictory-evidence refusals.
-
19d41fb: Let quoted waits pass when the text is a whole segment of one visible aggregated accessibility label, as quoted scroll-until already does.
-
b9be8c4: Prevent native-client reuse when a local config plugin reads ambient process inputs.
-
d052343: Stop runner setup, artifact fallback, and port acquisition through ambient walk cancellation even when callers omit an explicit signal.
-
0dbf84c: Leave a borrowed Android emulator as it was found: teardown and
qaren cleanupremove the run's Metro reverse and its runner while the run holds the device lease,adb reversenever rebinds an existing mapping and a run cancelled before its reverse leaves the port alone,--remove-apprefuses on a borrowed emulator, andqaren statusprobes it through the borrowed adb server. -
2f3bd11: Android dev-client launches pass Expo's
EXDevMenuDisableAutoLaunchextra so the dev menu no longer opens over the app at launch. -
3082f5f: Keep typed values out of Android device logs: the Android runner's exact fill writes through the bound field's accessibility node, its key-event fallback injects keys without logging their codes, and its device-tool snapshot reads the windows itself so off-screen text is never logged; the snapshot's output is unchanged. A cleared Android field that is empty and shows no hint now reads back as empty, so a plain clear verifies as exact.
-
abbce70: Let
--fresh-installrun on Android inqaren checkandqaren pr: the CLI first proves no other UI driver (UIAutomator, Maestro or another instrumentation) is running on the borrowed emulator, refusing when the process scan is busy or unreadable, then uninstalls the app with its data and proves it absent before the walk continues. -
693be73: Recognize Android header-role text on API 28+ and explain literal-check near misses. A button whose header title repeats its label stays pressable by that label. See the screen-evidence contract for eligibility and diagnostic rules.
-
a388081: Prevent Android keyboard dismissal from pressing BACK when the keyboard is hidden and require hidden-state and unchanged-screen evidence before reporting success.
-
3820416: Android QA snapshots now carry a validated native presence capture: the runner observes each element under the display density and refuses what it cannot prove, scrolled-out elements read as off screen, and elements under the keyboard read as covered, not scrolled out.
-
fa63876: Serialize Android runner APK refresh, hash comparison and installation with stale-retry invalidation within each worktree. Concurrent starts on different devices wait for the same outputs, with a bounded
ANDROID_RUNNER_APK_LOCK_TIMEOUTrefusal, while the rebuild budget and cleanup transitions remain unchanged.Record Android APK fence ownership by process birth and group. Cancellation stops owned build/install children before release; follow-up starts reclaim only proven-dead owners with no surviving group. Live-owner waits disclose owner and fence, and unprovable liveness refuses immediately with recovery steps.
-
dc494f1: The Android runner is always built from the shipped source instead of installing a published runner APK.
-
1ab08b0: Safe-area roots associate on Android as well
-
974eb9b: On Android, rows scrolled out of a scroll container such as a ScrollView, ListView or RecyclerView are treated as offscreen and clipped to that container, as on iOS.
-
b3c91d6: Android QA can now accept or dismiss a system dialog, such as the notification permission prompt: the runner taps exactly one labelled button in the dialog above the app, including a prompt that takes the app's place on screen, and the step passes only when that dialog is gone and the app is back in front.
Don't allowis now a dismiss label. While a system dialog is in front, QA sends no scroll or back to the app, ends a step that would tap the dialog's own button withOUTSIDE_APP_WINDOW, and leaves a prompt to the plan's next dialog step when there is one. -
3c5c1ba: Verify an Android tap whose screen draws after the first window check: when the hierarchy still looks unchanged, wait once more for a window update inside the remaining settle budget before reporting that the tap had no effect.
-
c6f06b0: Give Android walks the same visibility, keyboard, occlusion and foreign-driver answers as iOS: QA reads on Android take one live traversal of the accessibility windows with roots, ancestry, a keyboard node and a snapshot generation, a tap whose only visible effect is opening the keyboard counts as a screen change, and the runner's exact tap on an editable field returns once that keyboard is shown, within the tap's post-act settle budget, with a failed step naming a keyboard that did not open, a front dialog of another app reads as a dialog, a control not visible to the user counts as off screen unless the keyboard covers it, which reads as keyboard-covered as on iOS and gets one clearance scroll sized to clear the keyboard, the runner refuses a covered tap or focus tap as
FOCUS_TARGET_OCCLUDEDso the walker scrolls once and retries, and admission refuses when another automation driver holds the emulator. -
d31c304: On iOS, read label provenance for an unidentified, unlabelled wrapper that echoes one control's label, so a quoted press on that single visible control no longer refuses as
TARGET_AMBIGUOUS. -
014536c: Fail a check within seconds with
APP_EXITED_AT_LAUNCHwhen the launched app exits, naming the crash report's missing library on iOS, instead of polling until the readiness deadline. -
da37455: Keep verified dev-client artifacts on
ARTIFACT_INSTALL_FAILED(install-failedandinstall-timeout) and recommend target/package-service/transport diagnosis after cleanup, reserving rebuild advice forartifact-invalid. -
b557725: Preserve the previous native cache when cancellation interrupts staged build publication.
-
fb085be: During launch, the dev-build check and helper injection wait for the app's answer until the existing attach deadline instead of a fixed 5 s, so a slow but live app attaches, and an expired wait is classified as a readiness timeout.
-
dc6ecdb: Warm iOS dev-client launches and the single admission relaunch with margin, record each preparation attempt within its budget, retry matching candidates with failed platform evidence, and include manifest timing in attach refusals.
-
0f105f5: Wait for a Metro target matching the requested
appIdbundle (and on iOS, the run's simulator name on that same target) before exact target selection and record value-free device/bundle selection refusal diagnostics. -
b260643: Allow attested unscoped Maestro MCP control processes to coexist with QaReN checks while preserving refusal for observed active or unproven device automation.
-
0a13863: Wait briefly for initial debug-target registration on the owned Metro port before applying unchanged app-identity checks during QA startup.
-
fee9d54: Mask short filled codes in Button-cell rows with blank siblings across outward evidence.
-
df796f4: Keep same-label siblings distinct and let a wrapper around a text input stand for that input in the keyboard fallback.
-
6f20158: Add explicit
--restart-deviceand--shutdown-device-on-exitoptions for borrowed iOS simulators onqaren checkandqaren pr, gated by an exact--device <UUID>, strict foreign-driver admission, verifiedShutdown/Bootedreadbacks, and lease-held teardown and recovery. -
7085bb5: Allow up to 64 combined iOS admission candidates under unchanged identity, scoping, privacy, and time bounds, refusing larger scans before observation.
-
c3c3d87: Refuse a dev-client attach with no target whose app exited or never requested a bundle after the configurable
deadlines.attach_seconds, retain attach-state diagnostics, and re-prove transient runner-host cleanup failures before retaining the device lease. -
04792af: Provide bounded, content-free kernel inspection of oversized iOS process arguments without relaxing native-driver scoping or exact-argv limits.
-
0a13863: Resolve ambiguous iOS driver scans with bounded native executable identity while preserving unknown-driver refusals.
-
b39d6ff: Bound whole-assertion and visibility judgments to established private-safe screen evidence, propagating content-free refusal diagnostics and pinning final input association and frozen eval regressions.
-
e7f7d4b: Enforce finite-build lifecycle transitions in the run resource model and retain build locks and device leases until proven cancellation or process-group retirement is durably recorded.
-
e305093: Revalidate unpushed saved-block commits against current privacy admission before retrying publication.
-
8f14428: Refuse publication of cancelled runs and keep static accessibility roles noninteractive unless press handlers are observed.
-
7944172: Reserve time for native candidate provenance replies and resolve equal-frame ancestor echoes without a provenance read.
-
f498a9b: Preserve canonical saved-block identifiers for file operations while masking display output.
-
08c6619: Keep content-free native containers out of phrase-check contributions when only a screen-wide React capability gap leaves their press or fill unknown.
-
3a0198c: Keep QA capture, hidden-aware presence association, quiet preparation, observation-use deadlines and native mutation lifecycle guards aligned with bounded content-free diagnostics.
-
1df2be4: A fill whose read-back differs from the typed text only by letter case passes as unverified with its character count instead of failing, on both the native and keyboard-fallback paths.
-
640ac51: Restore centred scroll drags when no container band is supplied.
-
2c774aa: The iOS runner observes a nested React Native text once and skips nodes outside the window or their scroll view.
-
e7f7d4b: Recheck strict external-driver admission after dependency installation and Expo probing before selected-device boot or reset, and enforce CLI-owned iOS launch-scheme requirements in configuration and scenario validation.
-
28a0b58: Show live
qaren checkprogress on stderr: each command that takes over a second with its duration and each plan step as it resolves, withQAREN_PROGRESS=plain|offto force line output or disable it. -
cbdfb68:
qaren cleanupnow refuses before resource teardown and leaves the run record unchanged when a core is recorded and its qaren owner's process identity is proven still alive. -
d5897c8: Clarify Jev navigation and masked-value equality judgments without changing decision thresholds or exposing private values.
-
02d6521: Report malformed plans with their file, line, problem and repair, and honour CLAUDE_CONFIG_DIR throughout doctor’s Claude configuration checks.
-
b33365b: Keep scroll-until drags and target tap points clear of overlays without adding presence captures for quoted targets.
-
21efd3b: Allow screen capture through composite components with non-string display props while retaining strict host and identity checks.
-
a25dad8: Allow check and pull-request runs to configure bounded preparation deadlines while preserving existing defaults.
-
7944172: Resolve a single interactive control strictly inside a non-interactive container only when candidate provenance explicitly reports a clean between-sample deadline without contradictory evidence or a directly labelled outer container, retaining ambiguity refusals and value-free private diagnostics.
-
e47cb21: Tapping the first labelled control on a screen no longer refuses as ambiguous when iOS exposes an unidentified full-screen container with the same label.
-
7696865: A
SCREEN_EVIDENCE_INCOMPLETErefusal now names the coverage of each side, the codes that made a native capture incomplete, and the element whose capability is unknown. -
67605ff: A tap or fill whose target centre lies past its visible scroll area (behind a tab bar) or under the keyboard now scrolls once before any dispatch, and refuses without dispatching if it stays covered.
-
cedaa3b: Exclude unidentified custom React wrappers with separately observed interactive descendants from unmatched-control accounting while retaining views and known controls.
-
7a8403d: A slow or failing Expo dev-client floating-button preferences call is retried up to three times before
qaren checkrefusesDEV_MENU_HIDE_UNVERIFIED. -
0c078ff: A
DEV_MENU_HIDE_UNVERIFIEDrefusal from the floating-button hide now says whether the preferences call failed or the preferences did not read back hidden. -
6ad1c3e: Launch Expo dev clients with
disableOnboarding=1so the dev-menu tutorial never covers the app on a fresh install. -
93c9d3b: Phrase steps count a navigation tab once, no longer treat a React nativeID as a testID, and do not require a native match for interactive digest entries that have no testID and no handler.
-
b4c7229: Preserve unrelated same-frame text witnesses and scroll toward quoted targets using their clipping viewport.
-
99150a5: QaReN no longer adopts a session left at the 1.x
/tmp/qaren-session.jsonlocation, and onlyRN_IOS_FOREIGN_GUARD=0disables the foreign-flow gate; theRN_IOS_FOREIGN_WARNalias is gone. -
6dcf6f1: When a matching native fingerprint is incomplete because of a dynamic
app.config.*, the build decision now says so instead of reporting changed native inputs. -
f9edf2f: Start owned Metro alongside iOS native builds and cached-app installation while preserving readiness proofs and cleaning it up on failure.
-
aaa8f84: A fill naming a hidden field whose observed
-pressablewrapper echoes that name again reaches the wrapper's one-tap keyboard fallback, unless React places the field outside that wrapper. -
e0f8beb: Mask embedded contiguous secret fragments while preserving unrelated saved blocks.
-
1f8f6a3: Exclude legacy runner releases and use a prebuilt runner only when its manifest, archive name and checksum match the exact runtime version, including prerelease versions.
-
c29d2e8: An iOS app with a dynamic
app.config.*can now reuse its verified native build when the app's own@expo/fingerprintevaluation, Xcode version and native inputs are unchanged. -
326c879: Speed up iOS steps by skipping existence waits for present apps and preferring runner screenshots with a simctl fallback under the existing privacy gate.
-
07e4bb6: Record each Fill's ladder rung (
keyboard,clear-typeorset-handler) and, when typing cannot be verified on iOS, set the value once through the input's change handler, counting that pass as proof only for steps labelled(any method). -
0f270cd: A strict fill now clears the bound input before typing, so refilling an already-filled field replaces its content and verifies the exact plan text.
-
12aea9e: Mask fragments of dispatched protected code and secure-field fills across model input and public evidence regardless of layout.
-
6761475: Preflight app-local Expo build-only support, verify finite generic iOS simulator artifacts and direct Metro launcher support before exact-target installation and prompt-free launch, and retire known pre-spawn failures durably and run-local output only after verified cache publication while retaining unproven ownership.
-
1b99413: Admission now waits, within the walk budget, for React to become ready on the attached dev client before proving the bundle origin, so the first launch of a new build no longer refuses
METRO_ORIGIN_MISMATCHwhile Metro is still building the bundle. -
3d49474: Make focused iOS Fill replace a prefilled value wider than the field and stop dropping the characters typed right after the first one, so a private runtime input such as a sign-in email verifies exactly.
-
0259714: Fix iOS Fill to clear prefilled non-secure fields before typing, verify exact readback with one bounded retained-prefix recovery, and keep privacy specific to runtime input values.
-
0a13863: Consolidate forwarded React control identities along single-child wrapper chains so phrase targets resolve without hiding genuinely ambiguous controls.
-
66d0198: Preserve frameless literal text while excluding nodes with zero-area frames.
-
40b696f: Doctor now says how to finish pnpm's first-run download, warns when there is too little disk for a clean iOS build (which
checkandprnow refuse up front), and no longer tells you to delete 1.x files from an app repository. -
f6cbf1b: Phrase visibility on screens with more than 30 contributions asks Jev one question per group of at most 30 in the same request, instead of refusing.
-
9c6ae2a: Keep a phrase wait for a heading polling for its whole budget while the heading is not yet established, ending unsure instead of refusing at once or claiming absence.
-
466ef2e: On iOS, prepare writes the Expo dev-menu preferences before launch and the walk hides the floating dev-menu button through Expo's own preferences module, so the gear stays out of QA screenshots and video.
-
7de6323: On iOS a tap or focus tap whose target is covered by a bar, overlay or keyboard is refused before dispatch and retried once after scrolling the target clear.
-
2ff2ef4: Report bounded host memory, device inventory, simulator responsiveness and CPU load diagnostics during doctor and setup, and preserve tracked project files in the upgrade guide.
-
b260643: Raise the React host evidence cap from 200 to 1,000 hosts and serialize interactive evidence without silently truncating arrays, so screens with lazily mounted tabs stay capturable.
-
7270fff: Mark in-app alert and action sheet presses as unverified when closure alone does not prove the action, and preserve their receipt warnings when reason text is redacted.
-
d8e338e: Unlabeled, unidentified plain native views count as neither pressable nor fillable while React evidence accounts for every offering host, and the runner no longer spends presence time on them.
-
180573f: Step recovery declines a second, different pop-up after the first, declines each system prompt at most once per run, logs the declined dialog button, and no longer treats the app screen behind a modal as covering its header controls.
-
65ed249: On iOS, taps inside app alerts now anchor on the intended target or alert, blocked gestures refuse with
APP_ALERT_INTERRUPTIONwithout choosing an alert button, and workspace simulator builds use ad-hoc signing to preserve keychain entitlements. -
0e369a8: Build iOS apps for
qaren checkthrough the app-local Expo CLI or an explicitly configured Xcode workspace into run-local products, without passingCIto prebuild, Xcode builds or Metro. -
0f8463e: Retry the simulator's transient install-coordination refusal of the cached iOS dev client within the preparation budget.
-
4c0d8b5: Retry a timed-out cached iOS dev-client install within the preparation budget, accepting it once the installed app matches the cached build.
-
a858b26: On iOS, install/launch/ready now streams a stage event per substage, and status and cancel or deadline receipts name the substage in flight.
-
cdd39f6: Size the iOS keyboard clearance scroll from the target's keyboard overlap, as Android does, wait for the screen to settle after an iOS scroll, as after a tap, and clear a keyboard-covered keyboard-fallback wrapper with the same sized scroll before tapping it.
-
e85a599: iOS action replay now refuses a native-only command placed after exact-testID steps, which previously relaunched the app mid-flow.
-
3954293: The iOS runner's XCTest result bundles, whose activity logs record typed and displayed field text, are removed when the runner stops and before it starts, keeping the built runner warm.
-
ab92c9b: iOS system scroll-bar indicators no longer trigger private-input screenshot withholding under the exception documented in the capture and privacy contract.
-
238fe9c:
qaren checkneeds a TypeSafe key only for plans with phrase or unrecognised lines; fully quoted plans run without Jev. -
b260643: Let fresh-install admission clear long-argv processes launched through a symlink when their kernel executable is proven to be neither a device driver, shell nor interpreter.
-
974eb9b: Keyboard clearance now ends the visible band at a sticky footer and starts it below a sticky header, aims the target at the middle of that band, and lets the one clearance drag span the whole band while starting and ending inside its edges, so a field under the keyboard clears above an absolutely positioned Submit bar.
-
24597dc: Keyboard clearance scrolls now start inside the covered target's own scroll container and move its content toward the visible area, so a list row under the keyboard is lifted by the one existing clearance scroll.
-
2eb6a74: Literal checks, quoted waits and scroll-until text no longer count text painted under a visible keyboard, including the docked keyboard's chrome below its reported frame.
-
e9b600f: Text outside the cumulative screen, scroll-container and window bounds no longer satisfies literal checks, quoted waits or scroll-until when the keyboard is up or window ancestry is missing.
-
8dc8003: When the iOS keyboard is already up, a quoted fill that falls back to keyboard typing now types once into the field React positively reports focused, and otherwise types nothing.
-
cf57943: A quoted target resolves to the nearest labelled control when its single text descendant and matching outer wrappers echo its label, while distinct controls remain ambiguous with value-free candidate listings.
-
bfbeba2: A text-labelled control whose label is exposed inside it no longer fails its moved-target retry: the walker re-identifies the target with the same exact-identity rules as the step, the runner accepts the control that owned the label in the snapshot, and refusals record both elements' identity and frames.
-
3969ab0: Text from a later plan block that is visible on an app sheet no longer stops an earlier block from closing that sheet, and input runs record why an interruption was left in place in
core-diagnostics.jsonl. -
3969ab0: A later decline-label target such as "Maybe later" no longer blocks earlier sheet recovery through a different eligible control, while the control targeted by the later step remains protected.
-
2947e20: Preserve a live iOS dev client during missing-target admission waits and accept its app-job receipt after a timed-out launch instead of retrying the launch.
-
2686711: A dev-client launch that times out under host load is retried once and otherwise refused as an environment condition with the host-load reading, instead of being reported as a build failure.
-
cbdfb68: Filter the iOS launcher symbol and string probes inside the child so large debug images stay under the 16 MiB capture cap.
-
4183664: Relaunch an iOS dev client that is alive but stalled on its launcher when Metro has seen no bundle request since launch, while a slow bundle with a request in flight still keeps its load.
-
dfab1ca: Add opt-in iOS simulator booting to qaren check under its borrowed device lease with strict admission and exact-target readiness verification.
-
83d5a14: Record where each walked step spent its time in the ledger and report the median and p95 step time and the walk time.
-
943221d: Bound unresolved iOS path matching to linear time while preserving driver-pattern semantics and admission limits.
-
fe15b7e: Share literal visibility evidence across checks, text waits, scroll-until-text and replay text waits, accepting visible merged accessibility labels and keeping contradictory frames uncertain while preserving unique testID waits and native acquisition safeguards.
-
f99fadf: Refuse cached native reuse for worktree-local package plugins and bare imports whose executable inputs are not traced.
-
826e004: Each check and pr run now records local run identity metadata and the core's specific refusal code without recording flag values or harness session keys.
-
0f9418c: Exclude structural plan list numbers from protected-value matching so ordinary blocks remain savable while protected code content stays masked.
-
cae9a94: Pin maestro-runner 1.1.28 as the attested runner and saved-action floor, and keep executable payload scripts executable in the sealed runner snapshot.
-
3b30124: Restore Git-proven main-copy action links with clear refusal guidance and retain fresh-install admission probe evidence in refusals and run records.
-
7944172: Mask known runtime-input values on later screens before snapshot hashing so account details can be captured safely without replaying login.
-
aa6b938: A quoted tap can name a merged-label row by one whole segment such as Item 2, and those rows keep reporting that a check matched an accessibility label.
-
ab28182: Mask an input label that mirrors its typed or private value at any length, so a partially entered code never appears in failure output or model evidence.
-
3fbcbd4: A tap or fill the device runner refuses because its target moved, and attests it changed nothing, now fails as
TARGET_MOVED_BEFORE_DISPATCHinstead ofACTION_OUTCOME_UNCERTAIN, still without retry or fallback. -
ad47da2: A tap or fill refused because its target moved now records the retained and live frames, and the one retry waits until the same target holds one frame across two captures before dispatching again, failing with the observed frames if it never settles.
-
72bc4e7: Phrase heading waits ask Jev one narrow question, whether one of the qualified on-screen titles is the requested heading, so a real title is recognized instead of staying unestablished.
-
21efd3b: Allow complete React capture to omit non-string image-source placeholders on verified native Expo image hosts while preserving input and identity refusals.
-
8d9272d: Prevent native-client reuse for unbound Node inputs and resolve nested same-label controls to the innermost control.
-
8d7b6b3: Phrase taps treat native text, images and plain views as not pressable once complete React evidence accounts for every interactive host.
-
bd93fe6: Retain a finite native readiness reason in private capture diagnostics and restore the existing target binding when the iOS reader recovers from a timeout.
-
7d2a845: Prevent unproven legacy process signals and readiness persistence after cancellation while preserving loaded handshake timeout refusals.
-
0a13863: Run the developer-only native iOS suite under the same device lease as qaren check with durable startup and ownership-checked recovery.
-
7ddf3f2: Phrase heading steps accept an observed iOS navigation bar title as heading evidence, and treat a navigation bar itself as a plain container.
-
2be39a4: Phrase steps count a native node that is identical to its parent, as XCUI reports React Native text, as one observation.
-
bb57340: Refuse complete fingerprints for nested package resolution and show generated native regeneration before compilation in dry-run plans.
-
8715b55: Count nested unidentified text echoes as one target for actions and saved selectors while retaining identified text nodes without verified presence evidence.
-
0cc5338: Keep headings and images noninteractive when resolving screen actions.
-
7a4e813: Text whose container lies wholly outside the screen no longer satisfies literal checks, quoted waits or scroll-until, even when its own native frame reports an on-screen position.
-
26a6940: Phrase steps treat native content lying wholly outside the window or its scroll view as off screen, so visibility checks skip it and taps scroll toward it instead of refusing.
-
83c15c5: Centralize value matching and stream value-free progress while rendering reports and comments from the projected ledger.
-
ca1d205: Count only on-screen controls toward the phrase-action candidate bound and scroll toward a uniquely name-matching off-screen control up to six times with settled re-reads and unchanged-screen stopping.
-
e4b65cd: Project protected values and geometric code rows without treating hidden content as assertion evidence.
-
56a9f1c: Outbound model requests mask marker-looking free text and keep a real element's structured testID readable unless it contains a whole protected value of three or more characters, while short protected values stay masked in visible text.
-
21efd3b: Skip inactive, natively detached route screens in the error-overlay check of strict React captures, so retained tabs no longer refuse a capture of a small focused screen. Visible error overlays are still detected.
-
0a13863: Add an explicit fresh-install check that holds the device lease through reset and execution, refuses uncertain driver or app inventory observations, and requires durable reset and cleanup evidence for the phase gate.
-
a42b5e3: Cleanup after a cancelled run or a dead owner terminates the iOS runner host the run launched, on its exact simulator, and releases the device lease instead of leaving the lane busy.
-
1dfda8d: A core whose qaren owner vanished mid-walk no longer crashes on the closed wire before its teardown, so the first
qaren cleanupfinds its process group gone or proven ours. -
84ed016: Warm runs can reuse a cached native build again when app.json names package plugins or local plugins import packages: those are bound by the lockfile and the resolved package version.
-
c18c2c5: A saved text wait now replays when container labels echo its one painted line, and a wait whose text is painted more than once is no longer saved for replay.
-
4f4ca0a: Mask partial protected codes in associated cells regardless of glyph count across failure history and model descriptions.
-
342fc92: Conceal partially filled code boxes and separated three-digit values in failure history and model requests.
-
36dd474: Retain bounded value-free native capture boundary counts and actual viewport provenance in correlated private QA diagnostics.
-
b96facd: Refuse phrase actions when the selected control lacks the requested name and stop treating Header control names as heading claims.
-
7827025: Bind placeholder-labelled input wrappers and conceal nested code-box echoes and merged digits in failure history and model requests.
-
df721c3: Phrase visibility checks skip plain native containers that offer no operation and have no name of their own, instead of refusing on them.
-
a257ea1: Save ordinary blocks after a numeric code fill instead of withholding them because their generated plan hash shares digits with the code.
-
b6897ec: Exclude zero-area literal frames, scroll relative to trusted clips, report admitted media, and fingerprint optional native dependencies.
-
26a224f: Allow CLI-owned preparation to borrow an exact existing iOS simulator with explicit boot permission and lease-safe cleanup.
-
1494fb0: Reap and relaunch the iOS reader when a platform-presence capture hits the runner's main-thread timeout, so later reads are not refused by a runner that has wedged itself.
-
36dd474: Retain finite private predicate and scroll-origin facts when a target centre remains covered after its existing one-scroll allowance.
-
36dd474: Retain bounded, value-free first-refusal facts in the private QA dispatch log without changing action outcomes or retry behavior.
-
a7cf89c: Re-read transitional empty private snapshots within the existing settle and capture budgets before refusing incomplete capture.
-
a53ea4a: Reuse verified iOS builds across isolated macOS homes through a private per-UID native cache with an explicit QAREN_NATIVE_CACHE_ROOT override.
-
7944172: Resolve quoted container targets with bounded candidate-only label provenance after ordinary privacy admission, refusing unavailable provenance as target ambiguity with value-free diagnostics and provenance-relevant sample stability while preserving private-input masking and screenshot withholding.
-
0a13863: Capture React input values and secure flags through a bounded private QA channel for masking and protected checks, refusing incomplete capture and withholding sensitive screenshots.
-
24a8fca: Withhold saved blocks containing protected values and mask their display names while preserving operational identifiers.
-
5ab3ea9: Protect formatted code fragments, overlapping secure fragments, and whole-token short fills proven by observed code rows.
-
dfb1369: Associate React hosts within their proven native window so role-less controls remain eligible in multi-window captures while ambiguous ownership stays refused.
-
2532631: Release the publication lock when the publisher finishes even if a concurrent child inherits its descriptor.
-
965db66: The video
qaren publishuploads now starts when the app was admitted, so launcher and server-picker frames recorded before admission stay in the local recording only. -
cf06a1e: The posted PR video now ends on the app at the last checked step instead of showing the device session closing.
-
21efd3b: Allow strict React captures to use the existing 5,000-fiber ceiling for a single renderer root while preserving visibility rules, time limits, and refusal on incomplete evidence.
-
1b61d07: Explicit keyboard targets are no longer rejected as covered by their own keyboard.
-
95170b0: Explicit taps on keyboard-owned iOS Buttons such as Return now use the exact keyboard-target path instead of being refused as covered app content.
-
507064a: Add bounded content-free calibration timing across screen walking and native capture adapters without changing capture, dispatch, or verdict policy.
-
cf022d3: On Android,
qaren checknow waits for the JS reload that turning Fast Refresh off triggers, and re-proves the reloaded bundle before step 1, so the first step no longer reads the dev client's loading screen. Dev-menu recovery on Android no longer turns Fast Refresh off again mid-walk. -
810af8a: Select the installed Android runner by the SHA-256 identity of both APKs after refreshing the shipped-source build. Reinstall older or unprovable builds even when their package versions match, and reuse a matching installed pair.
-
6d435a2: Frame app-derived text as untrusted data in check failures and explain the boundary in reports and agent guidance.
-
a8b5639: Check a saved block's declared start screen before acting and report BLOCK_START_MISSING when it is absent.
-
9623c78: The feedback skill now drafts a sanitized QaReN issue and files it only after confirmation.
-
0e94148: Store device and host locks machine-wide by default, so any home on the machine sees an existing lease and refuses it; QAREN_MACHINE_LOCKS=0 keeps per-home locks.
-
42d6dfc: Reduce dev-client Refreshing banners in PR videos; see the walk-start clearance contract for Fast Refresh handling and platform-specific recovery behavior.
-
0369e83: The plugin now ships only the setup, using-qaren and feedback skills.
-
de3d5fc: Withhold failure screen text and mask moved-target labels whenever a captured screen contains a secure field.
-
b70ca0a:
/qaren:setupinstalls qaren onto PATH, writes.qaren/config.yamland runs a first smoke check on the simulator. -
8f5da5e: An iOS prebuilt runner that reported missing required capabilities is no longer reselected after the local rebuild, so later checks use the rebuilt runner instead of refusing
RN_FAST_RUNNER_STALE. -
7ff2c82: The using-qaren skill now explains how to plan around permission prompts and opt-in screens that may appear at uncertain times.
-
ed2d67d: The using-qaren skill now plans conditional screens from source, asks for missing
Fillvalues, and reports a correct path that stops on an unplanned screen as a plan gap rather than an app defect. -
f628a45:
qaren watchnow shows the step that is running and stops probing the run owner once the run has ended, probing a live owner at most every two seconds. -
8ea7c46: Keep watch stages and current or recent steps within the current terminal viewport, with omitted-row counts and compact or plain output for small terminals.
-
c287a58: Allow iOS fresh-install admission when unrelated long-command processes have unreadable identities while still refusing unproven automation candidates.
-
3a91d99: Resolve aggregated container labels to their hittable child controls and retain bounded, masked target-miss evidence in private run diagnostics.
-
847f205: Keep iOS settle snapshot failures advisory so the walker's after-capture decides the step after an acknowledged action.
-
dfb1369: Report value-free system-modal recognition, dialog refusal stage and interruption rejection diagnostics without changing recovery guards.
-
dfb1369: Decline system permission prompts by default and recover from eligible optional app interruptions under the step recovery contract.
-
bd93fe6: Wait for relaunched iOS runner readiness and retry private native capture within the existing settle budget before refusing.
-
985852f: Allocate distinct run directories when parallel QaReN checks start in the same second.
-
0d6f453: Retry the post-scroll screen capture once within the existing freshness allowance when association gaps for the step's kind or unresolved React identities increase or the target's visibility remains unknown.
-
5b69415:
Wait for "<testID>"no longer passes for an element whose centre is under the keyboard; plans that relied on that pass need a clearance step or a text wait. -
b03e881: A quoted wait or tap now matches a visible native element whose identifier a React-only element also carries, and a missed wait on an unchanged screen says whether the identifier was absent, ambiguous, React-only, offscreen or covered.
-
21efd3b: The "interactive digest unavailable" warning now names a fixed cause code and the capture's numeric counters, so a lost React digest can be traced to its limit, deadline or transport failure without exposing screen content.
-
6b9c687: Refuse otherwise passing walks whose JS/React connection remains lost, preserve loss and recovery facts per line in receipts and reports, and withhold learned blocks from degraded blocks and from walks that lost the connection.
-
c2d3892: A quoted fill whose testID matches both a native input and a React-only input is refused as ambiguous again before any tap, while a sole React-only match still routes to the keyboard fallback.
-
d23ae86: A fill whose field is wrapped by components that forward its testID, and that have no native input, now resolves to that one field instead of refusing as ambiguous, and ambiguity refusals say which candidates are native, React-only or proven wrappers.
-
58a663a: Share the existing reader relaunch deadline with startup and retain value-free relaunch outcomes in private diagnostics.
-
f365339: Classify transport timeouts and HTTP protocol errors as finite health details in private native readiness diagnostics.
-
2d253a0: Retry transient CDP startup, socket closure and Android Fast Refresh reload failures within the remaining run budget so concurrent runs can wait for their own app runtime to become ready.
-
180573f: Identify repeated system prompts by title and buttons, count attempted declines when read-back refuses, and explain repeated prompts after the recovery limit.
-
fa6a094: Preserve contained label-echo identity when refreshing native references without tap eligibility data.
-
7911b66: A QA walk now refuses with
DEV_MENU_HIDE_UNVERIFIEDbefore its first step when the dev-menu floating button or open menu cannot be proven hidden, re-checks clearance after a dev-menu recovery, and withholds published video if dev chrome is observed after admission. -
ef8048b: Regenerate generated native directories when native fingerprints change or are incomplete while retaining existing build caches.
-
702d744: Allow explicitly named registry auth environment variables for dependency install and prebuild while withholding their output.
-
c6217c1: Relaunch the native screen reader after timeout containment reaps it so subsequent captures can proceed.
-
65ed249: The iOS runner no longer has the positional alert command, whose dismiss tapped the last button (Allow on a permission prompt).
-
a3d723e: Resolve a stored testID on replay to its one native element instead of refusing it as ambiguous when the only other match is a React-only copy.
-
a79dce8: Replayed waits and scroll-until steps on a stored testID poll their existing budgets before reporting a missing identity.
-
b260643: Admit private screen capture on React Native dev builds, whose renderer deep-freezes host props into identity accessors and whose Fragment fibers carry child arrays, instead of refusing every screen with
PRIVATE_INPUT_CAPTURE_UNKNOWN. -
596c9d6: The core compiles a saved action into an
rn-flow/1replay plan withwalk.js --compile, refusing any command, selector or parameter outside thern-flow@1dialect with its line. -
26e4a97: Phrase steps no longer treat a plain native view as possibly pressable because of a React host or digest entry that has no testID and is interactive only by role.
-
b7b4f5e: An iOS check now attaches only to its own simulator's runtime, so another booted simulator running the same app on the shared Metro port can no longer satisfy the bundle proof while the run's app is still loading.
-
725846e: Guard process signals and setup continuations, refuse incomplete native fingerprints, and report reclaimed cleanup storage.
-
bc36e41: A run now decides publication from one final result saved after video finalization and teardown, so a late cancellation, a changed candidate or unproven process ownership is never published, and setup stops before every effect once a cancel arrives.
-
1f60728: The iOS runner no longer stops answering when XCTest records a UI automation issue during a command, such as reading a field that vanished after an auto-submitting fill, and a tap, press, drag or typing gesture that raises one now fails with
GESTURE_ISSUE_RAISEDand mutationpossibleinstead of reporting success. -
58149bc: The iOS runner's
xcodebuilddriver now runs in its own recorded process group, soqaren cleanupreclaims it by identity after its qaren owner is killed instead of refusingOWNERSHIP_UNPROVEN. -
409bdc9: Record why the iOS runner stopped answering — the readiness failure kind, a value-free runner post-mortem with app-process continuity, and per-phase fill dispatch timings — in private-run diagnostics.
-
20213a4: A failed step in a run with runtime inputs no longer writes on-screen text into its failure context or target-miss diagnostics.
-
a579343: Add the hermetic Rust replay engine that interprets compiled rn-flow/1 plans over native runners with interpreter-owned waits, single-dispatch mutations, and trace rows shaped like the existing ledger rows.
-
5feb017: Adds
RNCSafeAreaViewandRNCSafeAreaProvideras view-like hosts for native association and names the blocking association stage, including the nearest identified ancestor's, in value-free step refusal diagnostics. -
160166f: Keep scroll drags clear of Android system gesture edges and iOS status-bar and home-indicator edges.
-
13f97b2: Runs and
qaren doctornow point out a likely saved login block with the exactloginBlock/loginMarkerlines to confirm, and warn when the configured marker differs from its first screen, it fills no inputs, it signs out first, or saved blocks stay only in a throwaway worktree or PR run. -
b19268b: Fresh-install admission no longer refuses because of an automation driver that the kernel shows targeting only other simulators.
-
6ec7b55: The wholly-offscreen-container rule now applies only to text inputs, so on-screen rows under a stale off-screen container stay visible, and the viewport diagnostic names the rule and ancestor behind each excluded in-app node.
-
1951e64: Scroll-until drags inside a list span its visible band clear of the keyboard edge, content behind the list no longer shrinks the clearance band, and a band too short for a drag refuses instead of tapping.
-
f40bbbb: Scroll-until no longer refuses as ambiguous when a scroll container only echoes the label of the card inside it.
-
8596898: Phrase steps treat identical unidentified views repeated directly under a native scroll view, as iOS reports its scroll indicators, as platform chrome rather than screen content.
-
11e0455: A tap or fill after an explicit
Scrollstep now waits for the scrolled frame to settle before dispatching, instead of targeting a field still moving from the scroll's momentum. -
ac0c4c4: Scroll toward a
Scroll untiltarget that sits inside the screen but is covered at its top or bottom, such as an Android heading under the status bar, instead of scrolling away from it. -
1951e64: Start the scroll-until drag inside the list it is meant to move instead of at the screen centre.
-
9672a5a: Phrase steps associate a React Native scroll view with the native container view that carries its testID, so controls inside it are no longer left unassociated.
-
2dd650e: Remove secret-named environment variables from child processes while preserving model authentication, log redaction and scoped registry access during dependency preparation.
-
ed9d689: A fill into an iOS secure field passes again on the runner's stable masked read-back instead of failing
TEXT_ENTRY_UNVERIFIED. -
c879ded: Keep unrelated QA phrases and saved blocks readable by matching contiguous secret fragments.
-
f8ec115: Mask every contiguous three-character fragment of a secure value, including fragments that start or end with punctuation.
-
a50db6d: A press on a control whose only visible change is its selected state (such as a chip in a choice group) now proves itself by that change, and a press that leaves it unchanged still fails, naming whether it stayed selected.
-
a5aac2b: Reclaim a device lease held by a dead run through that run's own cleanup, and end a run with
RUN_CANCELLEDafter normal teardown when it is signalled or its caller exits. -
0a13863: Separate phrase presence judgments from action selection with bounded host evidence, opt-in iOS platform observations, proven host capabilities, and native-bound typographic titles that remain distinct from declared accessibility headings.
-
1532f07: The private
sensitive-pixelsdiagnostic now also lists up to four value-free carrier shapes (type code, scroll-bar label class, value shape and length bucket, size, parent type, identifier presence and value source) without any label or value text. -
7b6ad8b: Admitted complete, within-budget native captures whose own privacy verdict marks their pixels sensitive now log one bounded, value-free
sensitive-pixelsdiagnostic line to the private core log. -
5f40981: A tap or fill that first scrolls its target into view or clear of the keyboard now dispatches only after two agreeing screen readbacks, so it no longer acts on a target still moving with scroll momentum.
-
7019831: Cancel walk I/O through one signal, bound admission retries, refuse guessed native dependencies, and withhold publication until all producers are proven gone.
-
e4699f3: Retain value-free timing, dispatch and existing block and saved-login recovery outcomes for input-slot runs while preserving private-output withholding.
-
ecf7c2b: The iOS runner names toolbars
Toolbarinstead of a text input and keeps nested same-origin containers apart in snapshots, and core now requires itsSNAPSHOT_FIDELITY_V1capability so an older runner is rebuilt instead of reused. -
a9182bd: Protect codes shown in separate digit boxes from leaking through QA reporting.
-
c618467: One-time-code rows whose character text is narrower than its evenly spread cell are now recognized and shown as
[code]in failure history instead of leaking characters. -
ce28db2: A literal wait whose target stays absent on an unchanged screen until its deadline now fails with "did not appear" instead of
VISIBILITY_UNSURE: ITEM_DEADLINE_EXCEEDED. -
974eb9b: A press or fill target whose centre sits under a sticky footer or header inside its list now gets one clearance scroll toward the middle of the touchable band before dispatch, the same as a target under the keyboard.
-
d0dea68: Associate known React Native input hosts with explicit native text inputs only when existing identity, geometry, ancestry and presence proofs agree.
-
537fd3d: Host association accepts identified ancestors that match native ancestry structurally, so a covered root container no longer blocks every identified host beneath it.
-
65ed249: An iOS system dialog step taps the SpringBoard button element by its exact label and passes only when the runner proves that label was tapped and the alert closed, recording the tapped label and frame in the ledger and receipt.
-
f662eb0: Admit attested automation controllers across launcher path aliases and release device leases using owned-resource cleanup evidence instead of unrelated session activity.
-
b260643: Terminate the iOS runner's host app on the exact simulator when a session closes, so a kept simulator does not block the next check as a foreign automation flow.
-
3730528: Log value-free native match, frame, depth, hittability and viewport diagnostics when a quoted target fails.
-
2aefe14: Clear the current command when a piped or grouped child completes or fails to spawn.
-
c76f02d: Treat rows whose centres lie outside the visible list as offscreen scroll hints so untappable edge rows do not block capture.
-
d9f37b2: Retry dev-client launch and iOS dev-menu defaults timeouts regardless of host load within the remaining preparation budget, recording every attempt while preserving per-call timeouts, cancellation checks, readiness proof, and cleanup.
-
68f7358: Retain value-free typing refusal stages, native codes, and typing, readback, relayout, settle, and keyboard diagnostics in private-input runs.
-
554de1b: Compare screen state using its canonical in-memory content without hashing input text.
-
23d5e9a: A saved wait stores a testID only when no other on-screen element shares it, so a uniquely labelled control with a shared testID replays by its text instead of re-walking.
-
75b6eb1: Tell Jev that unmasked text never equals a protected value, so a check naming a typed value fails when the screen shows a different value in that place.
-
878af9b: Keep a phrase check unsure instead of asking Jev when it names a protected value that the observed screen does not show.
-
02fde6b: A build plan after an incomplete cached run no longer reports fingerprint parts the recorded build never captured, such as the toolchain, as changed native inputs; it names them as unrecorded instead.
-
240cfa2: Cleanup no longer retains a finished run's device lease when the process inventory lists a process whose state macOS cannot report.
-
6fdd880: The receipt lists each passing fill left unverified with its line and reason, and a fill the field kept short on both attempts carries its typed and held counts as numbers that masking cannot hide.
-
0453ca5: Runs that log in through the saved login block before the plan starts now publish a video that begins after login.
-
fd43d37: Admitted complete, within-budget native captures now log one bounded, value-free viewport-geometry diagnostic line to the private core log.
-
ffcf328: React-tree walk-up presses treat nested components that forward one testID and one press handler as one control even when their ancestry passes through React fiber alternates, instead of refusing them as ambiguous.
-
38863ef: Say accurately why a video is withheld for input: the plan has a fill or type step, or a login replay typed into a field.
-
5b63647: Withheld screenshots and videos now carry a value-free reason code in core.log, the ledger, the receipt and the report, and the pull request comment names the code.
-
c3f97d2: When React reads are unavailable, a keyboard fill of "X" can target the single native "X-pressable" wrapper that encloses no other text entry, reported as an unverified fill.
-
fe1c24c: Fresh-install admission no longer refuses because of an xcodebuild build that cannot drive a simulator.
1.0.13
Patch Changes
- fd4f88a:
device_fillgainsfocused: true, which types into the already focused iOS field through a synthesized text event and confirms the value through the React tree only when that named field is the focused responder, returningtyped: true, verified: falsewhen it cannot. - e0a5803: Doctor reports learned-action compatibility as its own read-only row — missing or low enginePin, regex text selectors, and unreadable actions — so a pinned-ok runner can no longer hide replay refusals behind ENGINE_PIN_MISMATCH.
- 0c2e0a4:
/qa-prposts a human-reviewer comment (the feature, the verdict, the commit and platform tested, the video, what was tested, repro steps) and then removes theneeds-qalabel when it is present. - 466b3f5: The React-tree replay refusal
frontmost proof cannot cover every mounted renderernow carriesmeta.coveragewith its reasons and renderer ids. - 466b3f5: The React-tree replay refusal's
meta.coveragenow names the phase and message of each scan error, and the frontmost prover reads style keys directly so styles it cannot enumerate no longer abort the scan. - bc219d9: Ship one committed bundled runtime with host-neutral
rn-dev-agent-coremetadata frompackages/claude-plugin, which both the Claude and Codex marketplaces now install (Codex selects its generated.codex-plugin/plugin.json,codex.mcp.json,bin/launchers andcodex-*adapters from the same directory), so a Codex local registration must point atpackages/claude-plugininstead of the removedpackages/codex-pluginruntime (GH #892). - 466b3f5: Expand TextInput designation scans to 20,000 fibers.
- 466b3f5: Raise React-tree
typeTextand input read-back to a shared 500,000-work-unit limit (a measured diagnostic, not a performance recommendation) and make TextInput designation recognize React alternates. - Updated dependencies [fd4f88a]
- Updated dependencies [e0a5803]
- Updated dependencies [466b3f5]
- Updated dependencies [466b3f5]
- Updated dependencies [bc219d9]
- Updated dependencies [466b3f5]
- Updated dependencies [466b3f5]
- rn-dev-agent-core@1.0.12
1.0.12
Patch Changes
- 14358df:
/qa-prnow proves an app-facing PR on the user path only (nocdp_navigate, deep link, or relaunch), with the Expo dev menu disabled and hidden, a recorded path, a saved or reused Maestro action rehearsed off camera, video started before the on-cameracdp_run_actionproof replay, and a GitHub comment at the live head with the video first and 390 px images.
1.0.11
Patch Changes
- 087d5b5: Pin an iOS simulator dev client with the same initial-URL launch the managed build uses instead of an openurl confirmation.
- 087d5b5: Record recognized authority refusals as one code-and-platform group across emission shapes, persist an explicit non-membership verdict, and retain runner traces for every failed replay and for recognized authority refusals without treating successful diagnostic calls as verified recovery. Stored symptoms and submitted feedback now redact UUID-shaped device identifiers. Dispose the previous CDP client before an Android managed relaunch or re-prove so Metro's inspector proxy sees one debugger per page.
- Updated dependencies [087d5b5]
- Updated dependencies [087d5b5]
- rn-dev-agent-core@1.0.11
1.0.10
Patch Changes
- ce181d2: Add first-class Cursor Plugin manifests and
${CURSOR_PLUGIN_ROOT}spawn on the Claude package, keep the process lock on Claude and Cursor spawns, make the lock-conflict message host-neutral without kill, lock-delete, or--no-lockadvice, and resolve reused Claude command/skill helper paths through a host-neutral plugin-root fallback that includesCURSOR_PLUGIN_ROOT(GH #1038, #872). - 8601c96: Sanitize
/qa-prcomments before posting, exempting only--attachdestinations; check again after rewrite. - 0ff6b0f: Redact hostname, machine UUID, and local paths from
/qa-prGitHub comments. - Updated dependencies [ce181d2]
- rn-dev-agent-core@1.0.10
1.0.9
Patch Changes
- c3fc818: Managed dev-client launches and relaunches now keep the Expo dev-menu onboarding tutorial and launch-time sheet from auto-opening over a native segment or after a reload on a fresh install, on by default and configurable per target class with
autoHideDevMenuin.rn-agent/config.json(GH #1004). - 814337d: Document the one-source-owner-per-worktree and one-exact-device-target-per-session authority bounds in the workflow skill, with pointers from the CLAUDE.md template and other workflow skills (GH #1026). Source ownership is the worktree/checkout, not only the app package root: a sibling app root in the same checkout is also blocked, and separate linked worktrees are independent copies. A session holds one
(platform, deviceId, appId)target at a time and replaces it after runner, proof, any explicitly started Observe, and any active recorder are released and no incompatible install receipt is bound. Authoritative cross-targetcross_platform_verifyis currently unsupported. - d76a9e2: Publish each release's runner zips and bundled runner trust root before the Version Packages merge advertises the plugin version, so a fresh install never verifies against a stale runner-manifest.json.
- 5c10049: Fix saved-action tab presses by resolving wrappers around a single native host and proving visibility from the owning route scope with navigator controls bound to their enclosing navigator and transparent destination providers, while preserving refusals for ambiguous controls and inactive content (HELPERS_VERSION bumped to 70 so connected sessions re-inject).
- 7ab6ae4: Normalize native recordings to 30 fps H.264 at their captured resolution through the authenticated stop boundary while preserving captured timing, stage the conversion inside the private runtime directory, refuse symlinked PR-body destinations, and embed proof screenshots and demo GIFs at 400px width in the generated PR body.
- 2085479:
rn_sessionstatus andcdp_statusnow report the running product on the envelope:product.coreVersionis the core package the live session process loaded, andproduct.pluginVersionis included when the host plugin manifest differs (GH #1025). - Updated dependencies [c3fc818]
- Updated dependencies [814337d]
- Updated dependencies [5c10049]
- Updated dependencies [7ab6ae4]
- Updated dependencies [2085479]
- rn-dev-agent-core@1.0.9
1.0.8
Patch Changes
- f1d414e: Print snapshot and find element refs as
@eN, matching the pinned form the current frame authorises for press, and accept a copied bareeNat the argv boundary (GH #979). - fa2cd21: Refuse managed-dev-client replay of flows that clear app state, and make login-path refusals name the actual next step.
- bc86d57: Add the
rn-pr-qaagent and/qa-prworkflow so a GitHub PR can be device-tested on iOS simulator, Android emulator, and physical device. - Updated dependencies [f1d414e]
- Updated dependencies [fa2cd21]
- rn-dev-agent-core@1.0.8
1.0.7
Patch Changes
- b352043: Report the managed-Metro readiness failure from the pre-kill launcher state and log tail, and raise the budget to a 90 s default configurable via
.rn-agent/config.json→metro.readinessTimeoutMs(GH #992). - Updated dependencies [b352043]
- rn-dev-agent-core@1.0.7
1.0.6
Patch Changes
- 8517ebc: Admit the canonical
expo-updatesruntime-version CLI and the verified developergitit shells out to in the Darwin managed-Metro sandbox profile, so Expo dev-client manifests under afingerprintruntime-version policy stop failing withspawn EPERM. - a7e439f: Re-pin the vendored
vercel-labs/agent-skillsbest-practice corpus to upstream commit063bee94and regenerate the rules routing index. - Updated dependencies [8517ebc]
- rn-dev-agent-core@1.0.6
1.0.5
Patch Changes
- 6d6dc39: Allow the managed-Metro Darwin sandbox to write the resolved, run-owned
react-native-css-interop/.cachedirectory while preserving all other sandbox restrictions and enforcement checks. - 6d6dc39: Accept the renamed Apple platform code-signing leaf authority during managed-Metro sandbox verification while preserving the trusted Apple chain, identity, and rejection requirements.
- 5008fd6: Discover deeply nested Redux and React Query stores and dispatch Redux actions through their Provider when an app bridge has no registered store.
- 6d6dc39: Record private per-attempt managed-Metro enforcement diagnostics with preparation outcomes, fixed-schema flags, timings, exits and predefined error categories without retaining arbitrary stderr text or changing permissions, timeouts or fallback behavior.
- 6d6dc39: Deliver the attested command snapshot to the managed-Metro sandbox preflight shim before signaling admission, so a shell-shim command no longer intermittently sources an empty snapshot and degrades enforcement to reported-v1.
- 6d6dc39: Detect a wildcard-bound Metro listener in the managed-Metro sandbox preflight by probing every local address shape, so a real Expo server that listens on all interfaces no longer reads as unoccupied and falls back to reported-v1.
- cc992e6: Omit automatic hook state from component trees while preserving existing explicit component-state inspection.
- e0d25c8: Retain a bounded closed-vocabulary projection of runner failure evidence on
maestro_runresults andcdp_run_actionRunRecords before the temporary report tree is deleted, withholding text, images, terminal output and original artifacts. - 6d6dc39: Allow the managed-Metro Darwin sandbox to look up the FSEvents service so Metro's native file watcher works on app-scale trees under enforcement instead of exhausting descriptors, without adding any executable, file, or network permission.
- e0d25c8: Recognize the rn-dev-agent instruction block in CLAUDE.local.md during workflow preflight without modifying either Claude instruction file or hiding read errors.
- Updated dependencies [6d6dc39]
- Updated dependencies [6d6dc39]
- Updated dependencies [5008fd6]
- Updated dependencies [6d6dc39]
- Updated dependencies [6d6dc39]
- Updated dependencies [6d6dc39]
- Updated dependencies [cc992e6]
- Updated dependencies [e0d25c8]
- Updated dependencies [6d6dc39]
- Updated dependencies [e0d25c8]
- rn-dev-agent-core@1.0.5
1.0.4
Patch Changes
- abd7562: Report managed-Metro cleanup obligations with missing stop-proof evidence as unrecoverable in-band while preserving the obligation and authority fence.
- 4047a2e: Resolve the Observe Device pane's live frame through the same authority-fenced device target as the mirror, so a parked runner or stale CDP no longer leaves the pane blank after
cdp_run_action. - fa85d1f: Expose observed replay runtime-state writes with exact sidecar paths—session-private when fenced and project-local under
.rn-agent/statefor unfenced compatibility—and document the current fresh-session action history. - Updated dependencies [abd7562]
- Updated dependencies [4047a2e]
- Updated dependencies [fa85d1f]
- rn-dev-agent-core@1.0.4
1.0.3
Patch Changes
- 7652515: Walk up to the nearest pressable ancestor for React-tree replay taps whose exact target is not a designatable TextInput, keeping input designation first and reporting type-path eligibility refusals as
INTERACTION_NOT_ACTUATEDwith the helper's reason, with pointer-event eligibility read on host views only and inherited root-to-leaf. - Updated dependencies [7652515]
- rn-dev-agent-core@1.0.3
1.0.2
Patch Changes
- f76cd30: Allow React-tree replay taps to designate an exact editable TextInput for only the adjacent inputText step without dispatching press or focus callbacks, and resolve a stock TextInput whose composite and host fibers share one onChangeText as a single typeText target instead of refusing it as ambiguous.
- Updated dependencies [f76cd30]
- rn-dev-agent-core@1.0.2
1.0.1
Patch Changes
- 7911ebd: Recognize a modal-hosted testID as frontmost when its React return chain threads through the modal host fiber's alternate, instead of refusing it as behind the active modal.
- 7911ebd: Re-prove the exact CDP session target before completing a partitioned native segment's deferred origin, so a passing native prefix survives the runner park/resume handoff into the React-tree suffix instead of failing with an empty before-first-step envelope.
- 7911ebd: Resume id-based iOS action waits through the React tree with Maestro-compatible timed visibility semantics.
- d30f866: Reuse completed WebDriverAgent builds across maestro-runner spawns through a toolchain-fingerprinted persistent store with atomic publication, removing the ~75 s per-invocation WDA rebuild while keeping per-spawn cache isolation, corruption fallback, and cold-build behavior.
- Updated dependencies [7911ebd]
- Updated dependencies [7911ebd]
- Updated dependencies [7911ebd]
- Updated dependencies [d30f866]
- rn-dev-agent-core@1.0.1
1.0.0
Major Changes
- 52f183f: Require Node 24 or newer and authenticate both Node calling conventions for managed-Metro descendant spawns so supported runtimes bind Metro without weakening strict proof.
Patch Changes
- 4c417dc: Report unreadable process births as unavailable while preserving genuine authority identity-mismatch refusals.
- Updated dependencies [52f183f]
- Updated dependencies [4c417dc]
- rn-dev-agent-core@1.0.0
0.77.7
Patch Changes
- b39903b: SessionStart no longer downloads the maestro-runner: it verifies the pin-cache offline and prints the explicit pinned install command instead.
- 18b95ca: maestro_run now emits a canonical per-attempt run ledger and attaches a ledger-derived
trailingVerificationqualifier block (trailingVerificationOnly: true, existing failureKind unchanged) to a still-failed flow whose mutating commands all provably completed while only a trailing wait/assert timed out, and cdp_run_action consumes it to refuse selector auto-repair and swap the simulator-reboot advice for verify-first guidance (final goal state stays unproven; genuine wedges keep the existing reboot hint). - Updated dependencies [18b95ca]
- rn-dev-agent-core@0.72.7
0.77.6
Patch Changes
- 9f6c1d0: Route iOS exact-testID actions through the authoritative React-tree prover, reconnect that exact runtime after native segments, refuse evidenced blind native WDA surfaces, and bound runner parking cleanup by the replay deadline.
- 7d70570: Surface the exact Expo Developer Menu dismissal tool and arguments in authoritative snapshots and always-loaded guidance while cleaning the exact advanced operation generation when recoverable admission later fails.
- 9dbb2c9: Consolidate Metro origin and session authority at the signed initial-bundle handshake while keeping launch endpoint checks diagnostic-only.
- b121a18: Delete the login prologue authority latch so
cdp_login_prologueruns the exactuser-loginaction and passes or fails like any other replay, and no tool is disabled because of the login result. - Updated dependencies [9f6c1d0]
- Updated dependencies [7d70570]
- Updated dependencies [9dbb2c9]
- Updated dependencies [b121a18]
- rn-dev-agent-core@0.72.6
0.77.5
Patch Changes
- c3a202e: Resolve wrapped React text handlers exactly while keeping native fills single-shot and verified.
- Updated dependencies [c3a202e]
- rn-dev-agent-core@0.72.5
0.77.4
Patch Changes
- 8616624: Bound the system-dialog fallback presence probe to a 15-second default (explicit caller timeouts through 120000ms remain accepted) so Android and session-less iOS calls with no dialog return typed DIALOG_NOT_FOUND promptly instead of appearing hung for two minutes, and teach the tool-docs generator to parse prettier-wrapped .describe() calls and wrapped zod chains so parameters such as timeoutMs and device_find.index are no longer published as undocumented unknowns.
- 37c65e3: Keep inherited learned-action inventory bounded and refuse inventory or replay if its operation-scoped corpus snapshot changes.
- 804aefb: Fix Observe HTTP request-body handling by preserving multi-byte UTF-8 code points split across TCP chunks and draining oversized bodies while returning parseable JSON 413 responses over usable keep-alive connections.
- 017ad96: Settle SQLite supervisor relaunch spawn failures on error or exit once, with a diagnostic and no hanging promise.
- Updated dependencies [8616624]
- Updated dependencies [37c65e3]
- Updated dependencies [804aefb]
- Updated dependencies [017ad96]
- rn-dev-agent-core@0.72.4
0.77.3
Patch Changes
- 744c313: Scope writable WDA cache provisioning to iOS, keep permission regressions portable under root, and hash action IDs in feedback-safe runner diagnostics.
- Updated dependencies [744c313]
- rn-dev-agent-core@0.72.3
0.77.2
Patch Changes
- a5feed4: Make maestro-runner pin installation idempotent with independently verified temporary stages and atomic publication so concurrent or interrupted installers cannot block the cache.
- 53ffc53: Keep valid action inventory entries available around corrupt files with typed warnings and avoid allocating Maestro report directories before preflight passes.
- 663441e: Carry the session-allocated Android Metro endpoint through Expo launch, wait, native configuration, and adapter connection.
- de5350f: Keep verified runner snapshots immutable while provisioning a lifecycle-bound writable WDA cache and attaching bounded sanitized runner diagnostics to feedback.
- 067c6ff: Route auto-login through the exact bound device and refuse unbound ambient device selection with an executable authority remedy.
- 6e69ea5: The login prologue remains a fail-stop navigation helper that requires a fresh user-login RunRecord, terminally blocks credential fallbacks, and coexists with exact locked e2e login proof without serving as PR proof.
- Updated dependencies [a5feed4]
- Updated dependencies [53ffc53]
- Updated dependencies [663441e]
- Updated dependencies [de5350f]
- Updated dependencies [067c6ff]
- Updated dependencies [6e69ea5]
- rn-dev-agent-core@0.72.2
0.77.1
Patch Changes
- 6c12426: Update packaged engineering-document references to their canonical owners.
- 48018f4: Honor the allowlisted linked-worktree
.rn-agent/actionscorpus in inventory and exact-ID replay, while still refusing dangling, foreign, whole-directory, and replaced links. - d847b3b: Add verified cross-platform Expo Developer Menu dismissal with Android parity, typed readiness outcomes, and default attaching-agent surface preflight guidance.
- Updated dependencies [6c12426]
- Updated dependencies [48018f4]
- Updated dependencies [d847b3b]
- rn-dev-agent-core@0.72.1
0.77.0
Minor Changes
- b89ff50: Require pin-cache maestro-runner
>= 1.1.24, keep SHA256 attestation for the 1.1.24 artifact this package installs as the default known-good, accept learned-actionenginePinvalues at that floor or newer, and refuse PATH, ambient Maestro CLI, older runners, and unattested binaries without a Maestro CLI fallback.
Patch Changes
- 2dd53a6: Preserve arbitrary Unicode text in Android runner commands by declaring the JSON request body as UTF-8 before NanoHTTPD decodes it.
- 6889910: An incomplete renderer-root scan (missing or malformed DevTools
renderersregistry plus the empty-ID early-exit) is no longer treated as proof the app is still mounting, so a live root on a sparse renderer ID above 5 keeps the legacy no-navigation result instead ofmounting: true. - ae0097e: An explicitly present but empty M7
# mutates:or# produces:field is now treated as invalid (?/metaInvalid) instead of omitted (-), so inventory rendering matches the GH #525 present-vs-absent legend. - 800252f: A recorded owner pid that the OS has recycled into a process this user cannot inspect now counts as proven dead instead of unprovable, so startup cleanup releases the stale ownership rather than wedging the source root forever; a proven-live owner and a genuinely unreadable identity still refuse with no force-steal, abandoned blocked contenders that never held a claim are discarded at startup, every affected refusal names a concrete remedy for interactive and headless clients, and the packaged
session-doctorcommand reports and repairs a wedged root from aclaude -psession that cannot run/mcp. - b89ff50: Close remaining Maestro 1.1.24 contract gaps: refuse complete regex selector syntax, migrate
.ymlwithout writing through inherited action symlinks, and route replay/recovery/helpers through pin-cache tools instead of ambient runner or manual login. - b89ff50: Ignore AppleDouble,
._*, and PaxHeader archive members when attesting the pin-cache payload so a checksum-matching maestro-runner 1.1.24 can spawn on Darwin extract layouts. - b89ff50: Keep execute permission on the copied pin-cache
.runner-exechelper socdp_run_actioncan spawn the attested runner. - 25348eb: Keep reconnect detection-only for legacy linked-worktree root links and provide an explicit locked repair that restores per-worktree integration and local state while sharing only the learned-actions corpus.
- Updated dependencies [2dd53a6]
- Updated dependencies [6889910]
- Updated dependencies [ae0097e]
- Updated dependencies [800252f]
- Updated dependencies [b89ff50]
- Updated dependencies [b89ff50]
- Updated dependencies [b89ff50]
- Updated dependencies [b89ff50]
- Updated dependencies [25348eb]
- rn-dev-agent-core@0.72.0
0.76.7
Patch Changes
- 02b2713: Make
cdp_navigation_statereport truthful mid-mount retry guidance instead of questioning the router install right after a reload (bundled-framework evidence now comes from Metro's module registry and the dev-shell allowlist matches only exact LogBox names), add an opt-in boundedwalkUppressable-ancestor press tocdp_interactdocumented as a boolean in the generated tool docs, render learned-action metadata absence as-/pre-M7with?for any parse failure including partially malformedproducesmaps, and keep the packaged sending-feedback skill host-neutral with collector resolution owned by each host's workflow. - 2a36f7d: Add
rn_sessionactionbind_sourceso linked git worktrees can rebind the session source root explicitly: the successor session mints on the declared same-repo worktree instead of the harness startup cwd, source-consuming actions accept aprojectRootfence that refuses divergent roots with the new typedSOURCE_ROOT_DIVERGENCEnaming both paths, the release hint now names the root the successor will actually bind, install-artifact content reads get a 180s budget (was 30s) so hashing a large APK over a tunneled remote-farm adb transport no longer times out, and an autostarted Observe binding yields the device axis on the firstbind_device(GH #776). - f127182: Prove the platform of custom-named devices ("rn-qa"-style simulators) against the live device inventory — booted simctl simulator names plus, only for a session-bound Android serial, that one device's adb model (ambient adb devices are never queried) — so
cdp_connectbinds the sole healthy exact-device Metro target instead of failing with a false "found 0", and name the true failing stage in exact-connect refusals. - abcd72a: Fence
adbreads for exact-connect device/platform inference whenever an authority session is present, so an available authority with no device binding, a registry lookup that throws, or an unavailable runtime whose code is notSESSION_NOT_INITIALIZED(SESSION_OWNER_LOST,PROCESS_BIRTH_UNAVAILABLE,AUTHORITY_STORE_UNAVAILABLE) no longer falls back to ambientadb devices; only a runtime that was never initialized keeps the legacy ambient read, and rawdevice_*tools are unaffected. - fb1eea9: Observe now renders an explicit blocked device state with a typed recovery hint when session authority is unbound, bounds frameless mirror pipelines with a first-frame watchdog instead of an indefinite blank stream, and shows device-mirror readiness as its own header pill so event-stream transport liveness can never masquerade as a live mirror.
- Updated dependencies [02b2713]
- Updated dependencies [2a36f7d]
- Updated dependencies [f127182]
- Updated dependencies [abcd72a]
- Updated dependencies [fb1eea9]
- rn-dev-agent-core@0.71.7
0.76.6
Patch Changes
- 800e550: Make the iOS B155 snapshot regression test wait for the runner to actually reach the foreground before dispatching the snapshot, so a failed or delayed re-activation fails the test instead of letting it pass vacuously.
- 7eff66a: Bound dead-Metro
cdp_statusto the discovery scan budget by skipping runner-spawning picker probes when no Metro is up, and absorb the fresh-simulator first-start rn-fast-runner transient with one bounded internal retry after the failed first spawn provably exits. - 83798d9: Resolve the Observe actions/e2e project root from the bound session's declared app root (falling back to RN_PROJECT_ROOT, then heuristic discovery) and refuse truthfully on foreign, missing, ambiguous, or non-project roots instead of showing another checkout's actions or a silently empty panel.
- d046940: Admit a fresh MCP transport when the blocking claim epoch is already gone instead of rebinding the leftover blocked session.
- 28f80c0: Expose a stable
runner-splash-titleaccessibility identifier on the iOS fast runner's splash screen so launch checks no longer depend on display copy. - Updated dependencies [7eff66a]
- Updated dependencies [83798d9]
- Updated dependencies [d046940]
- rn-dev-agent-core@0.71.6
0.76.5
Patch Changes
- a43ee7d: Allow canonical detect-libc Linux getconf/ldd module-load probes through managed Metro's descendant fence without opening arbitrary descendant execution.
- Updated dependencies [a43ee7d]
- rn-dev-agent-core@0.71.5
0.76.4
Patch Changes
- d90e0ed: Fix the three linked iOS session-recovery defects from GH #750: extend the iOS exact-target readiness deadline to the Android 120s bound so the sole exact-device bridgeless target that re-registers slowly after the managed terminate+relaunch is admitted and B binds atomically (accepting an advisory
targetIdwhile B is unbound instead of refusing every id), reprofilecdp_dismiss_dev_client_pickerto run without the A/B authority it exists to restore and prove A/B through the managed-origin lifecycle after a successful dismissal, and refuse maestro-runner replays on a drifted engine pin when the flow (including runFlow-nested steps) uses regex text selectors that drifted runners mistranslate into impossible WDA CONTAINS predicates. - d56efe1: Fix
cdp_navigate/cdp_navigation_state/cdp_nav_graphnav-ref discovery failing with "Navigation ref not found" on multi-renderer bridgeless apps: the fiber walk now resolves NavigationContainer names through React Navigation 7's forwardRef wrapper (fiber.type.render) on every registered renderer. - de8d516: The legacy ambient connect handler exported by
rn-dev-agent-coreno longer dead-ends an explicit force reconnect while a supervised reconnect is in flight (it now supersedes it, with a newCONNECT_IN_FLIGHTrefusal code for non-force callers); the registeredcdp_connecttool was already safe and is unchanged. - 9475fe5: Reconnects now persist a pinned target's device and bundle identity so shared-Metro multi-simulator sessions re-bind the exact pinned device and fail closed with candidates listed instead of silently attaching to a sibling simulator.
- 9c2fc53: maestro_test_all now commits the proof-carrying install-receipt re-issue after a clearState corpus flow reinstalls the app and resolves the iOS app container from the authority-bound simulator UDID instead of generic
booted, so a corpus containing clearState flows no longer breaks install identity for every subsequent flow and tool call. - 992dafb: If the iOS idb live mirror stays alive without a first frame, Observe now fails that stream and falls back to the simctl screenshot loop on the same device instead of leaving a blank 0x0 image, with the bounded first-frame wait configurable via
observe.mirror.firstFrameTimeoutMs(default 30000). - bbe8791: Pin device sessions to their Metro origin: record the expected Metro port on the device binding and refuse cdpconnect and device* tools with METRO_ORIGIN_MISMATCH when the bound device's app is proven to be served by a sibling Metro (dev-client fallback), while unprovable origin evidence keeps the existing optional-origin behavior.
- e5a92d0: Preserve the signed install receipt's buildGeneration across an authenticated managed-Metro restart when the installed artifact re-proves byte-identical on-device, so
rn_session pin_dev_client force=truerecovers coherent authority without a ceremonial full rebuild while changed, missing, foreign, stale, or unattestable installs still fall back to the bumped generation and refuse fail-closed. - Updated dependencies [d90e0ed]
- Updated dependencies [d56efe1]
- Updated dependencies [de8d516]
- Updated dependencies [9475fe5]
- Updated dependencies [9c2fc53]
- Updated dependencies [992dafb]
- Updated dependencies [bbe8791]
- Updated dependencies [e5a92d0]
- rn-dev-agent-core@0.71.4
0.76.3
Patch Changes
- 64b29b2: Refuse maestro-runner action replay on Android below API 26 with a truthful capability diagnosis instead of an opaque install error, and point RUNNER_OWNERSHIP_MISMATCH refusals at the device_snapshot re-open repair instead of a status read that repairs nothing.
- 89ee7f4: Separate exact native device control from managed source-origin evidence so raw snapshot, screenshot, press, fill, batch, and equivalent runner operations use exact controller/source/install/device/runner authority, explicitly report
originAuthority, and keep origin-unproven captures out of strict proof, cross-platform verdicts, and learned-action evidence. - Updated dependencies [64b29b2]
- Updated dependencies [89ee7f4]
- rn-dev-agent-core@0.71.3
0.76.2
Patch Changes
- 390567b: Recover plugin-owned Android UiAutomation wedges on the exact bound device, while refusing unscoped cleanup and surfacing release warnings.
- 6142e32: Make
device_filltruthful: bind exactly one input (direct ref/testID or unique${name}-pressablewrapper mapping) before any mutation, resolve/focus/type in one exact native operation that never substitutes an ambient-focused field or blind-types app-wide, verify every attempt (JS, native, retype, Maestro, timeout recovery,device_batch) through a new required secret-freeverifyInputread-back sofilled:truealways means a stable exact value, and hard-fail unverifiable outcomes asNO_TEXT_INPUT_TARGET/TEXT_ENTRY_UNVERIFIEDwith mutation dispositions instead of soft-accepting them. - 639dd05: Scope Android element matching to the owned app by default, refuse covered exact accessibility targets before actuation, and report uncertain Android effects without automatically dispatching the interaction a second time.
- 115fdf9: Establish and safely clean exact physical Android Metro reverse forwards, and keep integrated builds on the session's resolved authority state home.
- d0c08e0: Make
DEVICE_BUSYrefusals report sanitized live-holder and bounded heartbeat diagnostics with ownership-safe close, dedicated-device, and stale-recovery guidance. - Updated dependencies [390567b]
- Updated dependencies [6142e32]
- Updated dependencies [639dd05]
- Updated dependencies [115fdf9]
- Updated dependencies [d0c08e0]
- rn-dev-agent-core@0.71.2
0.76.1
Patch Changes
- f538146: Fold the initial stale-device transfer into
bind_devicewithconfirmed: true: a proven-dead device owner is released inline through the same journaled cleanup engine with death re-proven from durable state and no capability token minted, an interrupted journal resumes token-lessly via a barebind_deviceof the same target, andrelease_stale_devicestays as a token-less compatibility alias that acceptsconfirmed: true(or a previously minted legacy handle) while live, unproven, split, foreign-worker, and mismatched-journal cases keep refusing without mutation. - 95efdf1: Launch iOS Expo dev clients through the session's authority-bound Metro when no explicit dev-client deep link was bound.
- Updated dependencies [f538146]
- Updated dependencies [95efdf1]
- rn-dev-agent-core@0.71.1
0.76.0
Minor Changes
- 7419435: Replace the text-entry fallback ladder with exact fiber/native owners that mutate once and require stable exact read-back.
Patch Changes
- e4465e5: Enforce Android exact-target readiness as one absolute 120-second wall-clock deadline, staging the exact client off-global until live proof and an atomic authority commit succeed while preserving ambient state on failure and iOS behavior.
- 26d41da: Capture sanitized local failure and recovery patterns through the existing tool observer, deduplicate and bound the evidence store, and add a read-only trend report command.
- 6c1533f: Report an installed-but-crashing fb-idb client as an interpreter incompatibility instead of looping on an "install idb" hint that reinstalls the same broken combination (#578).
- 2d4b44f: Let a Maestro flow containing a mid-flow
launchApprelaunch run to completion by re-proving the managed native origin once at flow end — reconnect-only, with no second cold start — instead of aborting between stages when the relaunched dev-client has not re-registered yet, so the flow's own post-launch steps can drive it back to the managed origin while a genuine authority mismatch still fails the run. - b2c8cc8: Redact
device_filltext from stored Observe timeline events while retaining the target, text length, status, and other diagnostic metadata. - 722349e: Let
cdp_restart hardReset=truecomplete the cold start it promises from a runner-bound session by classifying a terminated-but-unreaped process as absent rather than as an unreadable identity, and by escalating the bound runner's stop to SIGKILL after its SIGTERM grace once the pid is re-proven to carry that binding's exact birth token. - 8a7510b: Recover the session after a plugin-initiated byte-identical reinstall (for example a runner-respawn recovery) by retrying a refused install-identity preflight once behind the existing artifact-digest proof, except while a strict proof run is bound where the reinstall stays a hard stop and status projects the new install_identity_reissue_blocked state naming proof_capture discard as the way out, so rn_session status and cdp_status always report a truthful installIdentity verdict instead of claiming ready while gated tools refuse.
- Updated dependencies [e4465e5]
- Updated dependencies [26d41da]
- Updated dependencies [6c1533f]
- Updated dependencies [2d4b44f]
- Updated dependencies [b2c8cc8]
- Updated dependencies [722349e]
- Updated dependencies [8a7510b]
- Updated dependencies [7419435]
- rn-dev-agent-core@0.71.0
0.75.3
Patch Changes
- 3563b3c: Report a stale-device release that already committed as a success naming the lost fence, instead of failing the whole call with
AUTHORITY_LOST_DURING_OPERATIONwhen the authority generation moves on after the commit. - Updated dependencies [3563b3c]
- rn-dev-agent-core@0.70.3
0.75.2
Patch Changes
- 661979e: Capture navigation-initiating taps on controls mounted before recording starts without duplicating app handler calls, so saved open/close actions begin with the initiating tap instead of an unreachable visibility assertion.
- Updated dependencies [661979e]
- rn-dev-agent-core@0.70.2
0.75.1
Patch Changes
- 9a3d901: Derive every gated-tool
SESSION_AUTHORITY_REQUIREDrefusal from the session's own measuredrecoveryRequirementinstead of naming unreachableaccept_handoff/adopt_staleactions, retain and project a refused proven-dead startup cleanup as astartupCleanupBlockedcarrying its typed code and truthful remedy rather than promising that another transport restart converges — redacting the refusal at the outcome boundary so no producer diagnostic, serial, PID, or path is ever logged, journaled, or projected — and propagate the ownership-recovery contract into the replay, readiness, and discovery workflow surfaces. - Updated dependencies [9a3d901]
- rn-dev-agent-core@0.70.1
0.75.0
Minor Changes
- cafb36d: Add the
rn-workflowskill and/rn-dev-agent:run-workflowcommand that sequence the proven operating chain before a real device journey — declared package-manager install, read-only inventory, typedrn_sessionrecovery with status as the sole classifier, one exclusive device, managed integration and Metro, replay only viacdp_run_actionafter readiness proof, and reverse-order cleanup verified by the new deterministicworkflow-checkCLI.
Patch Changes
- 7cb0d40: Re-issue the install receipt after a Maestro
clearStatereinstall of the session's own artifact — proven by re-hashing the installed bytes against the bound artifact digest, with any other or unattestable artifact still refused asAPP_INSTALL_IDENTITY_CHANGED— and accept anappFileoncdp_run_actionthat otherwise resolves from that same receipt. - Updated dependencies [7cb0d40]
- Updated dependencies [cafb36d]
- rn-dev-agent-core@0.70.0
0.74.6
Patch Changes
- f20c90f: Resolve a fresh session for the next worker when the current one is released or proven stale, so
rn_session action=releaseis no longer aSESSION_OWNER_LOSTdead end and released or proven-stale rows never trigger a spuriousSESSION_AUTHORITY_REQUIRED: multiple live sessions. - b219094: Name the exact non-Git declaration remedy —
RN_DEV_AGENT_DECLARED_ROOTfor the exact existing application root andRN_DEV_AGENT_DECLARED_MANIFESTSfor the required existing manifest files — inNON_GIT_MANIFEST_REQUIREDrefusals, unavailable session status, and the canonical session-authority, setup, and readiness-workflow documentation, while keeping refusal and mutation behavior, Git-worktree identity, implicit-directory distrust, symlink containment, and the never-generated declaration unchanged. - 6716c15: Refuse orphaned integrated builds with exit code 2 and the supported
restore_integrationrepair instead of starting an unmanaged bundler, and bound every stdio-capturing session-CLI wait so wedged CLIs fail typed; projects integrated by an earlier version must re-apply integration to refresh their on-disk adapter. - Updated dependencies [f20c90f]
- Updated dependencies [b219094]
- Updated dependencies [6716c15]
- rn-dev-agent-core@0.69.6
0.74.5
Patch Changes
- d7a814f: Return a successful
release_stale_deviceenvelope only after its authenticated, device-scoped cleanup commit atomically advances the contender's fenced authority generation while preserving stale-owner death proof, exact claim epochs and handles, resumable runner/recorder cleanup, and neighboring source, Metro, install, package-integration, and port authority. - Updated dependencies [d7a814f]
- rn-dev-agent-core@0.69.5
0.74.4
Patch Changes
- 03603da: Replace default stale-owner adoption for new
grouped-v1sessions with automatic verified-dead startup cleanup: a restarting supervisor journals obligations on the proven-dead same-root session's row before any side effect, stops its recorded children by exact identity, restores package integration only from the SHA-256-verified manifest, releases claims only after every obligation is durably complete, and mints no adoption or handoff-recipient handles, while a live or unproven owner keeps refusing and legacy sessions retain the adoption surface for drain. - Updated dependencies [03603da]
- rn-dev-agent-core@0.69.4
0.74.3
Patch Changes
- c070bf0: Give Android exact Dev Client pinning a bounded cold-start readiness window so a target that passes its initial CDP probe but stalls during setup can be disconnected and re-listed once it becomes responsive, while preserving exact Metro, app, and device filtering and the existing iOS timeout.
- c070bf0: Keep the adb serial as Android authority while translating it to Expo's uniquely verified model or AVD display name only at the Expo CLI boundary, refusing missing, unauthorized, duplicate, foreign, or drifted mappings before Expo starts, pinning Expo's adb work with
ANDROID_SERIAL, and preserving serial-bound build completion and abort behavior. - Updated dependencies [c070bf0]
- Updated dependencies [c070bf0]
- rn-dev-agent-core@0.69.3
0.74.2
Patch Changes
- e4bf0c2: Make runner unbind release its exclusive claim and clear the runner binding in one atomic registry transaction, so an interrupted device close or reacquire can no longer leave a divergent store whose dead session permanently vetoes
adopt_stalewithRUNNER_OWNERSHIP_MISMATCH(GH #692). - Updated dependencies [e4bf0c2]
- rn-dev-agent-core@0.69.2
0.74.1
Patch Changes
- 9cccec7: Regroup authority-profile bookkeeping around the four ownership groups (Session, Target, Runtime, Automation) with every tool's resolved facet set, live probes, and error codes unchanged, and verify profile exhaustiveness at worker startup so an unprofiled registered tool fails at boot instead of at first call.
- Updated dependencies [9cccec7]
- rn-dev-agent-core@0.69.1
0.74.0
Minor Changes
- 5e37f16: Project strict proof as an explicit opt-in
proofOverlay(activeonly while a run is in flight betweenbegin_rehearsalandfinalize/discard) outside the groupedsession/target/runtime/automationsub-objects, keeping the existingproofchild flag and all redaction rules unchanged.
Patch Changes
- c8b03c1: Reset an exact Android CDP connection after an advertised inspector handshakes but fails the mandatory runtime probe, serially re-list only the session's allocated Metro for the same app and serial/model association, and retain the actionable probe-timeout leaf when bounded re-registration expires.
- Updated dependencies [c8b03c1]
- Updated dependencies [5e37f16]
- rn-dev-agent-core@0.69.0
0.73.0
Minor Changes
- 5365f82: Make Observe a read-only child of the session:
observe startandrestartnow require only the live session (matching autostart's degraded mode) instead of the full device/Metro/bundle/runner authority chain, while the observe-port claim, capability and instance request authentication, fenced stop/cleanup chain, and the full authority gates on the E2E run and action panels all stay exactly as before.
Patch Changes
- Updated dependencies [5365f82]
- rn-dev-agent-core@0.68.0
0.72.0
Minor Changes
- e953f49: Add an additive grouped projection to session status — a happy-path
phase(selected/building/running/closing), the internal state indetail,session/target/runtime/automationsub-objects, andobserve/proofchild flags — alongside every existing field, with unchanged redaction.
Patch Changes
- Updated dependencies [e953f49]
- rn-dev-agent-core@0.67.0
0.71.8
Patch Changes
- 76a6045: Stop a second supervisor for the same app root from misreading the live owner as a reused PID and stealing its single-instance lock, keep blocked contenders from opening operational children, rotate expired adoption handles so
statusnever advertises a capabilityadopt_stalerefuses, add a bounded capability-authenticated release for a proven-dead device or runner owner discovered after startup that transfers only the exact device cleanup obligations, and report whether recovery needs a transport restart, an attach, or an adoption. - Updated dependencies [76a6045]
- rn-dev-agent-core@0.66.8
0.71.7
Patch Changes
- 3ee229d: Keep managed Metro descendants strict by default while allowing only Expo's canonical runtime-version manifest utility without session capability and requiring exact managed launch provenance.
- Updated dependencies [3ee229d]
- rn-dev-agent-core@0.66.7
0.71.6
Patch Changes
- 5b0a93f: Keep
.rn-agentreal and worktree-local by inheriting only.rn-agent/actionsthrough consented setup and repository-local post-checkout integration, keeping SessionStart report-only, and migrating recognized legacy root links without copying mutable integration or session state. - Updated dependencies [5b0a93f]
- rn-dev-agent-core@0.66.6
0.71.5
Patch Changes
- e7c04dc: Keep session-bound Dev Client discovery and reconnect, reload, and restart recovery on the exact managed Metro port, and recognize modern Bridgeless Hermes targets whose inspector metadata omits the legacy
vmfield, while continuing to require the signed runtime marker before authority becomes ready. - Updated dependencies [e7c04dc]
- rn-dev-agent-core@0.66.5
0.71.4
Patch Changes
- 89bdf7a: Classify maestro-runner 1.1.x ID-wait misses as SELECTOR_NOT_FOUND, surface bounded head+tail failure evidence with the exact selector on every terminal path, resume reactive CDP/JS replay at the failed selector instead of redispatching executed mutations, and refuse launchApp keys the CDP transport cannot honor.
- Updated dependencies [89bdf7a]
- rn-dev-agent-core@0.66.4
0.71.3
Patch Changes
- 8cd1ea2: Fix inline Maestro cold-start timeouts and authority transitions, bridge-lifetime cleanup refusal, optional learned-action bundle gating, truthful date-picker failures, and sanitized exact-device iOS screenshots with relative-path support.
- Updated dependencies [8cd1ea2]
- rn-dev-agent-core@0.66.3
0.71.2
Patch Changes
- 802efa2: Allow runner-verified exact iOS keyboard targets to activate once while removing corruption-prone automatic keyboard swipes and rejecting stale runner artifacts.
- Updated dependencies [802efa2]
- rn-dev-agent-core@0.66.2
0.71.1
Patch Changes
- 7937883: Coalesce helper setup and reinjection per execution world, require the exact helper version, and report bounded truthful helper-health evidence.
- Updated dependencies [7937883]
- rn-dev-agent-core@0.66.1
0.71.0
Minor Changes
- 784c880: Add fail-closed fenced worktree sessions with exact build, Metro, device, runner, Observe, and strict-proof authority.
Patch Changes
- 784c880: Store the package-integration restoration manifest durably inside the session binding, let on-disk manifest bytes authorize only the current owner's restore_integration, and make stale adoption, handoff acceptance, and resumed cleanup validate their capability non-mutatingly — resumed handoff cleanup now re-proves the exact consumed handoff and its original token against a durable cleanup binding pinned to the accepting target session and claim epoch, so a stale-adoption transfer revokes the old handoff capability in favor of the adoption handle — and refuse before any transfer or mutation unless the binding itself carries a SHA-256-verified restoration manifest, reporting file-state diagnostics and the supported recovery step instead of auto-reconciling.
- 784c880: Launch Expo session builds with a command shape the installed Expo CLI accepts and release pending build authority through an authenticated abort when the native command fails before completion.
- 784c880: Guarantee single-emission signed Metro startup with gate-composed strict-proof input handling, pre-helper error evidence, allocated-port exact-device reconnects, and authoritative migration, runner, and stale-Metro recovery.
- Updated dependencies [784c880]
- Updated dependencies [784c880]
- Updated dependencies [784c880]
- Updated dependencies [784c880]
- Updated dependencies [784c880]
- rn-dev-agent-core@0.66.0
0.70.10
Patch Changes
- ef084e4: Add native Codex parity for all fifteen workflows, deterministic read-only plugin health and restart guidance, Codex-native AGENTS.md setup, and complete packaged helpers. Disable best-effort command migration and publish a usable
proof_captureaction schema while retaining strict branch validation. - Updated dependencies [ef084e4]
- rn-dev-agent-core@0.65.8
0.70.9
Patch Changes
- 0e36a39: Classify WDA bootstrap failures from full structured replay evidence without adding preparation side effects.
- 0e36a39: Close final issue #588 validation gaps by failing closed when iOS runner authority is lost after typing, reaching Bridgeless keyboard blur, honoring exact active-session lifecycle identity, accepting the packaged Codex supervisor as candidate authority, and exposing per-call blind-probe compatibility control.
- 0e36a39: Close issue #588 live-validation gaps with exactly-once keyboard recovery, propagated iOS fault controls, non-rewriting action telemetry, explicit replay evidence, and device/app-scoped native logs.
- 0e36a39: Bind Maestro replays to the exact active device, reject mismatched direct runner or WDA provenance, and persist RunRecord device identity from execution evidence instead of requested metadata.
- 0e36a39: Guard taps with versioned fresh keyboard geometry and dismiss visible keyboards before unknown-geometry interactions.
- 0e36a39: Let each iOS XCTest runner request an OS-assigned listener port so parallel simulators cannot collide on port 22088, and make listener startup failures fail XCTest instead of producing a misleading passing result.
- 0e36a39: Launch exact Android sessions on keyless AVDs and report app-launch failures separately from runner startup failures.
- 0e36a39: Allow successful action replays to append runtime telemetry when only the tracked YAML mtime baseline is stale, while retaining sidecar CAS conflict detection and strict guards for every YAML-mutating promotion or repair.
- 0e36a39: Prove Bridgeless app identity from canonical Metro metadata and prevent agent prompt text from impersonating foreign iOS runners.
- 0e36a39: Accept exact, unambiguous maestro-runner device identity from its pinned-device log and structured report, and scope Android app lifecycle to the active session's exact adb serial, while continuing to reject missing, contradictory, shared, or multi-device evidence.
- 0e36a39: Refuse explicit and session-derived CDP platform mismatches while retaining warned best-available filterless discovery.
- 0e36a39: Restore actionable component-state truncation and tree scan-budget diagnostics without expanding tool schemas.
- 0e36a39: Scope iOS attach-only app liveness checks to the resolved simulator UDID instead of the ambiguous
bootedalias, and refuse when exact device identity is unavailable. - 0e36a39: Bind strict cross-repository proof receipts to both the app fixture and the exact packaged plugin runtime.
- 0e36a39: Require exact independent readback for iOS type-timeout recovery and poison and reap the wedged runner.
- Updated dependencies [0e36a39]
- Updated dependencies [0e36a39]
- Updated dependencies [0e36a39]
- Updated dependencies [0e36a39]
- Updated dependencies [0e36a39]
- Updated dependencies [0e36a39]
- Updated dependencies [0e36a39]
- Updated dependencies [0e36a39]
- Updated dependencies [0e36a39]
- Updated dependencies [0e36a39]
- Updated dependencies [0e36a39]
- Updated dependencies [0e36a39]
- Updated dependencies [0e36a39]
- Updated dependencies [0e36a39]
- Updated dependencies [0e36a39]
- rn-dev-agent-core@0.65.7
0.70.8
Patch Changes
- e3986d3: Make Android learned-action failures, device affinity, launch accessibility readiness, restart recovery, and batched input ordering deterministic and explicit.
- Updated dependencies [e3986d3]
- rn-dev-agent-core@0.65.6
0.70.7
Patch Changes
- 9486b9f: Keep Codex MCP sessions alive when another bridge owns the project-level singleton lock, and ship a discoverable feedback skill with its sanitized collector inside the Codex plugin package.
0.70.6
Patch Changes
- af49c37: Return a consistent non-zero status when ffmpeg cannot be ensured.
- 2bf6d4f: Discover React Navigation refs and state across every renderer ID registered with the React DevTools hook, while preserving the bounded numeric renderer probe so partial registries keep legacy coverage.
- Updated dependencies [2bf6d4f]
- rn-dev-agent-core@0.65.5
0.70.5
Patch Changes
- fddcfae: Release-record correction: this entry originally claimed the ensure-idb Python 3.14 incompatibility fix, but only the release-declaring changeset merged (#601) — no corresponding code shipped in 0.70.5. The fix shipped in 0.76.0 (#578, see the 6c1533f entry).
0.70.4
Patch Changes
- f66eb3f: Isolate the empty-Metro lifecycle integration tests from live default-port Hermes targets (#577): CDP discovery's default port list (8081/8082/19000/19006 +
RN_METRO_PORT) is now resolved lazily per call, and a newRN_CDP_DISCOVERY_PORTSoverride replaces it entirely — so the integration suite owns its whole discovery surface and stays deterministic while a real React Native app is running on the host. Production discovery is unchanged when the variable is unset. - Updated dependencies [f66eb3f]
- rn-dev-agent-core@0.65.4
0.70.3
Patch Changes
- 61f136e: Fix observe UI Route/Store/Tree panels staying empty while the device mirror shows the running app (#579): the panels now auto-read live state through a new
GET /api/state/(route|store|tree)endpoint that resolves the CDP client at call time — so they populate on a healthy connection without the agent having run the introspection tools and recover after a reload/reconnect — plus a manual "read live" refresh button in each panel. - Updated dependencies [61f136e]
- rn-dev-agent-core@0.65.3
0.70.2
Patch Changes
- 619c5fe: Accept a visually matched final proof screenshot when iOS video metadata ends up to two seconds before the assertion timestamp.
- Updated dependencies [619c5fe]
- rn-dev-agent-core@0.65.2
0.70.1
Patch Changes
- fdfa8bb: Make strict proof portable, TypeScript-native, and tolerant of clean recordings up to five seconds beyond the adaptive target.
- Updated dependencies [fdfa8bb]
- rn-dev-agent-core@0.65.1
0.70.0
Minor Changes
- 4e9bf7e: Add strict storyboard-gated video and screenshot proof receipts for unattended feature delivery.
Patch Changes
- Updated dependencies [4e9bf7e]
- rn-dev-agent-core@0.65.0
0.69.1
Patch Changes
- 61c258c: Agents & skills consistency audit: new
capturing-proofskill extracted from the/proof-capturecommand so proof/demo/PR-video intent triggers without the slash command (the command and rn-feature-dev Phase 8 now delegate to it);creating-actionsgains a replay/repair troubleshooting step (Step 7) and triggers on replay-failure intent. Fixes shipped alongside: purged the last staleagent-devicereferences (rn-tester, send-feedback), reconciled rawxcrun simctl/adbinstructions in rn-tester/rn-debugger against their owndevice_screenshot/collect_logsred flags, removed manualsleepadvice that contradicted the settle engine (#385), unified flow/action output paths (.maestro/for CI flows,.rn-agent/actions/for saved actions), restored the mandatory Step 0 artifact scan in/build-and-testPhase B, correctedcdp_run_action/recorder tool names in/test-feature, registered the missinglock-e2ecommand in the plugin manifest, hardened/send-feedbackissue creation (--body-file, no shell interpolation of user text), and refreshed theusing-rn-dev-agentrouter (79 tools / 15 commands / 9 skills, new decision-tree branches for doctor, lock-e2e, observe, check-vercel-rules, send-feedback, and capturing-proof).
0.69.0
Minor Changes
- 9359723: Story 10 (GH #391) — text-input reliability recipes. iOS: the runner's
typehandler now waits (≤1 s, best-effort) for the keyboard before the first keystroke and types in Maestro's two-burst shape (first character, 500 ms pause, remainder), killing the dropped-first-keystrokes flake class; typing telemetry (typingBurst,keyboardWaitMs) surfaces in the response and threads intodevice_fill'smeta.typing. Android: the runner'stypeclassifies itsACTION_SET_TEXTread-back (accepted / transformed / rejected), falls back to per-char keyevents at Maestro's 75 ms pacing when the set was ignored, and reportsSET_TEXT_REJECTEDwhen both tiers fail. Bridge:device_fill's Android unsafe-char/length short-circuit to chunkedadb input textis removed — emoji and long text now reach the runner's full-UnicodesetTextprimary, with chunked adb demoted to a genuine last resort andSET_TEXT_REJECTEDdescending the ladder without wasted re-taps.
Patch Changes
- Updated dependencies [9359723]
- rn-dev-agent-core@0.64.0
0.68.1
Patch Changes
- 53c3fb3: Auto-heal
KEYBOARD_OCCLUDEDtap refusals JS-first (GH #379): when the iOS keyboard guard refuses adevice_press/device_longpressbecause the tap point is under an iPhone QWERTY keyboard with no dismiss control, the bridge now dismisses via the new injected__RN_AGENT.dismissKeyboard()helper (RNKeyboard.dismiss(), falling back to blurring the focused TextInput host instance), refreshes the snapshot (targets relayout when the keyboard lifts), and retries the tap exactly once — surfaced asmeta.keyboardGuard: "js_dismissed"+meta.keyboardAutoHeal. The retried tap re-runs the native guard, so a dismissal that didn't take effect re-refuses instead of tapping through. Also ships the #370 review follow-ups: the iOS refusal now carries a structuredcode: "KEYBOARD_OCCLUDED", both runners report the guard step's native duration (lifted tometa.timings_ms.keyboardGuard), andsurfaceKeyboardGuardhardens its never-throws contract against non-object JSON envelopes. - Updated dependencies [53c3fb3]
- rn-dev-agent-core@0.63.1
0.68.0
Minor Changes
- de8f1c1: Story 14 (#407): runner transport recovery — every /command carries a commandId; on an ambiguous post-send failure the client issues one short status probe against the runner's outcome journal before invalidating. Recovered results return with meta.transportRecovery; mutating verbs are never auto-resent, eliminating double-fired taps; read-only verbs may be resent once. Unresolvable probes fall through to the existing invalidation path unchanged. Both native runners (iOS rn-fast-runner, Android rn-android-runner) gained a bounded command-outcome journal (32 entries, 8 KB UTF-8 body cap, snapshot/screenshot recorded state-only, error outcomes journaled) and the read-only
statusverb that replays a prior command's retained outcome.
Patch Changes
- Updated dependencies [de8f1c1]
- rn-dev-agent-core@0.63.0
0.67.3
Patch Changes
- dc5a87b: Harden observe-recorder screenshot ingestion (GH #429): the recorder now only reads screenshot files the capture pipeline itself just wrote (single-use trust grants registered by
device_screenshot), instead of any absolute image path named in a tool observation — closing an arbitrary local-file read surface on the observe server. The read itself is now TOCTOU-safe: one descriptor for the size check and the read,O_NOFOLLOW(no symlink following), and a hard byte cap enforced on the bytes actually read. - Updated dependencies [dc5a87b]
- rn-dev-agent-core@0.62.3
0.67.2
Patch Changes
-
dba5eb7: Observe UI test confidence (#438, audit P1-A): the web SPA and the observability server now share one wire-types module, the UI carries stable
data-testidselectors, and a Playwright e2e suite exercises the real server against the committed bundle on every PR.src/observability/wire-types.ts(pure types, zero Node imports) is the single source forAgentEvent/AgentEventFamily, the e2e run shapes (E2eFlowResult,E2eRunRecord,E2eRunIndexEntry, verdict/classification unions),ActionSummary, and the action-run result. The server modules re-export it andweb/src/types.tsre-exports it too — the hand-copied twins are gone, and the web-bundle CI gate now runstsc --noEmiton the SPA so server↔UI drift is a compile error (previouslyvite buildonly transpiled, so nothing checked).- 27
data-testidattributes across Header, FilterBar, Timeline, DevicePane, StatePane, ActionsPanel, and E2ePanel. - 10 Playwright specs (headless chromium) boot the real
ObservabilityServerwith a seededRecorder+ stub e2e deps on an ephemeral port: timeline render + family/errors/search filters, event detail, device hero screenshot, SSE live update, regression history + drill-down, and the CSRF-guarded suite/action run round-trips (including a 403 negative). - Server hardening from review: oversized
POST /api/e2e/*bodies now return a bounded 413 instead of becoming an unhandled rejection, and the CSRF token is injected viaJSON.stringify+<escaping so it can never break out of the inline bootstrap script.
-
Updated dependencies [dba5eb7]
- rn-dev-agent-core@0.62.2
0.67.1
Patch Changes
-
78700be: Golden wire-contract tests from captured runner payloads + named CI gate (#437, audit P0-B).
The biggest escaped-bug cluster (#396, #353, #418) was host↔runner wire-contract drift where hand-written fixtures encoded the wrong shape, so green tests certified broken behavior. This closes that hole:
test/contract/capture-goldens.tsrecords REAL/health, rawPOST /command snapshot, error-envelope, and bridgedevice_snapshotpayloads from live rn-fast-runner / rn-android-runner sessions into committed fixtures undertest/fixtures/goldens/<platform>/, each stamped with capture provenance (device, OS, runner version, date). Goldens are captured, never hand-written.gh-437-golden-contract.test.tspins the TS parsing layer (classifyRunnerCompatibility,findRefByTestID, the ref-map oracle + snapshot verdict) against those captured payloads for both platforms, and pins the capturedvstamp toRUNNER_PROTOCOL_VERSION— a protocol bump fails CI until goldens are re-captured against the new runner (refresh cadence, enforced).- New named CI step "Runner wire-contract gate" runs the #418 tri-surface
command-enum sync, the #383 protocol-version sync, and the golden contract
tests via
yarn workspace rn-dev-agent-core test:contract, so wire-contract drift fails a visible gate instead of hiding in the unit blob.
-
Updated dependencies [78700be]
- rn-dev-agent-core@0.62.1
0.67.0
Minor Changes
-
3b27e7d: Story 16 (#409) — snapshot quality verdicts: degraded captures must say so.
Every tree/snapshot capture now carries a structured quality verdict computed once at capture time, so a sparse or empty result caused by a degraded walk is no longer indistinguishable from a legitimately empty screen:
cdp_component_treereturnsmeta.treeVerdict(state: ok|degraded|failed,path,reasons,rootsSeeded,scannedNodes,effectiveDepth,droppedSubtrees,collapsedChildLists,rendererErrors,unscannedRendererIds). Previously-silent drop classes are now counted: per-renderer exception swallows, registered-but-unscanned renderers (the #126 early-exit class), depth-cap subtree drops, scan-budget/wall-clock exhaustion, and output truncation. Requires injected helpers v34 — a stale bundle simply omits the verdict.device_snapshot(iOS + Android runners) returnsmeta.snapshotVerdict(state,source,nodeCount,refMapUpdated,reasons).- Sparse captures never overwrite the last-known-good @ref map: a zero-node
snapshot leaves refs bound to the last verified capture
(
meta.snapshotVerdict.refMapUpdated: false, reasonempty-capture) instead of wiping the map self-healing taps depend on. - Interactive consumers fail closed:
device_find(exact + fuzzy) anddevice_focus_nextrefuse a zero-node capture withSNAPSHOT_DEGRADEDrather than asserting NOT_FOUND / "nothing on screen" on evidence that cannot support it.
Patch Changes
- Updated dependencies [3b27e7d]
- rn-dev-agent-core@0.62.0
0.66.16
Patch Changes
- 2cc8c82: fix(device-system-dialog): make SpringBoard-owned iOS dialogs reachable (#545).
device_accept_system_dialog/device_dismiss_system_dialogwere Maestro-only, and Maestro's iOS driver only sees the app under test — the deeplink "Open in ?" confirmation and other SpringBoard dialogs timed out on every label probe (DIALOG_NOT_FOUND while the dialog sat on screen), and theidb ui tapescape hatch crashes upstream ("no current event loop"). With an open iOS session the tools now route through rn-fast-runner first: its snapshot returns a blocking SpringBoard modal exclusively as an Alert-rooted payload, and press resolves to a coordinate tap that lands on whatever owns the pixels. When the modal is up but no probed label matches, the tool returns the dialog's actual buttons (DIALOG_BUTTON_NOT_FOUND+availableButtons) instead of burning N×4s Maestro probes that can never match.device_deeplinkon iOS now best-effort auto-accepts the "Open" confirmation before its picker check and annotatesmeta.openDialogTapped; the iOS DIALOG_NOT_FOUND hint documents the last-resort SpringBoard restart recovery (launchctl kickstart -k system/com.apple.SpringBoard). Maestro stays as the fallback for Android, in-app alerts, and session-less iOS calls. (The issue's third finding — picker dismiss being Android-only — already shipped in #523/#531.) - Updated dependencies [2cc8c82]
- rn-dev-agent-core@0.61.9
0.66.15
Patch Changes
- 6be3bca: fix(rn-android-runner): align Android
hittablesemantics with iOS (#520). Both Android sources now route through a single shared predicate implementing the #395 definition — "enabled AND visibly on-screen": the snapshot path (window-hierarchy XML) was reporting barevisible-to-user(a DISABLED but visible control counted as hittable), and the find path (UiObject2) was reporting bareisEnabled(an enabled element with an empty visible region counted as hittable). Divergent semantics meant platform-dependentdevice_findranking (+1000 hittable boost) anddevice_batchdead-control annotation for identical screens. The newHittableSemanticsobject lives in the main sourceset so the JVM CI lane pins it deterministically; a TS grep-sync test pins the dispatcher wiring (gh-397/gh-418 style). The Android runner's/healthnow advertisesHONEST_HITTABLElike iOS. Device-verified on a Pixel 9 Pro emulator: snapshot distribution non-uniform (62/63 hittable; the fixture's new deliberately-disabled button reportshittable=false, which the old path reportedtrue), andfindTextdiscriminates enabled ("Increment" → true) vs disabled ("Disabled" → false). No wire-shape change (capability list is additive, no protocol bump); existing runner artifacts pick the semantics up on their next rebuild/upgrade. - Updated dependencies [6be3bca]
- rn-dev-agent-core@0.61.8
0.66.14
Patch Changes
- 5bde12a: Fix #377:
record_proof.sh convert-gifnow creates the output's parent directory before invoking ffmpeg, sodevice_record action=stop gif=true gifPath=<fresh-dir>/clip.gifwith an explicit path into a not-yet-existing directory succeeds instead of failing the ffmpeg write with ENOENT. If the parent cannot be created (e.g. a path component is a regular file), the command fails with an honest error naming the directory instead of a silent ffmpeg exit. Mirrors themkdir -palready done bycmd_start/cmd_stop; regression-guarded by a PATH-stubbed ffmpeg test in CI.
0.66.13
Patch Changes
- bd28f76: Fix #476:
/setupnow recognizes symlink-inherited git worktrees. Step A short-circuits template injection only when the manual marker is actually present inCLAUDE.local.md(a bare.rn-agentsymlink from the SessionStart hook instead triggers an offer to complete the wiring), a missing/stale inherited scaffold halts setup before Steps B/C can inject unresolvabledev-bridgeimports, and Step D skips scaffold/partial-add for symlinked corpora while still running the per-worktree tsconfig include touch-up. The idempotency contract and anti-patterns document the inherited state explicitly.
0.66.12
Patch Changes
- Updated dependencies [41924c4]
- rn-dev-agent-core@0.61.7
0.66.11
Patch Changes
- 74da26f: Fix #523: break the expensive iOS recovery chain. (1)
cdp_reloadthat ends with zero targets now auto-chainssimctl terminate + launchand reconnects instead of returning RECONNECT_TIMEOUT (recovered_via: terminate_launchin meta). (2) The last-connected bundleId is persisted per platform in.rn-agent/state/last-bundle-ids.json, socdp_restart hardReset:truecan relaunch even after a bridge worker restart wiped the in-memory cache. (3)cdp_dismiss_dev_client_pickernow works on iOS (snapshot/press route through rn-fast-runner — the legacy-daemon guard was obsolete), also clears the stale-server "Error loading app" dialog, prefers the picker row matching the project's Metro port, and deprioritizes stale link-local (169.254.x) entries;device_deeplinkauto-dismisses the picker on iOS too. - Updated dependencies [74da26f]
- rn-dev-agent-core@0.61.6
0.66.10
Patch Changes
- 8d778ee: hardening(hooks): wrap the SessionStart
troubleshooting.mdinjection in an explicit untrusted-data boundary (#434). The repo-local troubleshooting memory is repo-controlled content; in a cloned/untrusted repo it was previously presented to the agent as trusted startup guidance — a prompt-injection surface flagged during the #419 review. The hook now frames the block as "data, not instructions", wraps it in<untrusted-repo-notes>tags, and strips any embedded tag-like token naming the boundary (any case/decoration) so the doc cannot fake an early close and smuggle text outside the block. No behavior change for trusted repos beyond the added framing.
0.66.9
Patch Changes
- 15def1d: fix(rn-fast-runner): honest
hittablein iOS snapshots (#395).hittablenow means "enabled and its center is on-screen" (plausibly tappable, half-open viewport bounds). The old occlusion heuristic counted trailing transparent full-screen containers (gesture-handler roots, portal hosts) as occluders and marked every nodehittable=falseon real RN screens — poisoningdevice_findcandidate ranking,device_batch's dead-control annotation, and starving the hittable-first screen-rect union (PR #517) into its all-nodes fallback. Real modal occlusion was never representable anyway: RN modals get their own UIWindow, so occluded content is absent from the XCUI tree entirely. Snapshot filtering (compact/interactiveOnly) is now explicitly hittable-independent, so snapshot sizes must not grow (small decreases expected: trailing contentless overlay wrappers the old algorithm marked hittable are no longer included). Intentional behavior change: a contentless, non-interactive-typed control rendered LAST in the tree (e.g. an identifier-less icon-only Image) was previously included by position-dependent luck (no later siblings → old hittable=true → included via the hittable escape hatch) and is now consistently excluded — give such controls a testID. Consumer-side calibration for the honest flag:device_findranking now uses type priority first with hittable as a same-type tiebreak, the settle hash no longer includes hittable (it is derived from enabled + rect, both hashed, and its unquantized edge bit defeated the 4px jitter absorption), the screen-rect union is capped by Application/Window extents on iOS (center-on-screen elements can legitimately straddle the edge), and a healthy runner artifact missing the new compiled-inHONEST_HITTABLEcapability queues a one-shotmeta.noteadvisory that its hittable values are stale. The refusal half of the original #395 report ("no longer hittable" errors on modal screens) was a stale-ref message fixed by #396. No wire-shape change; new plugin releases pick this up via their per-version runner artifact. Dev checkouts: deletepackages/rn-fast-runner/build/DerivedDatato rebuild. - Updated dependencies [15def1d]
- rn-dev-agent-core@0.61.5
0.66.8
Patch Changes
- Updated dependencies [f5beabb]
- rn-dev-agent-core@0.61.4
0.66.7
Patch Changes
- c375aa4: Story 06 Phase C: on-demand LLM-behavior evals for the MCP tool surface via mcp-server-tester — tool-call-correctness fixtures against the real server (no device) and output-usability fixtures over real recorded payloads, with a committed per-model baseline whose compare script is the regression gate for Story 08 (compact snapshot format) and Story 12 (tool consolidation). Dispatch-only workflow (llm-evals.yml) requiring the ANTHROPIC_API_KEY repo secret.
0.66.6
Patch Changes
- 1f07b3f: Post-merge review fixes for the Phase B device-smoke surface (two independent reviewers, findings cross-validated): (1) the screen rect used by direction device_scroll/device_swipe and scrollintoview's viewport check is now a hittable-first union — off-screen mounted content (RN FlatList windowing keeps rows past the fold in the tree with real coords, marked hittable:false) can no longer inflate the viewport and push gestures off the physical screen; all-nodes union remains as fallback for snapshots without hittable data. (2) The three direct fastSwipe call sites fall back to resolveBundleId('ios') when a legacy session lacks appId, closing the reopened host-app-drag gap. (3) The nightly integrity lane captures zip listings before grepping (grep -q + pipefail could SIGPIPE-false-fail a successful match). (4) The smoke's counter assertion is anchored (/^count: 1$/) and the screenshot check documents its encoding-only scope.
- Updated dependencies [1f07b3f]
- rn-dev-agent-core@0.61.3
0.66.5
Patch Changes
- 3b05042: Nightly device-smoke: retry the fixture launch (two ~30s attempts) instead of a single 20s deadline. A reused-but-cold CI simulator/emulator can take longer than one short window to foreground the fixture app; the old single 20s deadline occasionally tripped ("fixture did not start within 20s"). The re-launch is idempotent.
0.66.4
Patch Changes
- 16f21f7: Make the nightly device-smoke keyboard-guard step iOS-only. iOS reliably tests the #370 verify-or-refuse contract (XCUITest reports the occluded button; the guard refuses with
KEYBOARD_OCCLUDED/dismiss_failed). Android is skipped on-device because UiAutomator drops occluded views and its IME-frame containment check is edge-sensitive — the outcome (dismiss vs a tap swallowed at the frame edge) varies run-to-run. Android'sshouldDismisspredicate stays precisely unit-tested inKeyboardGuardTest.kt(Phase A CI), so the on-device Android step added flake, not coverage.
0.66.3
Patch Changes
-
abf974f: B269 (remaining half): treat idb client health, not PATH presence, as the source of truth. fb-idb installed under an incompatible Python (e.g. 3.14) crashes on every invocation; previously it counted as "present" everywhere, so the auto-installer never repaired it and the observe mirror selected the doomed idb tier and died ("idb video-stream keeps exiting", B263) instead of using the working simctl fallback.
detectIdb()(mirror tier selection) now probes a realidb --helpinvocation — ENOENT, a crash, or a hang all resolve to the simctl tier.ensure-idb.sh's foreground check health-probes the client and flags a present-but-broken one; the background worker replaces it (uninstall → reinstall → re-probe) and, if the reinstalled client still crashes, uninstalls it and marks the attempt failed — a crash-on-invocation client is never left on PATH, and the 24h backoff retries when a fixed fb-idb release ships./doctor's idb row now scores the client by the health probe instead of PATH presence.
-
Updated dependencies [abf974f]
- rn-dev-agent-core@0.61.2
0.66.2
Patch Changes
- e4cdf48: Fix idb-companion installation on current Homebrew: brew now refuses formulas from untrusted taps, so
brew tap facebook/fb && brew install idb-companionfails with "Refusing to load formula … from untrusted tap" — the plugin's auto-installers (ensure-idb.sh,ensure-idb-companion.sh) silently failed every session while pipx still installed the (Python-3.14-broken) client, leaving the worst combination: broken client on PATH, no companion (B269). The install commands now runbrew trust facebook/fbfirst (tolerant no-op on older Homebrew without thetrustsubcommand), and all ~10 user-facing hint surfaces (doctor, rn-setup skill, mirror hints insources.ts, SessionStart warning, physical-device probe) show the trusted three-step command. - Updated dependencies [e4cdf48]
- rn-dev-agent-core@0.61.1
0.66.1
Patch Changes
-
2f7ceda: Fix the documented install command: the marketplace registers under the manifest name
rn-dev-agent, so the correct command is/plugin install rn-dev-agent@rn-dev-agent— every doc previously saidrn-dev-agent@Lykhoyda-rn-dev-agent, which fails with "Marketplace not found" (caught live on the first post-split install). Also corrects the stale~/.claude/plugins/cache/Lykhoyda-rn-dev-agentpaths in troubleshooting docs.Also adds the missing Codex install path: the repo now ships a Codex marketplace manifest (
.agents/plugins/marketplace.json) resolvingpackages/codex-plugin, socodex plugin marketplace add Lykhoyda/rn-dev-agent+codex plugin add rn-dev-agent@rn-dev-agentworks (validated live: marketplace add, plugin add, and an MCP handshake through the installed launcher). Install instructions documented in the README, docs-site getting-started, and the Codex package README; the package-sync guard asserts the manifest.
0.66.0
Minor Changes
-
272c113: Add Codex plugin metadata and Yarn workspace package boundaries alongside the existing Claude Code plugin surface so rn-dev-agent can be used from both agents.
-
272c113: Make the Claude plugin package self-contained so marketplace installs work after the workspace split (fixes the release-blocking finding on PR #500). Claude Code copies ONLY the plugin source directory into
~/.claude/plugins/cache/…—${CLAUDE_PLUGIN_ROOT}/../…references resolve to nothing in an installed plugin (docs-confirmed; the pre-split plugin worked only because the runtime lived inside the plugin root).- The package now ships a bundled runtime at
rn-dev-agent-core/dist/{supervisor,index,learned-actions}.js(same esbuild output as the Codex package, byte-identical by construction), the observe web bundle, native runner sources underscripts/rn-fast-runner+scripts/rn-android-runner,runner-manifest.json, and the helper scripts the SessionStart hook and skills invoke (ensure-*,mcp-bridge-probe.mjs,check-physical-devices.sh,check-vercel-rules.mjs). plugin.jsonMCP entry now spawns${CLAUDE_PLUGIN_ROOT}/rn-dev-agent-core/dist/supervisor.js; all agent/command/skill snippets and hooks resolve package-local paths (dev-checkout fallbacks preserved).ensure-cdp-deps.shexits fast on the dependency-free bundled runtime.scripts/build-codex-runtime.ts→scripts/build-host-runtimes.ts: the single writer for every derived host-package artifact (both runtimes, runner copies, manifests, templates, helper scripts);check-dist-fresh.shregenerates and porcelain-checks all of it, andcheck-agent-package-sync.shasserts the Claude artifacts including byte-identity of the two host runtime bundles. Therunner-artifactsrelease workflow now commits the Claude manifest copy too.- The Codex launcher ships as plain
bin/cdp-supervisor.js(was.ts) sonode <launcher>cannot hard-fail on Node 22.x below 22.18 at the file-extension gate. - New
.gitattributesmarks all generated treeslinguist-generated(bundles additionally-diff) to collapse PR review noise; runner build output inside the package copies is now gitignored.
- The package now ships a bundled runtime at
Patch Changes
- Updated dependencies [272c113]
- rn-dev-agent-core@0.61.0
0.65.10
Patch Changes
- bfb5e10: Android device-smoke keyboard-guard step: accept both non-blocked guard outcomes (
dismissedandnot_occluded) instead of pinningdismissed. Which one fires depends on the emulator's exact keyboard geometry (whether the bottom button's tap point lands inside the IME frame or at/below its edge), which varies run-to-run. The smoke now verifies the guard evaluated on-device and did not wrongly block the tap; the preciseshouldDismisspredicate stays unit-tested inKeyboardGuardTest.kt.
0.65.9
Patch Changes
- 60720e1: Make the rn-fast-runner warm-launch ready gate overridable via
RN_FAST_RUNNER_READY_TIMEOUT_MS(default 30s) so a slow CI simulator that needs longer to install+launch+attach the XCUITest runner is not a falseRN_FAST_RUNNER_DOWN. The nightly iOS device-smoke lane also now reuses the image's already-booted (warm) simulator and shuts down only extras, instead of a blanketshutdown allthat cold-boots the target and makes the runner launch time out.
0.65.8
Patch Changes
- fd8909d: Nightly iOS device-smoke lane: build the rn-fast-runner fresh each run instead of restoring DerivedData from cache. A restored DerivedData drove an unreliable
test-without-buildingwarm launch (RN_FAST_RUNNER_DOWN), whereas a freshbuild-for-testingthen warm launch is the known-good path. The ~5 min build is well within the 40 min lane timeout and the nightly budget.
0.65.7
Patch Changes
- 27f320d: Nightly device-smoke fixes: (1) the iOS lane now shuts down any pre-booted simulators before booting exactly one, so
device_snapshot open(which refuses on >1 booted iOS device) resolves deterministically. (2) The keyboard-guard step is platform-split: Android UiAutomator drops occluded views, so the occluded bottom button is absent from a post-fill snapshot — the driver now presses the pre-fill ref (its cached coords are under the keyboard) without re-snapshotting, exercising the Android dismiss contract; iOS keeps its re-snapshot + refusal-contract path (XCUITest reports occluded elements).
0.65.6
Patch Changes
- 41d6bd9: Fix direction
device_scroll/device_swipecomputing a no-op gesture on Android when no snapshot node spans the full window. The screen rect (used to size direction gestures) was picked as the largest(0,0)-anchored node; on some Android snapshots that is a ~128px top-chrome strip while the scrollable content sits below it, so scrolls dragged ~50px in the status bar and never moved the list. The screen rect is now the union bounding box of all node rects (max extent), recovering the true viewport on both platforms.
0.65.5
Patch Changes
- 8c18951: Observe UI: surface the idb install hint as a banner under the device pane header while mirroring runs on the ~6fps simctl fallback, instead of an ellipsized footer line that truncated the brew command. Error hints stay in the footer. The idb install command is corrected everywhere to include the required tap (
brew tap facebook/fb && brew install idb-companion) — including the executed installs inensure-idb.sh/ensure-idb-companion.sh, which previously failed on untapped machines./rn-dev-agent:setupnow diffs an already-injected CLAUDE.md template block against the plugin's current CLAUDE-MD-TEMPLATE.md and offers an in-place refresh when stale (new<!-- rn-dev-agent:template-end -->sentinel delimits the block; legacy blocks are upgraded on refresh).
0.65.4
Patch Changes
- df5c76e: Nightly device-smoke Android lane: scroll the golden-set list at full amplitude (amount 1) so row 80 is reached within the 30-scroll budget. The earlier amount-0.5 guard (added for a local emulator's drag latency) fell ~5 rows short on CI, where all 30 drags run with zero RUNNER_TIMEOUT — the shorter drag bought nothing.
0.65.3
Patch Changes
- 552d151: Fix the nightly device-smoke workflow failing at setup: it ran
npm ciinsidescripts/cdp-bridge, which fights the root lockfile and triggers the rootprepare: huskywithout husky installed (exit 127). Both lanes now install from the repo root (npm workspaces resolves the cdp-bridge deps) withHUSKY=0.
0.65.2
Patch Changes
- 57e7699: Fix two Android device-control defects surfaced by the Story 06 Phase B smoke: (1) with interactive-windows snapshots (#370), the status bar precedes the app window, and the screen-rect heuristic took the first (0,0)-anchored node — so direction-based device_scroll/device_swipe computed gestures inside the status bar; it now picks the largest full-bleed rect. (2) The in-tree rn-android-runner could not re-foreground an app under test on API 30+ because its manifest lacked a package-visibility declaration (getLaunchIntentForPackage returned null → "No launch intent for package …"); a MAIN/LAUNCHER queries entry restores visibility.
- 57e7699: Fix device_scroll/device_swipe silently no-oping on iOS: the drag /command body omitted the target appBundleId, so the runner cleared its target, activated its own RnFastRunner host app, and dragged on a blank screen — every coordinate scroll/swipe returned ok:true with zero movement while foreground-stealing from the app under test. All fastSwipe dispatch sites now forward the active session's appId. Found by the Story 06 Phase B golden-set smoke before it ever reached CI.
- 57e7699: Story 06 Phase B: add a nightly device-smoke workflow that drives the golden device_* command set through the real bridge (MCP over stdio) against tiny native contract fixtures (test-fixtures/{ios,android}-fixture) on a booted simulator/emulator, plus a release-artifact-integrity lane and 2-consecutive-red tracking-issue alerting. Local
npm run smoke:ios/smoke:androidrun the same golden set against a developer's own device.
0.65.1
Patch Changes
-
f74b5b7: Observe UI: make the right state pane fit its width, and slim the timeline column.
The right pane is a fixed ~26% column (~340-450px), but the actions tab rendered a 5-column table and the e2e tab 3- and 4-column tables. Tables cannot shrink below their column content, so at typical window widths the Status/Params/Run columns were clipped clean off the pane — the Run button was unreachable — and action ids line-wrapped mid-word. Both tabs now render stacked rows designed for a narrow column:
- Actions: one item per action — id (truncating, full value on hover) + status badge + Run on the first line, intent wrapped below (2-line clamp), param inputs flex-wrapping to the available width instead of fixed 110px columns, result/output underneath.
- E2E: suite results and run history as one-line rows — pass/fail mark,
truncating test/run id, duration, classification badge or
2✓ 1✗totals + verdict — with error excerpts wrapping below and the expanded run detail reusing the same row layout. - Pane guards:
.pane.rightgetsmin-width: 340px, tabs wrap instead of overflowing, long live routes break instead of pushing the pane wide. - Layout rebalance: the left timeline column drops from 40% to 33%
(
min-width: 380px; summaries already ellipsize), and the device pane no longer greedily takes all remaining width — the mirror is a portrait phone screen capped at ~100vh, so the pane is capped at 400px and the state pane absorbs the surplus instead.
0.65.0
Minor Changes
- 24842f8: Story 13 (#397) Phases 1–2: maestro-runner engine pinning and a proactive blind-probe. The installer now installs the tested pin (
1.0.9) exactly, verifies its checksum fail-closed on fresh downloads, and warns on local drift;cdp_status.replayEngine+/doctorreport engine, version-vs-pin, and known quirks;maestro_runcarriesenginePinmeta and warns once on drift (opt-in hard enforcement:RN_ENGINE_PIN_STRICT=1).cdp_run_actionon at-risk iOS runtimes (>= 26, or a recent device-matchedTRANSPORT_BLINDwith clean-pass reset) probes the CDP tree first and, when the action's anchor is visible, skips the doomed ~40s WDA attempt and replays via CDP/JS directly —RunRecordgains additivedeviceId/blindProbe, probe-routed failures classify asFALLBACK_REPLAY_FAILED(never falseTRANSPORT_BLIND), probe-routed passes never auto-promote, and the DB mirror persists the new fields. Opt out withRN_BLIND_PROBE=0.
0.64.6
Patch Changes
- 6534bf3: Make the Runner artifacts workflow self-healing (B258, second half): the gate
is now state-based — any trigger checks whether release
v<plugin.json version>already carries both runner zips +runner-manifest.jsonand builds only when something is missing — and a 6-hourly scheduled sweep catches the releases the push trigger structurally cannot see. release.yml merges Version Packages PRs asgithub-actionswithGITHUB_TOKEN, and GitHub's recursion guard suppresses workflow triggers forGITHUB_TOKEN-initiated pushes, so under the normal automated release path the artifact build NEVER fired (v0.64.4 and v0.64.5 both shipped artifact-less and needed manualworkflow_dispatchbackfills). The state-based gate also heals partially failed builds: incomplete assets → rebuild both runners, uploads--clobber.
0.64.5
Patch Changes
- f4368e4: Fix the release-triggered iOS runner-artifact build (Runner artifacts workflow):
the
build-iosjob ran onmacos-14(Xcode 15.4), which cannot openRnFastRunner.xcodeprojin project format 77 — its first real invocation (the v0.64.2 release push) failed with "future Xcode project file format (77)" and the runner manifest was never generated, so installs kept resolving to local builds. The job now runs onmacos-15(Xcode 16.x), matchingnative-tests.ymlandcodeql.yml, which already build this project green.
0.64.4
Patch Changes
-
588dedc: Sync CLAUDE-MD-TEMPLATE.md (the operating manual
/setupinjects into user projects) with everything shipped since 0.49.0. The template still documented the pre-0.55 world: it told agents to run a manual multi-minutexcodebuildpre-build (obsolete since prebuilt runner artifacts, #382), described Android dispatch as "3-tier agent-device" (removed entirely in 0.55.0), routed MMKV through rawcdp_evaluateNitro poking (superseded bycdp_mmkv), and framed multi-device screenshot routing as an open bug (#60 — fixed).Updated: in-tree runner section rewritten around prebuilt-artifact resolution, protocol/command staleness self-healing, quiescence bypass, and the foreign-flow arbiter; new reliability-layers table (settle engine, self-healing taps, keyboard guard) with opt-out env vars; perception guidance for
cdp_component_tree(interactiveOnly),device_batch finalSnapshot, and cacheddevice_find; E2E lock/suite flow (/lock-e2e,cdp_lock_e2e_test,cdp_run_e2e_suite) in the actions lifecycle; dev-menu dismiss viacdp_dev_settings hideDevMenu;device_reset_statein the auth/permission pre-flight; nine new error-recovery rows (BUSY_FOREIGN_FLOW, RUNNER_COMMANDS_STALE, KEYBOARD_OCCLUDED, RUNTIME_DEGRADED, APP_NOT_INSTALLED, TRANSPORT_BLIND fallback, post-upgrade zero-tools recovery, wrong-worktree Metro); Key Commands table gains doctor / list-learned-actions / run-action / lock-e2e and the autostarting observe UI description.
0.64.3
Patch Changes
-
d041bac: Harden the Android raw screenshot capture path (
device_screenshot, GH #428), mirroring the iOS hardening from #427:- Truncate-before-success: raw capture now stages
adb exec-out screencapbytes in a unique sibling temp file andrenameSyncs onto the caller's path only after both the write stream drains and adb exits 0. A failed or timed-out capture can no longer truncate-then-delete an existing file the tool never created. - Multi-emulator first-pick: with several emulators booted and no session
binding, resolution now refuses (exactly-one-or-null via
resolveAndroidEmu) instead of silently grabbing the first emulator — matching iOS exactly-one-or-refuse. Sessions still bind to their device id. - adb child leak on stream error: a write-stream error (ENOSPC/EACCES) now
unpipes and kills the
adbchild before settling, instead of leaving it running blocked on stdout.
- Truncate-before-success: raw capture now stages
0.64.2
Patch Changes
- 277bc81: Story 06 Phase A (#387): the native runner unit suites now execute in CI.
native-tests.ymlrunsgradlew testDebugUnitTest(ubuntu) andxcodebuild testwith a skip-list (macos-15, simulator) — path-filtered with green skip notices on TS-only PRs, unconditional on pushes to main. Local entry points:npm run test:native:android/npm run test:native:ios. Also removes a danglingRnFastRunnerTeststestable from the shared scheme.
0.64.1
Patch Changes
- f583249:
cdp_dev_settingsgains ahideDevMenuaction that dismisses the iOS expo-dev-client dev menu bottom sheet over CDP viaExpoDevMenu.hideMenu()(#335). Because it runs throughclient.evaluateinstead of a coordinate tap/swipe, it never triggers the touch-induced Hermes detach the issue describes — the JS thread stays attached and the in-memory store survives.cdp_reloadnow also best-effort auto-dismisses the menu on iOS after reconnect, so the agent lands on the app instead of behind the sheet. The dismiss resolves theExpoDevMenunative module through a multi-tier chain (globalThis.expo.modules→NativeModules→ TurboModule proxies) and is a silent no-op on non-expo builds.
0.64.0
Minor Changes
- d6f72f7: Story 05 (#386) self-healing taps: stale
@reftaps re-resolve inline by identity signature (unique-match only; ambiguous/absent STALE_REF now lists candidates), swallowed taps retry exactly once via settle-hash change detection (meta.reResolved/meta.tapRetried/meta.noUiChange), 3 consecutive no-change taps on distinct targets surface a wedged-runtime hint, anddevice_batchtestID resolution refuses ambiguous matches (AMBIGUOUS_TESTID). Opt-outs:retryIfNoChange: falseper call,RN_SELF_HEAL=0global.
0.63.0
Minor Changes
- dabe8cc: Prebuilt runner artifacts (Story 01, #382): the iOS rn-fast-runner and Android
rn-android-runner now resolve from a verified prebuilt artifact — a SHA-256-checked
local cache, then a download of the release asset for the exact plugin version —
before falling back to the on-machine build. This removes the multi-minute cold
xcodebuild/ Gradle build from the firstdevice_snapshot action=openonce a release ships the artifacts. Resolution is fail-open: any missing manifest, offline state, 404, checksum mismatch, or unsafe archive falls back to the local build with a one-linemeta.note, never a hard failure.RN_RUNNER_BUILD=localforces the local build.cdp_status//doctornow report runner provenance (prebuilt v<X>vslocal-built). Until a release ships the artifacts, builds resolve tolocalby design.
0.62.4
Patch Changes
- 8740f75: Observe UI: single-page layout — the Live/Regression view split is gone. The right column now has five tabs (route | store | tree | actions | e2e): learned actions run from the main page next to the live mirror, and E2E suite runs + history live in the e2e tab. The mirror status/hint moved to a slim footer so the device pane keeps its full height.
0.62.3
Patch Changes
- 0abb27a: Engineering rule: all new code must be TypeScript. CI gains a typescript-only gate (
scripts/check-typescript-only.sh) that fails when a.js/.mjs/.cjsfile appears outside the grandfathered baseline (scripts/js-migration-baseline.txt, 344 pre-rule files slated for migration). Shrinking the baseline (migrating to TS) passes automatically; growing it requires an explicit, reviewable baseline edit.
0.62.2
Patch Changes
- f2c9fa4: SessionStart auto-installs idb in the background (
brew install idb-companion && pipx install fb-idb) for the observe live mirror's 20-30fps fast path — never blocks session start (detached worker, pidfile guard, 24h failure backoff)./doctorand/setupgain an idb row: OK / INSTALLING (background) / MISSING with the manual command. - a33f19d: Observe UI: continuous live mirroring of the simulator/emulator screen (Maestro-style MJPEG). New
GET /api/device/mirrorstream — idb (20–30fps) or simctl loop (~6fps) on iOS, adb screenrecord+ffmpeg on Android emulators and physical devices. Zero capture cost with no tab open; per-tool-call screenshots are skipped while the mirror streams. Config:observe.mirror.enabled/observe.mirror.fps, envRN_AGENT_OBSERVE_MIRROR=0to disable.
0.62.1
Patch Changes
- 396e862: rn-android-runner
findTextrefuses missing/blanktextwith a typedINVALID_ARGUMENTerror (#444). PreviouslyoptString("text")silently defaulted to"", falling through toBy.textContains("")— which matches an arbitrary node — so a malformed request reportedfound: truefor whatever element UIAutomator visited first instead of surfacing an argument error. The guard runs in the dispatch when-branch before any selector is constructed; a source-sync test (gh-418 style) enforces it in CI without an emulator.
0.62.0
Minor Changes
- 683a132: Story 04 (#385): shared two-tier settle engine. Every mutating device_* verb now waits for the UI to actually stabilize instead of relying on fixed sleeps: Android gates on a new
isWindowUpdatingrunner probe (capabilityWINDOW_UPDATE) then falls back to snapshot-hash equality polling; iOS polls a new on-runnerisScreenStaticSHA-256 screenshot compare (capabilitySCREEN_STATIC, Maestro's 3s screen-settle budget) with the same snapshot-hash fallback. Results surfacemeta.settle: {method, settled}+meta.timings_ms.settle.device_filldrops its fixed 150ms focus delay when settle ran and pins its target coordinates once up front (--at-x/--at-y) so the settle's ref-map refresh can never retarget the fill mid-call; its corrective retypes skip settle (their stability check is the CDP read-back).device_batchsettles between steps by default at a batch-scoped 2500ms budget (per-stepsettle: falseescape hatch) and its blanket 300ms inter-step delay defaults to 0 while settle is on. Legacy runner artifacts (no new capabilities) transparently degrade to snapshot polling — no rebuild required, the new verbs are deliberately NOT in the required-command gate. Opt out globally withRN_SETTLE=0or per batch step withsettle: false; tune the per-call budget withsettleTimeoutMs(a budget knob, not a disable switch). A perpetually-animating screen settles via hierarchy stability or returnsmethod: 'timeout'at budget — bounded, never hanging.
0.61.2
Patch Changes
- 04ce7bf: SessionStart hook no longer misleads after a plugin upgrade (GH #419): the upgrade notice now recommends the field-proven cheap recovery —
/mcp→ reconnect the rn-dev-agent server — before a full Claude Code restart; a new read-only lockfile probe (scripts/mcp-bridge-probe.mjs) explicitly flags a live bridge still running from a PREVIOUS plugin install (the cause of zero-tool sessions after marketplace upgrades) naming its PID and path; and the banner no longer asserts a static "76 MCP tools" count that can't reflect actual registration — it states the installed plugin version and tells the agent the reconnect recovery path when ToolSearch finds no cdp**/device** tools.
0.61.1
Patch Changes
- c15bc52: iOS
device_screenshothonors the caller'spath(#422): iOS pixels now route toxcrun simctl io screenshoteven with an rn-fast-runner session open — the runner's screenshot verb writes inside its own sandbox and returns a relativetmp/…path the host can never serve, which blanked the observe UI panel and brokesipsresizing (meta.resize.reason: no-dimensions). simctl was already the flow-active and runner-down backend; it is now the sole iOS pixel path ("pixels → simctl", D1249). Android is unchanged (its runner honorsoutPathhost-side). Defense-in-depth: the observe recorder rejects relative screenshot paths instead of resolving them against the bridge cwd. - c15bc52:
cdp_run_actionno longer dead-ends in an opaque UNKNOWN when WDA dies at launch (#423). Root cause chain from the field failure: the #317 CDP/JS replay fallback covers this exact case, but its single tree probe ran while CDP was mid-reconnect (the failed flow had just relaunched the app), was silently swallowed, and the fallback never engaged. The probe now retries (bounded, default 3×1.5s) until the probe testID is actually present — tolerating both a reconnecting CDP and a still-mounting app — and every skip is surfaced asmeta.cdpJsFallback: { attempted: false, reason }(no-replay-deps | no-probe-testid | cdp-unreachable | testid-not-in-tree). Acdp-unreachableskip appends actionable guidance (checkcdp_status, reconnect, stop foreign XCUITest automation) instead of a bare "failure not auto-repairable". Also (#422 hardening): the simctl UDID parsers now only consider iOS runtimes (a booted paired watchOS/tvOS simulator can neither win the screenshot UDID pick nor make the single iPhone look ambiguous toresolveIosUdid), and raw captures bind to the open device session's UDID when platforms match instead of picking the first booted device. - c15bc52: iOS cold start persists a reusable
.xctestrun(#424):startFastRunner()now runsxcodebuild build-for-testingfirst when no test product exists and then launches via the sametest-without-buildingpath as every warm start, instead of a single barexcodebuild test— which never writes a.xctestrun, so self-built runners were permanently "not prebuilt" and every runner death cost another multi-minute cold build. The build phase keeps the 360s cold timeout; the launch phase uses the standard 30s ready window. The #418 stale-artifact rebuild tier funnels through the same path, so it also leaves a reusable artifact now.
0.61.0
Minor Changes
- 8a21532: Command-surface gate (#418, B235): both native runners enumerate their supported
commands in
/health.commands(iOS derives it fromCommandType.allCases, Android from a sync-testedSUPPORTED_COMMANDSlist) and the liveness gate classifies a runner missing any bridge-required verb as stale (missing-commands). Remediation is tiered:device_snapshot action=openauto-invalidates the stale artifact and rebuilds — iOS deletes DerivedData and cold-builds (once per plugin version, behind a checkout-scoped build lock), Android deletes the runner APKs so self-install Gradle-rebuilds; mid-flow device tools refuse fast withRUNNER_COMMANDS_STALEinstead of silently building. An unknown verb reaching the iOS runner now returns a typedUNSUPPORTED_COMMANDerror instead of a raw Swift decode failure. Root cause of B235 fixed: the explicit iOS keyboard-dismiss path posteddismissKeyboard, which no Swift artifact ever accepted — the wire verb is nowkeyboardDismiss.cdp_statussurfacesdeviceSession.runnerProtocol.missingCommands. Hardening from per-edit review: the iOS runner validates client-supplied Content-Length (400 on invalid instead of crash/hang) and Android foregrounds alias verbs (press/fill/scroll) before dispatch.
0.60.0
Minor Changes
- d5acd6b: Observe web UI overhaul: session header (connection, app, route, duration, call/error stats), filterable + searchable timeline with follow/pause autoscroll, device-screenshot hero pane with route chip, guided empty states, inline param inputs for learned actions (server now honors UI-provided params), expandable action output, and E2E run-history drill-down with per-flow error excerpts. The SPA is split from one 670-line file into focused modules.
Patch Changes
- d5acd6b: SessionStart hook links
.rn-agentfrom the main checkout when running in a git worktree, so learned actions, e2e config, and troubleshooting notes stay available (previously they silently disappeared in worktrees).
0.59.0
Minor Changes
- d12f18f: feat(rn-fast-runner): quiescence bypass — make XCTest's private quiescence wait a no-op inside the iOS runner (#384, Story 03). RN apps with Reanimated worklets/looping animations never report idle, so XCTest queries and snapshots stalled until per-symptom patches (runner-timeout shim, HID-synthesis scroll, 35s budgets) caught them; the bypass removes the idle-wait at the root — the same WebDriverAgent-lineage approach Maestro uses. Probes both private selector variants (
waitForQuiescenceIncludingAnimationsIdle:and the Xcode-16:isPreEvent:form), swizzles exactly one (classic preferred), and degrades loudly (RN_FAST_RUNNER_QUIESCENCE_UNAVAILABLE) when Apple drifts the API — the runner keeps working without the bypass. Default ON; opt out withRN_QUIESCENCE_BYPASS=0(resolved at runner spawn; threaded asTEST_RUNNER_RN_QUIESCENCE_BYPASSbecause xcodebuild only forwardsTEST_RUNNER_-prefixed vars). Note:XCUIElement.typeTextruns its own internal sync, so the type-timeout shim remains as a safety net. Auditable viameta.quiescenceBypasson the first command after boot,QUIESCENCE_BYPASSin/health.capabilities, andcdp_status.deviceSession.runnerCapabilities. - 0cfa78a: The observe web UI now autostarts when the MCP worker boots in an RN project, listening on a
stable default port (7333,
http://127.0.0.1:7333) with an ephemeral fallback on collision. New.rn-agent/config.jsonblock{ "observe": { "autoStart": boolean, "port": number } }plusRN_AGENT_OBSERVE_AUTOSTARTenv override (precedence env > config > default, matchingcdp.autoConnect). Theobservetool gains arestartaction;stopis session-scoped. The live URL is recorded in a per-project state file and announced at SessionStart.
0.58.1
Patch Changes
- 3cf6787: fix(device_batch): testID steps failed with a misleading STALE_REF on the in-tree runners (#396).
findRefByTestIDpassed the envelope's ref through verbatim; the in-tree iOS/Android runners emit@-prefixed refs (@e68), so the testID branches ofdevice_batch(find+tap / press / fill) composed@@e68, which missed the ref-map (lookupRefstrips exactly one@) and surfaced asElement at ref @@e68 no longer hittable — UI re-rendered since snapshoteven though the snapshot was taken fresh that same step.findRefByTestIDnow returns the canonical bare id in both the flat-nodes and nested-tree envelope shapes, restoring the documented "re-resolve at execution time" contract; the GH #114 producer-consumer contract tests are updated to pin the bare-id contract for the in-tree producers.
0.58.0
Minor Changes
- 694a57d: feat(protocol): version the native runner /command wire protocol + move runner state out of /tmp (#383). Both runners'
GET /healthnow reports{protocolVersion, runnerVersion, capabilities}and every response carries a"v"stamp; the bridge classifies a reachable runner with a missing/older/newer protocol or a skewedrunnerVersionas stale and transparently reaps + reinstalls it (the first device tool call after upgrading from a pre-protocol plugin pays one runner restart —meta.note: "runner upgraded (protocol/version mismatch)"). Only a mismatch that survives reinstall surfaces the new typed errorRUNNER_PROTOCOL_MISMATCHwith exact rebuild commands. Runner state files move from fixed shared/tmppaths to per-device hardened files (0600, symlink-refusing, atomic) under the app-support state dir (runner-state/ios-<udid>.json,android-<serial>.json; Android persists only under a resolved serial) via a sharedutil/secure-state-file.tsalso adopted by the session file; a live pre-upgrade runner pointed at by the legacy/tmpstate is adopted once, reaped, and relaunched before the/tmpfiles are deleted, and a grep-enforced test keeps/tmpout of the runner clients.cdp_status→deviceSession.runnerProtocolsurfaces the handshake.
0.57.4
Patch Changes
- b1e0ad6: feat(keyboard-guard): in-runner keyboard-occlusion guard for live
device_press/device_longpresstaps on iOS + Android (#370). Before a guarded tap, the runner probes for a visible software keyboard whose frame contains the tap point (containment on a sane rect — non-empty, min height 120pt iOS / 150px Android, so accessory bars don't false-trigger) and auto-dismisses first when occluded. Android dismissal ispressBack+ a boundedwaitForIdle(1500)(≈3.6s measured incl. bounded idle), gated on a TYPE_INPUT_METHOD window with sane bounds so it never navigates back otherwise — requiresFLAG_RETRIEVE_INTERACTIVE_WINDOWS, now enabled at dispatcher init. iOS is verify-or-refuse: only the safe dismiss-control tap ("Hide keyboard"/"Dismiss keyboard"/"Done") is used, then re-verified; on iPhone standard QWERTY, which has no such control, the runner REFUSES the tap withKEYBOARD_OCCLUDED … keyboardGuard=dismiss_failedinstead of tapping the keyboard, because XCTest'sswipeDownon the keyboard triggers QuickPath slide-typing and corrupts the focused field (device-proven). Every guarded gesture returnsmeta.keyboardGuard:"off" | "no_keyboard" | "not_occluded" | "dismissed"(plusdismiss_failedinside the iOS refusal error). Opt out withRN_KEYBOARD_GUARD=0/false, resolved TS-side per command (guardKeyboardon the wire; absent → guard stays ON, so older clients keep guarding). Scope is command-handler tap/longPress only —tapSeries, by-text taps, element-center taps, the focus-tap inside type/fill, swipes/scrolls/drags, anddoubleTapare explicitly unguarded. Follow-up #379 tracks a JS-first (Keyboard.dismiss()) auto-heal for the iOS refusal case; #378 tracks a pre-existing Androidforeground()pre-flight stall surfaced (not fixed) during verification.
0.57.3
Patch Changes
- a6112e6: fix(record):
device_record stopno longer crashes on macOS withadb_args[@]: unbound variable(#374). Inrecord_proof.shthe Android stop branch expanded an emptyadb_argsarray unguarded ("${adb_args[@]}"); underset -euo pipefailon bash 3.2 (the macOS default/bin/bash) that is an unbound-variable error, aborting the stop before the pull/convert — so recording finalize (and, via a leftover Android.pid, even iOS stops) failed. All three expansions now use the+-default guard already present elsewhere in the file. Regression-guarded by a static invariant test (effective on bash 5.x CI) plus a behavioral reproduction gated to bash < 4.4.
0.57.2
Patch Changes
- 0a9a732: fix(interact): cdp_interact no longer corrupts react-hook-form Controller-wrapped inputs (#336).
setFieldValuekeeps a string a string for string-typed fields (a digit-string injected as a number is coerced back to string only when the field currently holds a string — number/boolean fields are untouched).pressgains an optionalvalue: when provided,onPressreceives the value instead of a synthetic event, so radio/chip-style controls whose onPress sets a form value select correctly. HELPERS_VERSION bumped to 33.
0.57.1
Patch Changes
- d61985f: fix(actions): inject
- hideKeyboardbefore button taps that follow text entry when generating/saving Maestro action flows, and route Android hideKeyboard replays to the official Maestro CLI (#356, Phase 1). Bottom-pinned taps (submit/continue) previously landed on the soft keyboard during replays — the single biggest source of flaky replays.generateMaestronow tracks soft-keyboard state and emits ahideKeyboardstep before atap/long_pressthat follows aninputText, reset on navigation.hideKeyboardis a no-op when no keyboard is showing and Maestro re-resolves the selector after dismiss, so the injection is safe. Device verification surfaced that maestro-runner v1.0.9 silently no-opshideKeyboardon Android (B223), somaestro_runnow prefers the official Maestro CLI for Android flows containinghideKeyboard(verified to dismiss the keyboard on-device), warning when the CLI is unavailable; iOS is unaffected (maestro-runner honors hideKeyboard there). Livedevice_*taps (the in-runner guard) and existing-corpus backfill are deferred to later phases.
0.57.0
Minor Changes
- 98d3fb7: Add an RNTL-style discovery resolver to the injected helpers.
resolveLadderfinds elements bybyRole(+name)/byText/byPlaceholder— ported from React Native Testing Library (matcher + normalizer, accessible-name, role, hidden, host-kind) — with fail-closed truncation and fail-closed multiplicity (never silently picks the wrong element), hidden-element exclusion by default, and a selector bundle (testID/text/accessibleName/role/placeholder/anchors).interact()routesrole/name/text/placeholderselectors through the ladder. Includes RNTLmatchDeepestOnlyso a composite+host fiber pair (e.g.Text+RCTText) resolves to a single on-device element instead of fail-closing as ambiguous.
0.56.0
Minor Changes
-
dd95747: Bump the plugin manifest so installed users receive the recently-merged cdp-bridge work via
/plugin update. Until now the changesets flow only versioned the internalrn-dev-agent-cdppackage, leavingplugin.json/marketplace.jsonpinned at 0.55.5 — so the plugin's cache key never moved and updates never reached installs even though the bundleddist/had advanced.This release ships, to installed users:
- observe Regression "Run" reaches the device (#351): the per-action Run resolves the connected app's project root via bundleId instead of falling back to
process.cwd(), so clicking Run no longer fails withNO_PROJECT_ROOT. - iOS 26.x action replay (#353, Phase 2): when WebDriverAgent reads an empty accessibility tree,
cdp_run_actionfalls back to a CDP/JS transport so replays still drive the app. - Durable action store (#359, Phase 1): run/repair history persists in a derived, gitignored node:sqlite store (dual-write mirror of the JSON sidecars; graceful degradation when node:sqlite is unavailable);
cdp_statusreports the activeactionStorebackend. - CI now runs nested unit test dirs (#340).
- observe Regression "Run" reaches the device (#351): the per-action Run resolves the connected app's project root via bundleId instead of falling back to
0.55.5
Patch Changes
- 577b13b: cdp_repair_action now reports TRANSPORT_BLIND when the failed Maestro selector is present in the live rn-fast-runner snapshot — the iOS 26.2 + bridgeless empty-a11y-tree case (GH #317) — instead of the misleading "no confident replacement". cdp_run_action surfaces it as a terminal refusal with refusedReason TRANSPORT_BLIND. Diagnostic-only; restoring replay on that runtime is Phase 2.
0.55.4
Patch Changes
-
9a0f632: Live-sim speedup (GH #321, quick win #4):
device_batchreturns a salient final payload by default and gains afinalSnapshotoption (salient|full|none).device_batchalready collapses N interactions into one MCP round-trip, but itsfinal_snapshotwas always the full a11y node list (large) and it always took an implicit trailing snapshot. Now:salient(default) —final_snapshotis compacted to only actionable nodes (Button/TextField/Switch/Slider/Cell/Link/…), each{ ref, type, label, identifier, hittable? }, with afullNodeCount. Far fewer tokens;@refs for actionable elements are preserved so follow-updevice_press(ref)still works.none— skips the implicit trailing snapshot entirely (~1,450 ms saved) for action-only batches verified viaexpect_*/cdp_store_state.full— the legacy complete node list.
An explicit
snapshotstep orscreenshotOn:'end'still populates the payload; the option only governs the implicit trailing snapshot and its shape.rn-testernow recommends a singledevice_batchfor known multi-step sequences.
0.55.3
Patch Changes
-
e4d9e3b: Live-sim speedup (GH #321, quick win #3):
cdp_component_tree(interactiveOnly: true)returns a compact salient digest of a screen — only actionable nodes (Pressable/Button/TextInput/Switch/Link andaccessibilityRolecontrols) with a minimal{ testID, role, text, label, placeholder, disabled }shape, dropping props, hook state, and nesting.This is the perception payload (token) lever, complementary to the cached-find round-trip lever: answering "what can I tap here?" on a novel screen now costs hundreds of tokens instead of the full fiber tree's thousands. Implemented as an
interactiveOnlymode in the injected__RN_AGENT.getTree()(HELPERS_VERSION 26) — a bounded BFS over every renderer root that collects interactive fibers and their text.rn-testeris updated to prefer it for perceiving novel screens.
0.55.2
Patch Changes
-
3186f64: Live-sim speedup (GH #321):
device_findnow reuses the snapshot it already captured instead of issuing a redundant runner round-trip — but only while that snapshot is still a faithful picture of the screen.A snapshot cache already existed (
cacheSnapshot) but nothing read it for targeting, so everydevice_findre-snapshotted. On the live iOS test-app a warmdevice_findmeasured ~1,449 ms — essentially one full XCUITest accessibility snapshot (~1,435 ms) plus matching. Reusing a valid cache drops a repeated find on an unchanged screen to ~0.004 ms (in-memory filter), saving ~1.45 s per avoided find.Correctness is gated on a two-condition validity check, not just a TTL: the cache must be clean AND within the freshness budget. Invalidation is fail-safe and centralized at the MCP tool boundary (
trackedTool): every tool call that is not on an explicit read allowlist marks the cache dirty — so JS-level mutations that bypass the native dispatch path (cdp_interact,cdp_navigate, thefastSwipeswipe/scroll path,device_deeplink,cdp_dispatch/cdp_reload/maestro_run, …) all invalidate it, and any future tool defaults to "invalidate" until proven a pure read. The nativerunNativechoke point also marks dirty as defense-in-depth for direct (intra-composite) handler calls. A tap or navigation therefore forces a fresh snapshot — the cache is never reused against a screen it no longer describes. Only thedevice_findhandler opts in (allowCache); all other snapshot callers are unchanged.
0.55.1
Patch Changes
-
65fc134: Fix #312: harden the Maestro step-line parser (
maestro-step-parser.ts), which structuresmaestro_runresults from the runner's untrusted combined stdout+stderr.- B211 — cap the
verbfield toMAX_FIELD; previously onlynamewas bounded, so a step-shaped line with a multi-KB first token could bloat the MCP response across up to 1000 steps. - B212 — anchor
parseStepson the runner's leading indentation (horizontal-whitespace-only^[ \t]+, matched against the un-trimmed line) so an unindented (column-0) app-log line shaped like✓/✗ … (N.Ns)can no longer be mistaken for a step and poisonlastStep/failedStep/the failure headline.\r/\v/\f/NBSP-prefixed lines are rejected too (JS\swould have re-admitted them).parseTapLatencies(#263) inherits the same hardening. - A new
combineRunnerOutput(stdout, stderr)helper joins the streams for parsing without the blanket.trim()that would strip the first step line's indent (droppinglaunchAppfrommeta.steps); it uses native.trimEnd()to stay linear on multi-MB output. - Stripped stale review-provenance comments per the repo's no-unnecessary-comments convention.
- B211 — cap the
0.55.0
Minor Changes
- 9c3b1d2: Harden device-control conflicts: add an Android serial-scoped device lock (parity with iOS) that engages on a normal emulator, separate the Android runner's probed host port from its fixed device-listener port (
adb forward), and let the iOS runner self-assign a free port when 22088 is taken. - 1954ef1: Android
rn-android-runnernow self-installs on first use (parity with the iOSrn-fast-runnercold build):startAndroidRunnerinstalls the prebuilt APKs — and cold-builds them via Gradle if absent — when the instrumentation isn't on the device yet. No external CLI or manualgradlew + adb installstep is required; this makes the/setupand/doctor"builds/installs on first use" promise true on Android. - fec0464: Remove the agent-device dependency entirely. The Android daemon-socket + CLI fallback tiers are deleted; session open/close/list and find now route natively (simctl/adb + the in-tree rn-fast-runner / rn-android-runner), the Android dispatch gained an ensure-on-dispatch choke point (parity with iOS), session open validates the appId and acquires the device lock before any side-effect, RN_ANDROID_RUNNER=0 now errors (RUNNER_DISABLED) instead of silently falling back, and the agent-device install script + its SessionStart hook are gone. The in-tree runners are the sole device backend; the foreign-AgentDeviceRunner cleanup (self-heal for old installs) is retained.
Patch Changes
- d591710: Fix #303: Metro-port discovery now prefers the port with an attached Hermes target over a merely-running one, and when several Metros have an app it auto-selects the one whose serving directory matches this worktree's project root (resolved via
findProjectRoot+ realpath, containment-aware).cdp_statussurfaces all candidate Metros (metro.candidates) plusprojectRoot/servingCwd, and warns when the connected Metro serves a different worktree — catching the silent trap where an agent verifies against the wrong worktree's JS bundle even with a single Metro running.cdp_targets(discoverForList) prefers the attached port too. Fail-open throughout (macOSlsof; degrades to prior behavior off-darwin or when paths can't be resolved).
0.54.7
Patch Changes
-
8305bbd:
maestro_runnow returns structured per-step results and partial progress on timeout (GH #211).The result gains
steps[]({index,name,verb,status,durationMs}),failedStep,reason(sanitized{kind,selector}— never the raw runner log),lastStep(progress marker),timedOut, andoutputTruncated. On timeout the partial steps are returned instead of a bare failure, and the failure headline names the failing/last step. Parsed from maestro-runner stdout (the JVM Maestro CLI fallback degrades fail-open to empty steps);tapOnlatencies for #263 now derive from the shared parser. Additive —outputis preserved forrun-actionconsumers.
0.54.6
Patch Changes
-
16f0a0d:
maestro_runnow flags a wedged simulator runtime (GH #263).When a flow fails AND the median latency of its successful
tapOnsteps exceeds a floor (default 1500ms,RN_RUNTIME_DEGRADED_FLOOR_MS), the result gains aRUNTIME_DEGRADEDhint andmeta.runtimeDegraded— "the simulator test runtime is likely wedged; reboot it (xcrun simctl shutdown/boot), relaunch, and retry." This replaces the misleading "Element not found" that previously sent the agent chasing app code when the real cause was a degraded simulator (taps reported success butonPressnever fired). Detection is purely additive — it never changes a pass/fail verdict, never fires on a passing run, and only counts successful taps (a failed tap's duration is the step timeout, which would otherwise false-positive an ordinary element-not-found failure). Fail-open: unparseable output → no hint.
0.54.5
Patch Changes
-
6c77108:
/observedevice panels now refresh live (GH #206).The observability layer was a passive recorder of tool observations — the screenshot only updated on
device_screenshotcalls and the route only on navigation-family tools, so driving the app withcdp_interact/cdp_navigateleft both panels stale. A fire-and-forget hook now captures a fresh screenshot (simctl/adb, OS-level) + route (CDP nav-state) after each state-mutating tool and delivers them via a dedicated live SSE channel ({type:'live'}+/api/live-screenshot), so the timeline stays clean. Platform resolves from the active device session or the connected CDP target (so a purely CDP-driven flow with no agent-device session still refreshes). Gated on a connected/observetab, skipped during Maestro flows, single-flight trailing-coalesce, opt-out withRN_OBSERVE_LIVE=0.
0.54.4
Patch Changes
-
64531c8: Bump esbuild to 0.28.1 across the build toolchains to clear the HIGH Dependabot advisory (GHSA-gv7w-rqvm-qjhr).
The advisory is in esbuild's Deno installer (binary-integrity RCE via
NPM_CONFIG_REGISTRY) — a code path this repo never executes (esbuild is consumed as an npm transitive dep via Vite/Astro, not Deno), so it was never exploitable here. Still, both the observability web UI (scripts/cdp-bridge/src/observability/web/) and the docs site carried the vulnerable transitive esbuild, so both now pin it to the patched 0.28.1 via an npmoverrides. The observability Vite build also setsbuild.target: 'esnext'(it's an internal localhost-only dev tool viewed in a modern browser) to sidestep an esbuild 0.28 regression that refused to downlevel destructuring to Vite's default old-browser baseline; the single-file bundle was rebuilt.npm auditis clean in both subtrees.
0.54.3
Patch Changes
-
a88d139:
cdp_network_logno longer returns two entries per request (GH #214).Root cause: setup sends
Network.enable(modecdp), thenprobeNetworkDomainfires a probe fetch and watches the buffer. On RN ≥ 0.83 the CDP Network domain does deliver events, but when they don't flush within the probe window — a false negative documented after platform switches / reloads (GH #59 #9) — the probe returnsnoneand setup injects the fetch/XHR hook without disabling the still-enabled Network domain. Both paths then capture every request (CDP numeric-id entries + hook UUID-id entries), and the existing exact-id dedup can't collapse them because the two id schemes never collide.Fix: when setup falls back to the hook, it now disables the CDP Network domain first, so the hook is the single capture source. This also makes
cdp_status'snetworkDomain: falsetruthful instead of a label over a still-running domain — the "capability flag out of sync" symptom in the report was the same root cause. Read-time fuzzy dedup was deliberately rejected: it would collapse legitimately-identical rapid requests (a real double-mutation) and hide bugs — the opposite of what the reporter needed.
0.54.2
Patch Changes
-
0386204:
cdp_mmkvdelete and boolean reads now work on the Nitro react-native-mmkv line (GH #209).deletewas callingmmkv.delete(key)— a JS-wrapper-class method that doesn't exist on the raw Nitro hybrid object the tool actually talks to (createHybridObject('MMKVFactory').createMMKV(...)), whose spec exposesremove(key). The generated expression now prefersremove(), falls back todelete()for wrapper-shaped objects, and reports a named error (instead of a bare TypeError) when neither exists. This unblocks first-class auth/storage resets for logged-out replays on iOS — previously a rawcdp_evaluateescape hatch every time.getwithtype: 'boolean'emittedmmkv.getBool(key), which exists on no MMKV surface (hybrid object and wrapper both spell itgetBoolean) — broken since the tool shipped. Now fixed.- The follow-up enhancement from the issue (a
clearKeys:action-YAML directive for self-contained auth-gated replays) is tracked as GH #286.
-
0466d15:
/send-feedbackno longer presents weeks-old telemetry as "recent" (GH #266).Root cause: the per-tool-call telemetry writer was removed with the Experience Engine (GH #200, v0.49 era), but
collect-feedback.shkept reading the orphaned~/.claude/rn-agent/telemetry/*.jsonlfiles and shipped their tail as "Recent Tool Activity" in filed issues. The collector now cross-checks the newest event's age: fresh events (<24h, legacy plugin versions still writing) ship as before withtelemetry_status: "ok"; otherwise events are omitted andtelemetry_statusreportsstale (last event N days ago — …)ornoneexplicitly. The/send-feedbackissue template renders the status line instead of an empty/misleading activity table, and the empty-telemetry edge no longer emits a single bogus{}event.
0.54.1
Patch Changes
-
bd5d585: Recovery paths now detect "app not installed" and resolve their relaunch target truthfully (GH #262, absorbs #194 BUG 2).
cdp_statusAPP_DETACHED auto-relaunch: whensimctl launchfails ANDget_app_container's stderr carries theNSPOSIXErrorDomain code=2marker (allowlist-only, stderr-only — argv-spoof-proof), the tool returns a distinctAPP_NOT_INSTALLEDcode with install advice — including a shell-quotedsimctl installline for the newest matching.appsnapshot from the last clearState (GH #201 dir, mtime-sorted budgeted scan). Ambiguous probe verdicts fail open to the existingAPP_DETACHEDbehavior. Concurrent recoveries are serialized, and a confirmed missing bundle is cached (with a cheap re-probe) so the diagnosis is never masked bybudget-exhausted.cdp_restart hardReset=true: the relaunch target resolves throughexplicit arg > connectedTarget > cache > active-session appId > strict per-platform app.json(no iOS←Android fallback), simctl targets the active session's UDID when one exists, failed launches are classified the same way inhardResetSteps, and a successful hard reset resets the detached-recovery budget.
-
81c386a:
device_screenshotno longer blames "device transitioning state" when the target directory doesn't exist (GH #265).captureAndResizeScreenshotnowmkdir -p's the parent of the derived output path before any dispatch tier runs (simctl raw, rn-fast-runner, agent-device daemon/CLI, adb stream) — new directories are the expected case, since the tool's own advisories steer agents toward freshdocs/proof/<slug>/paths. The fix coversdevice_screenshot,device_batchauto-captures, andproof_step, all of which funnel through the same helper.- When the directory itself cannot be created (e.g. a file blocks an intermediate path segment), the tool short-circuits before probing any device and returns an honest
SCREENSHOT_FAILEDwithreason: 'target-dir-unavailable'naming the offending path — never the device-state guess. - A leading
~/in the screenshot path is now expanded to the real home directory (Node never expands~, so mkdir would otherwise create a literal./~/under the bridge cwd and report success into the wrong location). Unexpandable forms (~user/..., bare~) are refused with an actionable error.
0.54.0
Minor Changes
-
85a6b60: Agent model upgrades + skill efficiency pass.
Agents: all agents now run on
opus(rn-tester, rn-code-explorer, rn-code-reviewer up from sonnet; rn-debugger unchanged);rn-code-architectmoves tofable— the top model tier for the pipeline's single deep-reasoning blueprint step. Model-tier prose synced in the router skill and docs-site.Skills (token efficiency + correctness, verified by a confined-subagent retrieval test):
rn-feature-development5,076 → ~3,960 words (−22%): Phase 8 no longer duplicates the proof protocol —commands/proof-capture.mdis the single source of truth, with pipeline deltas (architect's flow table as source, persist-as-action via creating-actions Steps 3–6,cdp_run_actionsmoke-test, Deviations section) listed on top; 8 repeated per-phase evaluator lines collapsed into one core principle; description rewritten trigger-only (a workflow-summarizing description makes the body get skipped).using-rn-dev-agent(always loaded at session start) 2,065 → ~1,825 words: HELPERS_NOT_INJECTED recovery protocol moved torn-debugging(its natural home) with a routing pointer left behind; stale surface counts fixed (76 MCP tools / 14 commands).rn-testing: M7 header section slimmed to a 5-key table + creating-actions pointer (the full glossary lives there) — same heading kept for existing citations.rn-best-practices/rn-setup: descriptions rewritten trigger-only (dropped the rot-prone rule-count inventory; added concrete failure-phrase triggers).- Stale claims fixed everywhere:
maestro_run/cdp_run_actionDO forwardparamssince #272 (proof-capture + feature-dev said otherwise); broken section citation inrun-action.md; dangling "Step 1.4" cross-references from the old inline Phase 8; smoke-test now consistentlycdp_run_action(RunRecord + auto-promotion) with plainmaestro_runreserved for the on-camera replay.
0.53.0
Minor Changes
-
eff45cd: #202 Phase 6 / #186 — foreign Maestro sessions become arbiter refusals; plugin maestro_run is the canonical surface.
While a foreign Maestro/XCUITest session drives the target simulator (UDID-scoped detection, 5 s TTL, fail-open), local
device_*and flow tools refuse fast withBUSY_FOREIGN_FLOW(~50 ms measured) — pointing at the safe L1 reads — instead of colliding into the ~44 s runner-leak cascade. L1 introspection stays free;device_screenshotserves pixels via its simctl fallback; a ~10 s teardown grace after the plugin's own flows prevents self-false-positives while WDA dies. The two historical reasons to leave the plugin surface are live-gate-verified closed and #201 is closed — including a new fix: the clearState--app-fileresolution is snapshotted outside the device container (the installed-container path used to be deleted by clearState itself before the reinstall could read it).RN_IOS_FOREIGN_GUARD=0disables both the warning and the refusal (RN_IOS_FOREIGN_WARN=0remains a deprecated alias). The foreign-runnerpsscan now uses-ww(command-column truncation could silently drop the UDID → false negatives).
0.52.0
Minor Changes
-
c05c058: #202 Phase 5 / #264 — the bridge now survives Metro restarts (supervisor split).
The MCP entry point is now
dist/supervisor.js: a thin stdio shim holding zero network sockets (immune tolsof -ti tcp:8081 | xargs kill -9, which used to SIGKILL the whole server and cost the session all 77 tools). It spawns the real bridge as a worker, and on worker death: errors in-flight calls with-32000("retry the call"), respawns it (max 3 per rolling 60 s, then a terminal crash-loop error), and replays the cached MCPinitializehandshake so the session continues seamlessly. Visibility:cdp_status→bridge: { supervised, workerRestarts, lastWorkerExit }. Opt out withRN_BRIDGE_SUPERVISOR=0(legacy single process).SIGUSR2now performs a real hot-reload (worker restart + handshake replay).
0.51.0
Minor Changes
-
abe4411: New
creating-actionsskill — guided authoring of reusable Maestro actions.Walks the agent through the full authoring contract: inventory-dedup scan before authoring (via
learned-actions.mjs), creation-path choice (recorder vs direct YAML vsmaestro_generate), selector grounding (never invent a testID), a required ASCII flow diagram of screens/transitions annotated with exact testIDs and${PARAMS}(embedded in the YAML header — glyph-first lines so the M7 parser can't misread a diagram line as metadata, which would otherwise silently overwrite fields likestatus), the M7 header contract, pre-replay validation (header round-trip through the inventory parser, placeholder↔params coverage, selector audit), and replay-to-promote viacdp_run_action(never hand-setactive). Ships with a full M7 field reference (references/m7-header-reference.md) and a toolchain-validated worked example (examples/add-product-to-cart.yaml— verified againstparseM7Header,learned-actions.mjs, and Maestro's syntax checker). Routed fromusing-rn-dev-agent(decision tree + skill map) and cross-linked fromrn-testing's M7 section.
0.50.0
Minor Changes
-
73c6bf4: #202 Phase 4 — eradicate legacy runner apps, not just processes.
At iOS device-open,
ensureSingleRunnernow detects the legacy upstream runner apps installed on the target simulator (com.callstack.agentdevice.runner+.uitests.xctrunner) andsimctl uninstalls them. Killing the host processes (Phase 1) was insufficient: iOS relaunches an installed XCUITest runner into the foreground mid-maestro_run, backgrounding the app under test and wedging CDP. Scanned at every device-open (onesimctl listapps, ~150–350 ms measured — no memo, so a reinstall by another session is always caught); error-safe (warnings, never a blocked session); opt out withRN_DEVICE_KILL_LEGACY=0. Results surface asremovedApps+meta.timings_ms.appEradication.
0.49.0
Minor Changes
-
58c4886: Debugger-seat coexistence with React Native DevTools + silent hook-mode network capture.
- New opt-out for background auto-reconnect:
RN_CDP_AUTOCONNECT=0or.rn-agent/config.json{ "cdp": { "autoConnect": false } }. In passive mode the bridge yields the single RN debugger seat to the visual DevTools and reconnects only on explicit tool calls. Resolved mode is visible incdp_status→autoConnectand/doctor. - Hook-mode network capture (RN < 0.83 fallback) no longer transports entries via
console.log("__RN_NET__:…")— entries go to an in-app ring buffer drained on demand, so Metro logs and the user's DevTools console stay clean.
- New opt-out for background auto-reconnect:
0.48.5
Patch Changes
- 6190178: fix(#253):
cdp_repair_actionno longer hardcodestargetPlatform='ios'— Android auto-repair works against an emulator. The repair orchestrator now derives the platform from the active device session viadetectPlatform()(booted-device probe fallback when no session is open;'ios'only as the final no-session, no-device fallback). Previously an Android repair foregrounded the app viaxcrun simctl, snapshotted through the iOS short-circuit, and bootstrapped the iOS fast-runner — so Android selector drift always escalated as a hard failure instead of self-healing.
0.48.4
Patch Changes
- e5404ed: fix(#249): Maestro pass detection no longer flips passing flows to failed when app logs contain the substring
FAILED. The exit-0 secondary guard inmaestro_run,maestro_test_all, and the inline maestro fallback used a bareoutput.includes('FAILED')over combined stdout+stderr — app/console output like aFETCH_FAILEDRedux action or aLOGIN_FAILEDanalytics event marked a genuinely passing flow as failed and triggered pointless auto-repair. All three call sites now shareoutputIndicatesFlowFailure, which keys on Maestro's own terminal status lines (Test FAILED/Flow FAILED/ a[FAILED]step marker / a bareFAILEDline) instead of a substring. - 070586d: fix(#250):
cdp_interactno longer reports success when the app's own handler throws. The injected interact dispatch caught handler exceptions (onPress/onChangeText/setValueraising — unmounted component, missing context, thrown validation) and returnedsuccess: true, action_executed: true, which the tool layer surfaced as a non-error warning — so agents proceeded against a screen that may be in an error state. The helper now reportssuccess: false(keepingaction_executed: trueto distinguish "dispatched but handler threw" from "couldn't dispatch"), and the tool layer maps it to a structured error withmeta.actionExecuted,meta.handlerError, and a check-cdp_error_loghint. HELPERS_VERSION bumped to 25 so connected sessions re-inject. - 8269476: fix(#251,#252): startup hardening. The project single-instance lock (
Lockfile.acquire) now uses the same atomicopenSync('wx')exclusive-create pattern asDeviceLock— the previous read-then-write let two bridges starting in the same instant both "acquire" the lock, with the second silently truncating the first; the loser now gets a structured conflict, stale-holder reclaim narrows the steal window with a re-read before unlink, and fs infra errors fail open (degraded: true) instead of crashing the bridge at boot. Separately, SessionStart is now bounded: the hook declares an explicit 120s timeout and the maestro-runner installer'scurl | bashcarries--connect-timeout 10 --max-time 90, so a stalled CDN can no longer block session start indefinitely; a CI guard (session-start-bounded.test.sh) pins both.
0.48.3
Patch Changes
- 609c825: fix(B191,B192): post-flow lifecycle hardening follow-ups to #243/#244.
isAndroidConnectionFailurenow also classifiesstartAndroidRunner's startup-failure shapes (exited before readiness,Failed to spawn Android runner instrumentation) into the structured retryableRN_ANDROID_RUNNER_DOWNinstead of letting a startup crash escape as a raw exception. AndisBenignSessionGoneErrorno longer runs its session-gone regex over unparseable (non-JSON) close payloads — with no error field to scope the match to, they surface unchanged, so a real close failure whose raw text merely mentions "no active session" can't be silently swallowed.
0.48.2
Patch Changes
- c9d447d: fix(#243,#244): Android post-flow lifecycle.
rn-android-runnerreadiness is now gated on its ownGET /healthinstead of theadb logcatring buffer — a prior runner's stale ready line (same tag + fixed port) used to fire readiness before the new socket bound, so the firstdevice_*after a Maestro flow returned a barefetch failed. When the runner genuinely can't come up,runAndroidnow surfaces a structuredRN_ANDROID_RUNNER_DOWNwith a retry hint. Separately,device_snapshot action=closenow tolerates an underlying session that a flow already tore down (the #237 slot-release): it cleans up local state and returns ok, soopen → flow → closeround-trips cleanly instead of erroringSESSION_NOT_FOUND.
0.48.1
Patch Changes
- 51976e8: fix(#237): Android instrumentation-slot handoff —
runFlowParkednow releases the single AndroidUiAutomationslot before a Maestro flow (maestro_run/maestro_test_all/cdp_auto_login), fixingUIAutomator2 server not ready after 30s. It stops the in-treern-android-runner,am force-stops our two instrumentation packages (the decisive device-side release), and — gated byRN_DEVICE_KILL_LEGACY— kills a stale legacyagent-devicedaemon by its specific PID (neverpkill, guarded against our own process tree so the MCP server is never collateral). Best-effort and idempotent; iOS behavior is unchanged.
0.48.0
Minor Changes
- de6a8d8: fix(#191): JS-first text entry —
device_fillnow prefers the deterministic ReactonChangeTextpath when CDP is connected and the ref resolves to a testID (via its cached snapshot identifier), settle-polls the field value to verify it (defeating the debounced-onChangeTextread race), and on the native fallback runs a bounded clear+retype (realclearFirst+ per-character delay) when the value is corrupted, escalating to a verified maestro fallback before erroring. Adds best-effort iOS predictive-keyboard suppression at session-open and a newTEXT_ENTRY_UNVERIFIEDerror code for the exhausted-and-still-corrupted case. Additivemetaonly (textEntryPath,verify,timings_ms); no breaking change for existing callers. NOTE:device_batchfills are not yet JS-first (they call the runner directly) — tracked as a follow-up.
0.47.5
Patch Changes
-
72d17b5: Fix #210: iOS device-session visibility + self-healing.
cdp_statusnow reportsdeviceSession: { sessionOpen, rnFastRunner: 'alive'|'stale'|'dead', appId?, deviceId?, foreignRunner? }so the agent can see the XCUITest runner state before callingdevice_*(iOS-gated — Android leavesrnFastRunner:'dead'and skips the probe/scan).device_find/press/fillnow auto-spawn the runner from the dispatch choke point when a session or booted simulator exists and the rig is prebuilt — cold-build-safe: a missing prebuilt rig returns an actionableRN_FAST_RUNNER_DOWNerror namingdevice_snapshot action=openinstead of a silent multi-minutexcodebuild.device_screenshotnow falls back toxcrun simctl io screenshot(oradb) whenever the runner can't serve it — including while a Maestro flow owns the device — so it never hard-fails on iOS. Also fixes a latent bug where an omitted-platformdevice_snapshot action=openstoredplatform: undefined, skipping the iOS dispatch branch.Reframes the issue's "ride Maestro's WDA" suggestion (rejected: WDA is per-flow/ephemeral with no session to ride, and a WDA client would add a second XCUITest backend rather than unify; mid-flow pixels use simctl, mid-flow state uses CDP introspection). (GH #210, B186, D1249)
0.47.4
Patch Changes
-
75a9573: Fix #182: the CDP MCP no longer fails with
-32000: Connection closedwhen an orphaned bridge from a dead Claude Code session holds the single-instance lock.Root cause: when CC dies abnormally (SIGKILL/crash/window-close on macOS) without closing the child's stdin or signaling it, the bridge becomes a live orphan — still running, still holding the project lock. The existing reclaim (PID-dead / mtime>24h / process-name) can't recover a live owner, so the next session hard-failed for up to 24h. Four composing fixes:
- Parent-death self-exit (prevent). A
getppid()poll (lifecycle/parent-watch.ts) captures the bridge's PPID at startup and self-exits (releasing the lock) when it changes — i.e. the original Claude Code host died and the bridge was reparented. This catches the abnormal-death cases stdin-EOF + signal handlers miss. It compares against the startup PPID rather than testing=== 1so a bridge whose host runs as PID 1 (a container with no init system) is never falsely killed. - Orphaned-owner reclaim (recover).
Lockfile.isLockLivereclaims a live owner whose parent changed from the PPID it recorded at acquire (ps -o ppid=) — so a new session self-heals past an existing orphan instead of hard-failing. A null PPID lookup fails safe; pre-0.39 locks with no recordedppidfall back to a legacyPPID===1reclaim. - Heartbeat (recover wedged). The lock body carries
lastHeartbeat, refreshed every ~10s; a live owner whose heartbeat goes stale (>90s) is wedged and reclaimable — mirroring the device-lock's self-healing. Pre-0.39 locks withoutlastHeartbeatfall back to the existing mtime check (back-compat). - Usurp self-terminate (sleep/wake safety).
Lockfile.touch()now returns whether we still own the lock. If a contender reclaimed our slot while the laptop slept (heartbeat expired → reclaimed → we wake), the next heartbeat detects the foreign PID and self-terminates instead of running as a second bridge on the same device. This also makes the (pre-existing, non-atomic) reclaim path self-correcting within one tick.
Together these eliminate the manual
kill <pid> && rm <lock>workaround. 15 #182 unit tests (incl. container-safety, sleep/wake usurp, and a realps -o ppid=check); unit suite 1744/1744;tscclean. (GH #182, B185, D1246) - Parent-death self-exit (prevent). A
0.47.3
Patch Changes
-
b29a8e4: Fix
cdp_console_logand harden the helper-expr injection guard. The guard that validates injected-helper calls beforeRuntime.evaluatebanned any call containing{}, which brokecdp_console_log— it passes a JSON object argument (getConsole({"level":"all","limit":50})) and was refused with "helper-expr: refusing to interpolate untrusted call". The guard now validates that the argument list is pure JSON data (object/array literals included) instead of banning{}characters. This fixescdp_console_log(and any object-arg helper call, e.g.dispatchAction) and tightens security: the old[^;{}]*regex let nested calls such asgetConsole(stealSecrets())through; those are now rejected. The one non-JSON token a call site emits —undefined(store-state's absent path/type) — is normalized tonullfor validation only; the original call is interpolated unchanged. Verified: 1710/1710 unit tests pass (incl. 10 new helper-expr tests) and livecdp_console_logreturns the console buffer (69 entries). (B180) -
bc577e9: Fix the CDP connection wedge (GH #208):
cdp_statusno longer dead-locks on "Already connecting to Metro..." and no longer misreports a detached app as "Metro not found". Three root causes were addressed:- RC1 — reconnect-storm wedge. When the app detaches but Metro stays up, the WS-close reconnect loop holds
isReconnecting()true for up to ~12 min (30 attempts × 30s cap, then re-armed indefinitely by the background poll).autoConnect's guard threw "Already connecting" for everycdp_status/cdp_*call in that window.cdp_statusnow preempts an active reconnect storm viasoftReconnect()(the existing 3ssoftReconnectRequestedhandshake) for one fresh attempt instead of refusing, and surfaces the livereconnectState(attempt N/30) on any connect failure so it reads as progress, not a dead end. - RC2 — misleading error. "Metro up but 0 Hermes targets" now throws a typed
AppDetachedError("Metro is up … advertises 0 Hermes debug targets — the app isn't attached") instead of being conflated with the genuine "Metro not found" (now reserved fordiscoverMetroPortreturning null). - RC3 — no auto-recovery. New
recoverDetached()cold-restarts a detached iOS app (simctl terminate+launch) → reconnects → confirms with a real CDP liveness probe. Bounded to 3 consecutive attempts/session, skips while a Maestro flow holds the arbiter lease, iOS-only, opt-out viaRN_AUTO_RELAUNCH_ON_DETACH=0. Cold-restart (vs recover-wedge's bare launch) is acceptable because it only fires when the app is ALREADY detached — never against a working app.
Hardened via a Codex + Gemini multi-review:
cdp_statusnow honors an explicitargs.platformduring a storm (tears down + reconnects rather than reusing the storm's target), auto-relaunch is skipped when the caller pinned a non-iOS platform (never cold-restarts an unrelated iOS session),simctl launchfailures are surfaced instead of hidden behind "still detached", and the post-recovery status read can no longer throw out of the handler.19 new unit tests; full suite 1729/1729;
tsc --noEmitclean. Live false-positive guard verified: the realdiscover()against Metro does not fireAppDetachedErrorwhile a target is present. Scoping: the literal-0-targets case is fixed; the RN-0.85 "C++ target present, 0 Hermes" flavor remains B156/B184 territory (recover-wedge path). (GH #208, B181, D1245) - RC1 — reconnect-storm wedge. When the app detaches but Metro stays up, the WS-close reconnect loop holds
0.47.2
Patch Changes
- dc49a98: Fix B178: CDP introspection returning zero frames on Expo SDK 56 / RN 0.85. The B177 Origin fix used
localhost, which clears@react-native/dev-middleware's loopback gate (no 401) but trips Expo SDK 56's second origin gate increateDebugMiddleware(isMatchingOrigin): it requires theOriginhost to equal the dev server'sserverBaseUrlhost (127.0.0.1), and a mismatch is force-closed viasocket.terminate()→ a 1006 abnormal close right after connect, before any CDP frame relays. SwitchingmetroOriginto emit127.0.0.1clears both gates (and bare RN's single gate), fully restoringcdp_status/cdp_component_tree/cdp_store_state/cdp_evaluateon RN 0.85. Verified end-to-end against a live RN 0.85 app:Runtime.evaluateplus Redux / Zustand / navigation reads now relay. (B178 / D1242) - de2353b: Fix CDP-bridge connection failure on React Native 0.85 / Expo SDK 56. RN 0.85's Metro inspector proxy (
@react-native/dev-middleware) now enforces a WebSocketOriginallowlist (loopback hostnames only) as a CSRF defense and returns HTTP 401 to the bridge's header-lesswsclients — breakingcdp_statusand all CDP introspection on the newest RN. A newmetroOrigin()helper (scripts/cdp-bridge/src/ws-origin.ts) synthesizes a loopbackOriginmatching the dev-server port; it is now sent on all three Metro WebSocket clients (cdp/connect.ts,cdp/multiplexer.ts,metro/events-client.ts). Verified end-to-end: the handshake now succeeds against an RN 0.85 / SDK 56 app (proven: no-Origin → 401, loopback Origin → OPEN). (B177 / D1240)
0.47.1
Patch Changes
- 6835fbf: #202 Phase 3: formalize the three-layer device-control contract (L1 introspection / L2 interaction / L3 flow) in the docs, and add a proactive, informational
FOREIGN_RUNNER_ACTIVEwarning. Whendevice_snapshot action=openfinds a foreign maestro automation session driving the simulator (UDID-scoped) and rn-dev-agent is not itself running a flow, the open result now carriesmeta.foreignRunner+ a heads-up that interleavingdevice_*may trigger a re-foreground (CDP reads are unaffected). Opt out withRN_IOS_FOREIGN_WARN=0. The reactive recovery for an actual leak shipped earlier in #188; this is the complementary proactive signal.
0.47.0
Minor Changes
- 6e8af52: #202 Phase 2a: a process-wide in-memory
DeviceSessionArbiternow serializes the three device-control planes —flow(Maestro) is exclusive;introspection(CDP reads) andinteraction(device_*) coexist. A read or tap issued while a Maestro flow is running refuses fast withBUSY_FLOW_ACTIVEinstead of interleaving with it. The flow tools (maestro_run,maestro_test_all,cdp_auto_login) park the in-tree fast-runner for the flow's duration and mark CDP stale afterward so the next read reconnects. Diagnostics (cdp_status), connection management, and session-less tools stay unarbitrated and always work; a wedged arbiter (a leaked plane lease) is cleared viacdp_status({ resetArbiter: true }). - 6e8af52: Phase 1 of device-control hardening (#202):
ensureSingleRunner()now kills staleAgentDeviceRunnerprocesses scoped to the target simulator and clears orphaned~/.agent-device/daemon.{json,lock}(default-on; opt out withRN_DEVICE_KILL_LEGACY=0). Fast-runner state is no longer reused across simulators.maestro_runauto-resolves--app-filefor iOSclearStateflows (#201). - 6e8af52: #202 Phase 2b:
cdp_statusnow auto-recovers the JS-thread-paused wedge. When the simulator's foreground is stolen and iOS suspends the app's JS thread (CDP wedged),cdp_statusparks the fast-runner, re-foregrounds the target app (simctl launch, which resumes its JS thread), reconnects, and confirms recovery with a real CDP liveness probe — bounded to 3 consecutive attempts per session (reset on a successful recovery and ondevice_snapshot action=open). It skips when a Maestro flow is running (it would yank the app out from under the flow) and falls back to suggestingcdp_restart(hardReset=true). This replaces the previous dead-end "Debugger is still paused" warning that left the agent to rediscover the fix over many attempts. iOS-only.
Patch Changes
-
6e8af52: Fix a batch of bugs, regressions, and reliability issues surfaced by a multi-agent repo audit.
Security
- Redaction no longer leaks private-key material.
redactStringnow applies secret patterns BEFORE truncating (a >2000-char PEM previously had its-----END-----marker severed by truncation so the key body passed through), and the PEM rule now matches multi-word labels likeRSA PRIVATE KEY/OPENSSH PRIVATE KEY(the old single-word pattern never matched the most common headers).
Device interaction
device_scrollno longer throws on Android (and on the iOS fast-runner fallback): a direction-form scroll is now converted to coordinates before dispatch, matchingdevice_swipe.device_batchscroll steps no longer crash the whole batch on either platform (same root cause).- A coordinate
device_swipewith--count/--patternbut nodurationMsno longer mis-parses the flag value as a 3 ms duration on iOS (the positional extractor now strips flag values, matching Android). - The Android runner is no longer reused across emulators:
shouldReuseAndroidRunnerchecks the bounddeviceId(parity with iOSshouldReuseRunner), so a runner bound to one emulator can't silently drive another. - A wedged-but-alive fast-runner is now reaped:
ensureFastRunnerprobes tri-state liveness instead of PID-only, so a hung HTTP listener no longer makes every subsequent command burn the full timeout. ensureSingleRunneris now awaited at session-open so the stale-runner kill completes before the first interaction, and itspsfailure surfaces as a warning instead of a silent no-op.
Actions / Maestro
- Actions now auto-promote
experimental → activeon the first clean replay (the documented lifecycle was defined + tested but never wired). - The GH#186 route-drift guard is now active in production (
cdp_run_actionis wired with a CDP-backed live-route reader; it previously defaulted to a no-op). maestro_test_alland the inline Maestro fallback no longer mark passing flows as failed when app/console output merely containsError:, and both now auto-resolve--app-filefor iOSclearStateflows (previously onlymaestro_rundid).clearStatedetection also recognises the standalone- clearStatecommand.- All Maestro
execFilecalls raisemaxBufferto 10 MB so a large flow log can't kill the child and mask a passing run. cdp_repair_actionRUNNER_LEAKrefusals are now bucketed asSNAPSHOT_FAILEDin MTTR telemetry instead ofINTERNAL_ERROR.- A bare-form
id:repair now emits a quoted scalar, so a testID containing YAML-special characters can't corrupt the action.
Reliability / correctness
collect_logsno longer double-shifts Android logcat timestamps by the host UTC offset (which corrupted both the time and the cross-source merge order).- CDP freshness/dev probes attach a no-op catch to the raced
evaluate()promise so a mid-probe WebSocket close can't surface as an unhandledRejection. - The observability server keeps a small
headersTimeout(slow-loris guard), broadcasts ashutdownevent so the browser stops auto-reconnecting after stop, andRecorder.clear()notifies subscribers instead of orphaning live SSE streams. - Action IDs now accept dots (
v2.0-login) per their documented contract while still rejecting... - The post-edit health-check hook's "app not installed → skip" guard works again (
grep -c || echo "0"produced a two-line0\n0). learned-actionsresolves the project memory dir correctly for paths containing a dot, and its${VAR}extractor accepts digit-bearing keys.- The injected-helpers version is a single source of truth (the post-injection log no longer reports a stale
v11). sync-versions.shdrops a dead, misleading variable and documents thatrn-dev-agent-cdpis independently versioned.
Hardened the previously flaky
proof_stepunit tests (they depended on a machine-global session file) with a dependency-injection seam, making the suite deterministic. - Redaction no longer leaks private-key material.
-
6e8af52: #202 Phase 1.5: iOS
device_snapshot action=opennow takes a persisted, UDID-scoped simulator-ownership lock — closing the multi-bridge race where two Claude Code windows (two bridge processes) could drive the same simulator. The second bridge gets aDEVICE_BUSYerror. The lock self-heals via PID-liveness + a 30s heartbeat (reclaimable once the holder PID is dead or its heartbeat is >90s stale), so it cannot orphan like the legacydaemon.lock; on an fs error it fails open (logged) rather than blocking a session.
0.46.0
Minor Changes
-
3beb8e5: Replace the Experience Engine with a repo-local troubleshooting memory.
/rn-agent-compact,/rn-agent-health,/rn-agent-export, and/rn-agent-importare removed (GH #200: compaction had no runnable entry point and the read path was vestigial). In their place, rn-dev-agent now maintains a gitignored.rn-agent/local/troubleshooting.mdper repo: failures are captured by a hook, the agent synthesizes them into the doc at session end, and the doc is injected at session start so the agent learns this repo's config and gotchas.
0.45.0
Minor Changes
- 5c4ca04: Add the read-only observability UI (D1226 "watch the agent live"): an in-process recorder + opt-in SSE server serving a React SPA (timeline | device | state). New
observeMCP tool +/rn-dev-agent:observeslash command. Deep-redacted (args + payload, fail-closed), localhost-only with Host-header + Sec-Fetch-Site guards.
Patch Changes
-
c4804dc: Add
cdp_dismiss_dev_client_pickerMCP tool (Android) and best-effort Dev Client picker dismissal after Android deep links (#136 sub-3). Routed through a single guardedclearDevClientPickerIfPresent()helper; iOS returns an actionable manual-select message instead of touching the legacy agent-device path. Cross-platform iOS support tracked as a follow-up. -
2c82b18: Fix iOS runner auto-install and stop force-installing agent-device on iOS-only setups.
- rn-fast-runner now self-builds on first use.
startFastRunner()falls back to a fullxcodebuild test(build + test) when no prebuilt.xctestrunexists, instead of always usingtest-without-building(which failed on a fresh machine wherebuild/DerivedDatais gitignored and never produced). The firstdevice_snapshot action=openon a clean clone now succeeds — it just cold-builds the rig once (ready-signal timeout widened to 360s for that path). Steady-state spawns still use the fasttest-without-building. - agent-device install is gated on a live Android target. The SessionStart hook (
detect-rn-project.sh) no longer runsnpm install -g agent-deviceunconditionally. Since D1219/PR #164 iOS device control is owned by the in-tree rn-fast-runner, so agent-device is Android-only; the install now only runs whenadb devicesshows a booted device/emulator. iOS-only macOS users stop paying for a dependency they never use. /setupand/doctornow offer to run the one-timexcodebuild build-for-testingpre-build to move the cold-build cost out of the first interaction (the lazy fallback covers correctness; pre-building just avoids the slow first call).
- rn-fast-runner now self-builds on first use.
0.44.45
Patch Changes
-
Deliver the GH #186 maestro-interop fixes that merged in #188 without a version bump (closes #189).
cdp_run_actionnow allowsrunFlow(includingwhen:conditionals and{file}sub-flows) through the Maestro command allowlist, so actions with conditional dialog-handling (Expo dev-server picker, iOS "Open in" dialog) replay through the canonical runner instead of hard-failing withCommand not in allowlist: runFlow (Phase 134.1).- Non-destructive runner-leak
reacquirerecovery tier + cross-tool CDP re-pin, avoiding the ~44s relaunch / ~47s STALE_TARGET when maestro-mcp and rn-dev-agent contend for the same iOS device. - Structural route-drift detection: a stale-selector failure on an inserted screen is classified
ROUTE_DRIFTinstead of triggering a wasted fuzzy-repair.
#188 shipped these to
mainwith no version bump, leaving them undeliverable to marketplace installs; this patch publishes them.
Core
1.0.12
Patch Changes
- fd4f88a:
device_fillgainsfocused: true, which types into the already focused iOS field through a synthesized text event and confirms the value through the React tree only when that named field is the focused responder, returningtyped: true, verified: falsewhen it cannot. - e0a5803: Doctor reports learned-action compatibility as its own read-only row — missing or low enginePin, regex text selectors, and unreadable actions — so a pinned-ok runner can no longer hide replay refusals behind ENGINE_PIN_MISMATCH.
- 466b3f5: The React-tree replay refusal
frontmost proof cannot cover every mounted renderernow carriesmeta.coveragewith its reasons and renderer ids. - 466b3f5: The React-tree replay refusal's
meta.coveragenow names the phase and message of each scan error, and the frontmost prover reads style keys directly so styles it cannot enumerate no longer abort the scan. - bc219d9: Ship one committed bundled runtime with host-neutral
rn-dev-agent-coremetadata frompackages/claude-plugin, which both the Claude and Codex marketplaces now install (Codex selects its generated.codex-plugin/plugin.json,codex.mcp.json,bin/launchers andcodex-*adapters from the same directory), so a Codex local registration must point atpackages/claude-plugininstead of the removedpackages/codex-pluginruntime (GH #892). - 466b3f5: Expand TextInput designation scans to 20,000 fibers.
- 466b3f5: Raise React-tree
typeTextand input read-back to a shared 500,000-work-unit limit (a measured diagnostic, not a performance recommendation) and make TextInput designation recognize React alternates.
1.0.11
Patch Changes
- 087d5b5: Pin an iOS simulator dev client with the same initial-URL launch the managed build uses instead of an openurl confirmation.
- 087d5b5: Record recognized authority refusals as one code-and-platform group across emission shapes, persist an explicit non-membership verdict, and retain runner traces for every failed replay and for recognized authority refusals without treating successful diagnostic calls as verified recovery. Stored symptoms and submitted feedback now redact UUID-shaped device identifiers. Dispose the previous CDP client before an Android managed relaunch or re-prove so Metro's inspector proxy sees one debugger per page.
1.0.10
Patch Changes
- ce181d2: Add first-class Cursor Plugin manifests and
${CURSOR_PLUGIN_ROOT}spawn on the Claude package, keep the process lock on Claude and Cursor spawns, make the lock-conflict message host-neutral without kill, lock-delete, or--no-lockadvice, and resolve reused Claude command/skill helper paths through a host-neutral plugin-root fallback that includesCURSOR_PLUGIN_ROOT(GH #1038, #872).
1.0.9
Patch Changes
- c3fc818: Managed dev-client launches and relaunches now keep the Expo dev-menu onboarding tutorial and launch-time sheet from auto-opening over a native segment or after a reload on a fresh install, on by default and configurable per target class with
autoHideDevMenuin.rn-agent/config.json(GH #1004). - 814337d: Document the one-source-owner-per-worktree and one-exact-device-target-per-session authority bounds in the workflow skill, with pointers from the CLAUDE.md template and other workflow skills (GH #1026). Source ownership is the worktree/checkout, not only the app package root: a sibling app root in the same checkout is also blocked, and separate linked worktrees are independent copies. A session holds one
(platform, deviceId, appId)target at a time and replaces it after runner, proof, any explicitly started Observe, and any active recorder are released and no incompatible install receipt is bound. Authoritative cross-targetcross_platform_verifyis currently unsupported. - 5c10049: Fix saved-action tab presses by resolving wrappers around a single native host and proving visibility from the owning route scope with navigator controls bound to their enclosing navigator and transparent destination providers, while preserving refusals for ambiguous controls and inactive content (HELPERS_VERSION bumped to 70 so connected sessions re-inject).
- 7ab6ae4: Normalize native recordings to 30 fps H.264 at their captured resolution through the authenticated stop boundary while preserving captured timing, stage the conversion inside the private runtime directory, refuse symlinked PR-body destinations, and embed proof screenshots and demo GIFs at 400px width in the generated PR body.
- 2085479:
rn_sessionstatus andcdp_statusnow report the running product on the envelope:product.coreVersionis the core package the live session process loaded, andproduct.pluginVersionis included when the host plugin manifest differs (GH #1025).
1.0.8
Patch Changes
- f1d414e: Print snapshot and find element refs as
@eN, matching the pinned form the current frame authorises for press, and accept a copied bareeNat the argv boundary (GH #979). - fa2cd21: Refuse managed-dev-client replay of flows that clear app state, and make login-path refusals name the actual next step.
1.0.7
Patch Changes
- b352043: Report the managed-Metro readiness failure from the pre-kill launcher state and log tail, and raise the budget to a 90 s default configurable via
.rn-agent/config.json→metro.readinessTimeoutMs(GH #992).
1.0.6
Patch Changes
- 8517ebc: Admit the canonical
expo-updatesruntime-version CLI and the verified developergitit shells out to in the Darwin managed-Metro sandbox profile, so Expo dev-client manifests under afingerprintruntime-version policy stop failing withspawn EPERM.
1.0.5
Patch Changes
- 6d6dc39: Allow the managed-Metro Darwin sandbox to write the resolved, run-owned
react-native-css-interop/.cachedirectory while preserving all other sandbox restrictions and enforcement checks. - 6d6dc39: Accept the renamed Apple platform code-signing leaf authority during managed-Metro sandbox verification while preserving the trusted Apple chain, identity, and rejection requirements.
- 5008fd6: Discover deeply nested Redux and React Query stores and dispatch Redux actions through their Provider when an app bridge has no registered store.
- 6d6dc39: Record private per-attempt managed-Metro enforcement diagnostics with preparation outcomes, fixed-schema flags, timings, exits and predefined error categories without retaining arbitrary stderr text or changing permissions, timeouts or fallback behavior.
- 6d6dc39: Deliver the attested command snapshot to the managed-Metro sandbox preflight shim before signaling admission, so a shell-shim command no longer intermittently sources an empty snapshot and degrades enforcement to reported-v1.
- 6d6dc39: Detect a wildcard-bound Metro listener in the managed-Metro sandbox preflight by probing every local address shape, so a real Expo server that listens on all interfaces no longer reads as unoccupied and falls back to reported-v1.
- cc992e6: Omit automatic hook state from component trees while preserving existing explicit component-state inspection.
- e0d25c8: Retain a bounded closed-vocabulary projection of runner failure evidence on
maestro_runresults andcdp_run_actionRunRecords before the temporary report tree is deleted, withholding text, images, terminal output and original artifacts. - 6d6dc39: Allow the managed-Metro Darwin sandbox to look up the FSEvents service so Metro's native file watcher works on app-scale trees under enforcement instead of exhausting descriptors, without adding any executable, file, or network permission.
- e0d25c8: Recognize the rn-dev-agent instruction block in CLAUDE.local.md during workflow preflight without modifying either Claude instruction file or hiding read errors.
1.0.4
Patch Changes
- abd7562: Report managed-Metro cleanup obligations with missing stop-proof evidence as unrecoverable in-band while preserving the obligation and authority fence.
- 4047a2e: Resolve the Observe Device pane's live frame through the same authority-fenced device target as the mirror, so a parked runner or stale CDP no longer leaves the pane blank after
cdp_run_action. - fa85d1f: Expose observed replay runtime-state writes with exact sidecar paths—session-private when fenced and project-local under
.rn-agent/statefor unfenced compatibility—and document the current fresh-session action history.
1.0.3
Patch Changes
- 7652515: Walk up to the nearest pressable ancestor for React-tree replay taps whose exact target is not a designatable TextInput, keeping input designation first and reporting type-path eligibility refusals as
INTERACTION_NOT_ACTUATEDwith the helper's reason, with pointer-event eligibility read on host views only and inherited root-to-leaf.
1.0.2
Patch Changes
- f76cd30: Allow React-tree replay taps to designate an exact editable TextInput for only the adjacent inputText step without dispatching press or focus callbacks, and resolve a stock TextInput whose composite and host fibers share one onChangeText as a single typeText target instead of refusing it as ambiguous.
1.0.1
Patch Changes
- 7911ebd: Recognize a modal-hosted testID as frontmost when its React return chain threads through the modal host fiber's alternate, instead of refusing it as behind the active modal.
- 7911ebd: Re-prove the exact CDP session target before completing a partitioned native segment's deferred origin, so a passing native prefix survives the runner park/resume handoff into the React-tree suffix instead of failing with an empty before-first-step envelope.
- 7911ebd: Resume id-based iOS action waits through the React tree with Maestro-compatible timed visibility semantics.
- d30f866: Reuse completed WebDriverAgent builds across maestro-runner spawns through a toolchain-fingerprinted persistent store with atomic publication, removing the ~75 s per-invocation WDA rebuild while keeping per-spawn cache isolation, corruption fallback, and cold-build behavior.
1.0.0
Major Changes
- 52f183f: Require Node 24 or newer and authenticate both Node calling conventions for managed-Metro descendant spawns so supported runtimes bind Metro without weakening strict proof.
Patch Changes
- 4c417dc: Report unreadable process births as unavailable while preserving genuine authority identity-mismatch refusals.
0.72.7
Patch Changes
- 18b95ca: maestro_run now emits a canonical per-attempt run ledger and attaches a ledger-derived
trailingVerificationqualifier block (trailingVerificationOnly: true, existing failureKind unchanged) to a still-failed flow whose mutating commands all provably completed while only a trailing wait/assert timed out, and cdp_run_action consumes it to refuse selector auto-repair and swap the simulator-reboot advice for verify-first guidance (final goal state stays unproven; genuine wedges keep the existing reboot hint).
0.72.6
Patch Changes
- 9f6c1d0: Route iOS exact-testID actions through the authoritative React-tree prover, reconnect that exact runtime after native segments, refuse evidenced blind native WDA surfaces, and bound runner parking cleanup by the replay deadline.
- 7d70570: Surface the exact Expo Developer Menu dismissal tool and arguments in authoritative snapshots and always-loaded guidance while cleaning the exact advanced operation generation when recoverable admission later fails.
- 9dbb2c9: Consolidate Metro origin and session authority at the signed initial-bundle handshake while keeping launch endpoint checks diagnostic-only.
- b121a18: Delete the login prologue authority latch so
cdp_login_prologueruns the exactuser-loginaction and passes or fails like any other replay, and no tool is disabled because of the login result.
0.72.5
Patch Changes
- c3a202e: Resolve wrapped React text handlers exactly while keeping native fills single-shot and verified.
0.72.4
Patch Changes
- 8616624: Bound the system-dialog fallback presence probe to a 15-second default (explicit caller timeouts through 120000ms remain accepted) so Android and session-less iOS calls with no dialog return typed DIALOG_NOT_FOUND promptly instead of appearing hung for two minutes, and teach the tool-docs generator to parse prettier-wrapped .describe() calls and wrapped zod chains so parameters such as timeoutMs and device_find.index are no longer published as undocumented unknowns.
- 37c65e3: Keep inherited learned-action inventory bounded and refuse inventory or replay if its operation-scoped corpus snapshot changes.
- 804aefb: Fix Observe HTTP request-body handling by preserving multi-byte UTF-8 code points split across TCP chunks and draining oversized bodies while returning parseable JSON 413 responses over usable keep-alive connections.
- 017ad96: Settle SQLite supervisor relaunch spawn failures on error or exit once, with a diagnostic and no hanging promise.
0.72.3
Patch Changes
- 744c313: Scope writable WDA cache provisioning to iOS, keep permission regressions portable under root, and hash action IDs in feedback-safe runner diagnostics.
0.72.2
Patch Changes
- a5feed4: Make maestro-runner pin installation idempotent with independently verified temporary stages and atomic publication so concurrent or interrupted installers cannot block the cache.
- 53ffc53: Keep valid action inventory entries available around corrupt files with typed warnings and avoid allocating Maestro report directories before preflight passes.
- 663441e: Carry the session-allocated Android Metro endpoint through Expo launch, wait, native configuration, and adapter connection.
- de5350f: Keep verified runner snapshots immutable while provisioning a lifecycle-bound writable WDA cache and attaching bounded sanitized runner diagnostics to feedback.
- 067c6ff: Route auto-login through the exact bound device and refuse unbound ambient device selection with an executable authority remedy.
- 6e69ea5: The login prologue remains a fail-stop navigation helper that requires a fresh user-login RunRecord, terminally blocks credential fallbacks, and coexists with exact locked e2e login proof without serving as PR proof.
0.72.1
Patch Changes
- 6c12426: Update packaged engineering-document references to their canonical owners.
- 48018f4: Honor the allowlisted linked-worktree
.rn-agent/actionscorpus in inventory and exact-ID replay, while still refusing dangling, foreign, whole-directory, and replaced links. - d847b3b: Add verified cross-platform Expo Developer Menu dismissal with Android parity, typed readiness outcomes, and default attaching-agent surface preflight guidance.
0.72.0
Minor Changes
- b89ff50: Require pin-cache maestro-runner
>= 1.1.24, keep SHA256 attestation for the 1.1.24 artifact this package installs as the default known-good, accept learned-actionenginePinvalues at that floor or newer, and refuse PATH, ambient Maestro CLI, older runners, and unattested binaries without a Maestro CLI fallback.
Patch Changes
- 2dd53a6: Preserve arbitrary Unicode text in Android runner commands by declaring the JSON request body as UTF-8 before NanoHTTPD decodes it.
- 6889910: An incomplete renderer-root scan (missing or malformed DevTools
renderersregistry plus the empty-ID early-exit) is no longer treated as proof the app is still mounting, so a live root on a sparse renderer ID above 5 keeps the legacy no-navigation result instead ofmounting: true. - ae0097e: An explicitly present but empty M7
# mutates:or# produces:field is now treated as invalid (?/metaInvalid) instead of omitted (-), so inventory rendering matches the GH #525 present-vs-absent legend. - 800252f: A recorded owner pid that the OS has recycled into a process this user cannot inspect now counts as proven dead instead of unprovable, so startup cleanup releases the stale ownership rather than wedging the source root forever; a proven-live owner and a genuinely unreadable identity still refuse with no force-steal, abandoned blocked contenders that never held a claim are discarded at startup, every affected refusal names a concrete remedy for interactive and headless clients, and the packaged
session-doctorcommand reports and repairs a wedged root from aclaude -psession that cannot run/mcp. - b89ff50: Close remaining Maestro 1.1.24 contract gaps: refuse complete regex selector syntax, migrate
.ymlwithout writing through inherited action symlinks, and route replay/recovery/helpers through pin-cache tools instead of ambient runner or manual login. - b89ff50: Ignore AppleDouble,
._*, and PaxHeader archive members when attesting the pin-cache payload so a checksum-matching maestro-runner 1.1.24 can spawn on Darwin extract layouts. - b89ff50: Keep execute permission on the copied pin-cache
.runner-exechelper socdp_run_actioncan spawn the attested runner. - 25348eb: Keep reconnect detection-only for legacy linked-worktree root links and provide an explicit locked repair that restores per-worktree integration and local state while sharing only the learned-actions corpus.
0.71.7
Patch Changes
- 02b2713: Make
cdp_navigation_statereport truthful mid-mount retry guidance instead of questioning the router install right after a reload (bundled-framework evidence now comes from Metro's module registry and the dev-shell allowlist matches only exact LogBox names), add an opt-in boundedwalkUppressable-ancestor press tocdp_interactdocumented as a boolean in the generated tool docs, render learned-action metadata absence as-/pre-M7with?for any parse failure including partially malformedproducesmaps, and keep the packaged sending-feedback skill host-neutral with collector resolution owned by each host's workflow. - 2a36f7d: Add
rn_sessionactionbind_sourceso linked git worktrees can rebind the session source root explicitly: the successor session mints on the declared same-repo worktree instead of the harness startup cwd, source-consuming actions accept aprojectRootfence that refuses divergent roots with the new typedSOURCE_ROOT_DIVERGENCEnaming both paths, the release hint now names the root the successor will actually bind, install-artifact content reads get a 180s budget (was 30s) so hashing a large APK over a tunneled remote-farm adb transport no longer times out, and an autostarted Observe binding yields the device axis on the firstbind_device(GH #776). - f127182: Prove the platform of custom-named devices ("rn-qa"-style simulators) against the live device inventory — booted simctl simulator names plus, only for a session-bound Android serial, that one device's adb model (ambient adb devices are never queried) — so
cdp_connectbinds the sole healthy exact-device Metro target instead of failing with a false "found 0", and name the true failing stage in exact-connect refusals. - abcd72a: Fence
adbreads for exact-connect device/platform inference whenever an authority session is present, so an available authority with no device binding, a registry lookup that throws, or an unavailable runtime whose code is notSESSION_NOT_INITIALIZED(SESSION_OWNER_LOST,PROCESS_BIRTH_UNAVAILABLE,AUTHORITY_STORE_UNAVAILABLE) no longer falls back to ambientadb devices; only a runtime that was never initialized keeps the legacy ambient read, and rawdevice_*tools are unaffected. - fb1eea9: Observe now renders an explicit blocked device state with a typed recovery hint when session authority is unbound, bounds frameless mirror pipelines with a first-frame watchdog instead of an indefinite blank stream, and shows device-mirror readiness as its own header pill so event-stream transport liveness can never masquerade as a live mirror.
0.71.6
Patch Changes
- 7eff66a: Bound dead-Metro
cdp_statusto the discovery scan budget by skipping runner-spawning picker probes when no Metro is up, and absorb the fresh-simulator first-start rn-fast-runner transient with one bounded internal retry after the failed first spawn provably exits. - 83798d9: Resolve the Observe actions/e2e project root from the bound session's declared app root (falling back to RN_PROJECT_ROOT, then heuristic discovery) and refuse truthfully on foreign, missing, ambiguous, or non-project roots instead of showing another checkout's actions or a silently empty panel.
- d046940: Admit a fresh MCP transport when the blocking claim epoch is already gone instead of rebinding the leftover blocked session.
0.71.5
Patch Changes
- a43ee7d: Allow canonical detect-libc Linux getconf/ldd module-load probes through managed Metro's descendant fence without opening arbitrary descendant execution.
0.71.4
Patch Changes
- d90e0ed: Fix the three linked iOS session-recovery defects from GH #750: extend the iOS exact-target readiness deadline to the Android 120s bound so the sole exact-device bridgeless target that re-registers slowly after the managed terminate+relaunch is admitted and B binds atomically (accepting an advisory
targetIdwhile B is unbound instead of refusing every id), reprofilecdp_dismiss_dev_client_pickerto run without the A/B authority it exists to restore and prove A/B through the managed-origin lifecycle after a successful dismissal, and refuse maestro-runner replays on a drifted engine pin when the flow (including runFlow-nested steps) uses regex text selectors that drifted runners mistranslate into impossible WDA CONTAINS predicates. - d56efe1: Fix
cdp_navigate/cdp_navigation_state/cdp_nav_graphnav-ref discovery failing with "Navigation ref not found" on multi-renderer bridgeless apps: the fiber walk now resolves NavigationContainer names through React Navigation 7's forwardRef wrapper (fiber.type.render) on every registered renderer. - de8d516: The legacy ambient connect handler exported by
rn-dev-agent-coreno longer dead-ends an explicit force reconnect while a supervised reconnect is in flight (it now supersedes it, with a newCONNECT_IN_FLIGHTrefusal code for non-force callers); the registeredcdp_connecttool was already safe and is unchanged. - 9475fe5: Reconnects now persist a pinned target's device and bundle identity so shared-Metro multi-simulator sessions re-bind the exact pinned device and fail closed with candidates listed instead of silently attaching to a sibling simulator.
- 9c2fc53: maestro_test_all now commits the proof-carrying install-receipt re-issue after a clearState corpus flow reinstalls the app and resolves the iOS app container from the authority-bound simulator UDID instead of generic
booted, so a corpus containing clearState flows no longer breaks install identity for every subsequent flow and tool call. - 992dafb: If the iOS idb live mirror stays alive without a first frame, Observe now fails that stream and falls back to the simctl screenshot loop on the same device instead of leaving a blank 0x0 image, with the bounded first-frame wait configurable via
observe.mirror.firstFrameTimeoutMs(default 30000). - bbe8791: Pin device sessions to their Metro origin: record the expected Metro port on the device binding and refuse cdpconnect and device* tools with METRO_ORIGIN_MISMATCH when the bound device's app is proven to be served by a sibling Metro (dev-client fallback), while unprovable origin evidence keeps the existing optional-origin behavior.
- e5a92d0: Preserve the signed install receipt's buildGeneration across an authenticated managed-Metro restart when the installed artifact re-proves byte-identical on-device, so
rn_session pin_dev_client force=truerecovers coherent authority without a ceremonial full rebuild while changed, missing, foreign, stale, or unattestable installs still fall back to the bumped generation and refuse fail-closed.
0.71.3
Patch Changes
- 64b29b2: Refuse maestro-runner action replay on Android below API 26 with a truthful capability diagnosis instead of an opaque install error, and point RUNNER_OWNERSHIP_MISMATCH refusals at the device_snapshot re-open repair instead of a status read that repairs nothing.
- 89ee7f4: Separate exact native device control from managed source-origin evidence so raw snapshot, screenshot, press, fill, batch, and equivalent runner operations use exact controller/source/install/device/runner authority, explicitly report
originAuthority, and keep origin-unproven captures out of strict proof, cross-platform verdicts, and learned-action evidence.
0.71.2
Patch Changes
- 390567b: Recover plugin-owned Android UiAutomation wedges on the exact bound device, while refusing unscoped cleanup and surfacing release warnings.
- 6142e32: Make
device_filltruthful: bind exactly one input (direct ref/testID or unique${name}-pressablewrapper mapping) before any mutation, resolve/focus/type in one exact native operation that never substitutes an ambient-focused field or blind-types app-wide, verify every attempt (JS, native, retype, Maestro, timeout recovery,device_batch) through a new required secret-freeverifyInputread-back sofilled:truealways means a stable exact value, and hard-fail unverifiable outcomes asNO_TEXT_INPUT_TARGET/TEXT_ENTRY_UNVERIFIEDwith mutation dispositions instead of soft-accepting them. - 639dd05: Scope Android element matching to the owned app by default, refuse covered exact accessibility targets before actuation, and report uncertain Android effects without automatically dispatching the interaction a second time.
- 115fdf9: Establish and safely clean exact physical Android Metro reverse forwards, and keep integrated builds on the session's resolved authority state home.
- d0c08e0: Make
DEVICE_BUSYrefusals report sanitized live-holder and bounded heartbeat diagnostics with ownership-safe close, dedicated-device, and stale-recovery guidance.
0.71.1
Patch Changes
- f538146: Fold the initial stale-device transfer into
bind_devicewithconfirmed: true: a proven-dead device owner is released inline through the same journaled cleanup engine with death re-proven from durable state and no capability token minted, an interrupted journal resumes token-lessly via a barebind_deviceof the same target, andrelease_stale_devicestays as a token-less compatibility alias that acceptsconfirmed: true(or a previously minted legacy handle) while live, unproven, split, foreign-worker, and mismatched-journal cases keep refusing without mutation. - 95efdf1: Launch iOS Expo dev clients through the session's authority-bound Metro when no explicit dev-client deep link was bound.
0.71.0
Minor Changes
- 7419435: Replace the text-entry fallback ladder with exact fiber/native owners that mutate once and require stable exact read-back.
Patch Changes
- e4465e5: Enforce Android exact-target readiness as one absolute 120-second wall-clock deadline, staging the exact client off-global until live proof and an atomic authority commit succeed while preserving ambient state on failure and iOS behavior.
- 26d41da: Capture sanitized local failure and recovery patterns through the existing tool observer, deduplicate and bound the evidence store, and add a read-only trend report command.
- 6c1533f: Report an installed-but-crashing fb-idb client as an interpreter incompatibility instead of looping on an "install idb" hint that reinstalls the same broken combination (#578).
- 2d4b44f: Let a Maestro flow containing a mid-flow
launchApprelaunch run to completion by re-proving the managed native origin once at flow end — reconnect-only, with no second cold start — instead of aborting between stages when the relaunched dev-client has not re-registered yet, so the flow's own post-launch steps can drive it back to the managed origin while a genuine authority mismatch still fails the run. - b2c8cc8: Redact
device_filltext from stored Observe timeline events while retaining the target, text length, status, and other diagnostic metadata. - 722349e: Let
cdp_restart hardReset=truecomplete the cold start it promises from a runner-bound session by classifying a terminated-but-unreaped process as absent rather than as an unreadable identity, and by escalating the bound runner's stop to SIGKILL after its SIGTERM grace once the pid is re-proven to carry that binding's exact birth token. - 8a7510b: Recover the session after a plugin-initiated byte-identical reinstall (for example a runner-respawn recovery) by retrying a refused install-identity preflight once behind the existing artifact-digest proof, except while a strict proof run is bound where the reinstall stays a hard stop and status projects the new install_identity_reissue_blocked state naming proof_capture discard as the way out, so rn_session status and cdp_status always report a truthful installIdentity verdict instead of claiming ready while gated tools refuse.
0.70.3
Patch Changes
- 3563b3c: Report a stale-device release that already committed as a success naming the lost fence, instead of failing the whole call with
AUTHORITY_LOST_DURING_OPERATIONwhen the authority generation moves on after the commit.
0.70.2
Patch Changes
- 661979e: Capture navigation-initiating taps on controls mounted before recording starts without duplicating app handler calls, so saved open/close actions begin with the initiating tap instead of an unreachable visibility assertion.
0.70.1
Patch Changes
- 9a3d901: Derive every gated-tool
SESSION_AUTHORITY_REQUIREDrefusal from the session's own measuredrecoveryRequirementinstead of naming unreachableaccept_handoff/adopt_staleactions, retain and project a refused proven-dead startup cleanup as astartupCleanupBlockedcarrying its typed code and truthful remedy rather than promising that another transport restart converges — redacting the refusal at the outcome boundary so no producer diagnostic, serial, PID, or path is ever logged, journaled, or projected — and propagate the ownership-recovery contract into the replay, readiness, and discovery workflow surfaces.
0.70.0
Minor Changes
- cafb36d: Add the
rn-workflowskill and/rn-dev-agent:run-workflowcommand that sequence the proven operating chain before a real device journey — declared package-manager install, read-only inventory, typedrn_sessionrecovery with status as the sole classifier, one exclusive device, managed integration and Metro, replay only viacdp_run_actionafter readiness proof, and reverse-order cleanup verified by the new deterministicworkflow-checkCLI.
Patch Changes
- 7cb0d40: Re-issue the install receipt after a Maestro
clearStatereinstall of the session's own artifact — proven by re-hashing the installed bytes against the bound artifact digest, with any other or unattestable artifact still refused asAPP_INSTALL_IDENTITY_CHANGED— and accept anappFileoncdp_run_actionthat otherwise resolves from that same receipt.
0.69.6
Patch Changes
- f20c90f: Resolve a fresh session for the next worker when the current one is released or proven stale, so
rn_session action=releaseis no longer aSESSION_OWNER_LOSTdead end and released or proven-stale rows never trigger a spuriousSESSION_AUTHORITY_REQUIRED: multiple live sessions. - b219094: Name the exact non-Git declaration remedy —
RN_DEV_AGENT_DECLARED_ROOTfor the exact existing application root andRN_DEV_AGENT_DECLARED_MANIFESTSfor the required existing manifest files — inNON_GIT_MANIFEST_REQUIREDrefusals, unavailable session status, and the canonical session-authority, setup, and readiness-workflow documentation, while keeping refusal and mutation behavior, Git-worktree identity, implicit-directory distrust, symlink containment, and the never-generated declaration unchanged. - 6716c15: Refuse orphaned integrated builds with exit code 2 and the supported
restore_integrationrepair instead of starting an unmanaged bundler, and bound every stdio-capturing session-CLI wait so wedged CLIs fail typed; projects integrated by an earlier version must re-apply integration to refresh their on-disk adapter.
0.69.5
Patch Changes
- d7a814f: Return a successful
release_stale_deviceenvelope only after its authenticated, device-scoped cleanup commit atomically advances the contender's fenced authority generation while preserving stale-owner death proof, exact claim epochs and handles, resumable runner/recorder cleanup, and neighboring source, Metro, install, package-integration, and port authority.
0.69.4
Patch Changes
- 03603da: Replace default stale-owner adoption for new
grouped-v1sessions with automatic verified-dead startup cleanup: a restarting supervisor journals obligations on the proven-dead same-root session's row before any side effect, stops its recorded children by exact identity, restores package integration only from the SHA-256-verified manifest, releases claims only after every obligation is durably complete, and mints no adoption or handoff-recipient handles, while a live or unproven owner keeps refusing and legacy sessions retain the adoption surface for drain.
0.69.3
Patch Changes
- c070bf0: Give Android exact Dev Client pinning a bounded cold-start readiness window so a target that passes its initial CDP probe but stalls during setup can be disconnected and re-listed once it becomes responsive, while preserving exact Metro, app, and device filtering and the existing iOS timeout.
- c070bf0: Keep the adb serial as Android authority while translating it to Expo's uniquely verified model or AVD display name only at the Expo CLI boundary, refusing missing, unauthorized, duplicate, foreign, or drifted mappings before Expo starts, pinning Expo's adb work with
ANDROID_SERIAL, and preserving serial-bound build completion and abort behavior.
0.69.2
Patch Changes
- e4bf0c2: Make runner unbind release its exclusive claim and clear the runner binding in one atomic registry transaction, so an interrupted device close or reacquire can no longer leave a divergent store whose dead session permanently vetoes
adopt_stalewithRUNNER_OWNERSHIP_MISMATCH(GH #692).
0.69.1
Patch Changes
- 9cccec7: Regroup authority-profile bookkeeping around the four ownership groups (Session, Target, Runtime, Automation) with every tool's resolved facet set, live probes, and error codes unchanged, and verify profile exhaustiveness at worker startup so an unprofiled registered tool fails at boot instead of at first call.
0.69.0
Minor Changes
- 5e37f16: Project strict proof as an explicit opt-in
proofOverlay(activeonly while a run is in flight betweenbegin_rehearsalandfinalize/discard) outside the groupedsession/target/runtime/automationsub-objects, keeping the existingproofchild flag and all redaction rules unchanged.
Patch Changes
- c8b03c1: Reset an exact Android CDP connection after an advertised inspector handshakes but fails the mandatory runtime probe, serially re-list only the session's allocated Metro for the same app and serial/model association, and retain the actionable probe-timeout leaf when bounded re-registration expires.
0.68.0
Minor Changes
- 5365f82: Make Observe a read-only child of the session:
observe startandrestartnow require only the live session (matching autostart's degraded mode) instead of the full device/Metro/bundle/runner authority chain, while the observe-port claim, capability and instance request authentication, fenced stop/cleanup chain, and the full authority gates on the E2E run and action panels all stay exactly as before.
0.67.0
Minor Changes
- e953f49: Add an additive grouped projection to session status — a happy-path
phase(selected/building/running/closing), the internal state indetail,session/target/runtime/automationsub-objects, andobserve/proofchild flags — alongside every existing field, with unchanged redaction.
0.66.8
Patch Changes
- 76a6045: Stop a second supervisor for the same app root from misreading the live owner as a reused PID and stealing its single-instance lock, keep blocked contenders from opening operational children, rotate expired adoption handles so
statusnever advertises a capabilityadopt_stalerefuses, add a bounded capability-authenticated release for a proven-dead device or runner owner discovered after startup that transfers only the exact device cleanup obligations, and report whether recovery needs a transport restart, an attach, or an adoption.
0.66.7
Patch Changes
- 3ee229d: Keep managed Metro descendants strict by default while allowing only Expo's canonical runtime-version manifest utility without session capability and requiring exact managed launch provenance.
0.66.6
Patch Changes
- 5b0a93f: Keep
.rn-agentreal and worktree-local by inheriting only.rn-agent/actionsthrough consented setup and repository-local post-checkout integration, keeping SessionStart report-only, and migrating recognized legacy root links without copying mutable integration or session state.
0.66.5
Patch Changes
- e7c04dc: Keep session-bound Dev Client discovery and reconnect, reload, and restart recovery on the exact managed Metro port, and recognize modern Bridgeless Hermes targets whose inspector metadata omits the legacy
vmfield, while continuing to require the signed runtime marker before authority becomes ready.
0.66.4
Patch Changes
- 89bdf7a: Classify maestro-runner 1.1.x ID-wait misses as SELECTOR_NOT_FOUND, surface bounded head+tail failure evidence with the exact selector on every terminal path, resume reactive CDP/JS replay at the failed selector instead of redispatching executed mutations, and refuse launchApp keys the CDP transport cannot honor.
0.66.3
Patch Changes
- 8cd1ea2: Fix inline Maestro cold-start timeouts and authority transitions, bridge-lifetime cleanup refusal, optional learned-action bundle gating, truthful date-picker failures, and sanitized exact-device iOS screenshots with relative-path support.
0.66.2
Patch Changes
- 802efa2: Allow runner-verified exact iOS keyboard targets to activate once while removing corruption-prone automatic keyboard swipes and rejecting stale runner artifacts.
0.66.1
Patch Changes
- 7937883: Coalesce helper setup and reinjection per execution world, require the exact helper version, and report bounded truthful helper-health evidence.
0.66.0
Minor Changes
- 784c880: Add fail-closed fenced worktree sessions with exact build, Metro, device, runner, Observe, and strict-proof authority.
Patch Changes
- 784c880: Store the package-integration restoration manifest durably inside the session binding, let on-disk manifest bytes authorize only the current owner's restore_integration, and make stale adoption, handoff acceptance, and resumed cleanup validate their capability non-mutatingly — resumed handoff cleanup now re-proves the exact consumed handoff and its original token against a durable cleanup binding pinned to the accepting target session and claim epoch, so a stale-adoption transfer revokes the old handoff capability in favor of the adoption handle — and refuse before any transfer or mutation unless the binding itself carries a SHA-256-verified restoration manifest, reporting file-state diagnostics and the supported recovery step instead of auto-reconciling.
- 784c880: Launch Expo session builds with a command shape the installed Expo CLI accepts and release pending build authority through an authenticated abort when the native command fails before completion.
- 784c880: Give authenticated managed-Metro descendants an open stdin so Tailwind-backed transforms cannot stall, and fail typed instead of hanging when a child's first authenticated exchange never completes.
- 784c880: Guarantee single-emission signed Metro startup with gate-composed strict-proof input handling, pre-helper error evidence, allocated-port exact-device reconnects, and authoritative migration, runner, and stale-Metro recovery.
0.65.8
Patch Changes
- ef084e4: Add native Codex parity for all fifteen workflows, deterministic read-only plugin health and restart guidance, Codex-native AGENTS.md setup, and complete packaged helpers. Disable best-effort command migration and publish a usable
proof_captureaction schema while retaining strict branch validation.
0.65.7
Patch Changes
- 0e36a39: Classify WDA bootstrap failures from full structured replay evidence without adding preparation side effects.
- 0e36a39: Close final issue #588 validation gaps by failing closed when iOS runner authority is lost after typing, reaching Bridgeless keyboard blur, honoring exact active-session lifecycle identity, accepting the packaged Codex supervisor as candidate authority, and exposing per-call blind-probe compatibility control.
- 0e36a39: Close issue #588 live-validation gaps with exactly-once keyboard recovery, propagated iOS fault controls, non-rewriting action telemetry, explicit replay evidence, and device/app-scoped native logs.
- 0e36a39: Bind Maestro replays to the exact active device, reject mismatched direct runner or WDA provenance, and persist RunRecord device identity from execution evidence instead of requested metadata.
- 0e36a39: Guard taps with versioned fresh keyboard geometry and dismiss visible keyboards before unknown-geometry interactions.
- 0e36a39: Let each iOS XCTest runner request an OS-assigned listener port so parallel simulators cannot collide on port 22088, and make listener startup failures fail XCTest instead of producing a misleading passing result.
- 0e36a39: Launch exact Android sessions on keyless AVDs and report app-launch failures separately from runner startup failures.
- 0e36a39: Allow successful action replays to append runtime telemetry when only the tracked YAML mtime baseline is stale, while retaining sidecar CAS conflict detection and strict guards for every YAML-mutating promotion or repair.
- 0e36a39: Prove Bridgeless app identity from canonical Metro metadata and prevent agent prompt text from impersonating foreign iOS runners.
- 0e36a39: Accept exact, unambiguous maestro-runner device identity from its pinned-device log and structured report, and scope Android app lifecycle to the active session's exact adb serial, while continuing to reject missing, contradictory, shared, or multi-device evidence.
- 0e36a39: Refuse explicit and session-derived CDP platform mismatches while retaining warned best-available filterless discovery.
- 0e36a39: Restore actionable component-state truncation and tree scan-budget diagnostics without expanding tool schemas.
- 0e36a39: Scope iOS attach-only app liveness checks to the resolved simulator UDID instead of the ambiguous
bootedalias, and refuse when exact device identity is unavailable. - 0e36a39: Bind strict cross-repository proof receipts to both the app fixture and the exact packaged plugin runtime.
- 0e36a39: Require exact independent readback for iOS type-timeout recovery and poison and reap the wedged runner.
0.65.6
Patch Changes
- e3986d3: Make Android learned-action failures, device affinity, launch accessibility readiness, restart recovery, and batched input ordering deterministic and explicit.
0.65.5
Patch Changes
- 2bf6d4f: Discover React Navigation refs and state across every renderer ID registered with the React DevTools hook, while preserving the bounded numeric renderer probe so partial registries keep legacy coverage.
0.65.4
Patch Changes
- f66eb3f: Isolate the empty-Metro lifecycle integration tests from live default-port Hermes targets (#577): CDP discovery's default port list (8081/8082/19000/19006 +
RN_METRO_PORT) is now resolved lazily per call, and a newRN_CDP_DISCOVERY_PORTSoverride replaces it entirely — so the integration suite owns its whole discovery surface and stays deterministic while a real React Native app is running on the host. Production discovery is unchanged when the variable is unset.
0.65.3
Patch Changes
- 61f136e: Fix observe UI Route/Store/Tree panels staying empty while the device mirror shows the running app (#579): the panels now auto-read live state through a new
GET /api/state/(route|store|tree)endpoint that resolves the CDP client at call time — so they populate on a healthy connection without the agent having run the introspection tools and recover after a reload/reconnect — plus a manual "read live" refresh button in each panel.
0.65.2
Patch Changes
- 619c5fe: Accept a visually matched final proof screenshot when iOS video metadata ends up to two seconds before the assertion timestamp.
0.65.1
Patch Changes
- fdfa8bb: Make strict proof portable, TypeScript-native, and tolerant of clean recordings up to five seconds beyond the adaptive target.
0.65.0
Minor Changes
- 4e9bf7e: Add strict storyboard-gated video and screenshot proof receipts for unattended feature delivery.
0.64.0
Minor Changes
- 9359723: Story 10 (GH #391) — text-input reliability recipes. iOS: the runner's
typehandler now waits (≤1 s, best-effort) for the keyboard before the first keystroke and types in Maestro's two-burst shape (first character, 500 ms pause, remainder), killing the dropped-first-keystrokes flake class; typing telemetry (typingBurst,keyboardWaitMs) surfaces in the response and threads intodevice_fill'smeta.typing. Android: the runner'stypeclassifies itsACTION_SET_TEXTread-back (accepted / transformed / rejected), falls back to per-char keyevents at Maestro's 75 ms pacing when the set was ignored, and reportsSET_TEXT_REJECTEDwhen both tiers fail. Bridge:device_fill's Android unsafe-char/length short-circuit to chunkedadb input textis removed — emoji and long text now reach the runner's full-UnicodesetTextprimary, with chunked adb demoted to a genuine last resort andSET_TEXT_REJECTEDdescending the ladder without wasted re-taps.
0.63.1
Patch Changes
- 53c3fb3: Auto-heal
KEYBOARD_OCCLUDEDtap refusals JS-first (GH #379): when the iOS keyboard guard refuses adevice_press/device_longpressbecause the tap point is under an iPhone QWERTY keyboard with no dismiss control, the bridge now dismisses via the new injected__RN_AGENT.dismissKeyboard()helper (RNKeyboard.dismiss(), falling back to blurring the focused TextInput host instance), refreshes the snapshot (targets relayout when the keyboard lifts), and retries the tap exactly once — surfaced asmeta.keyboardGuard: "js_dismissed"+meta.keyboardAutoHeal. The retried tap re-runs the native guard, so a dismissal that didn't take effect re-refuses instead of tapping through. Also ships the #370 review follow-ups: the iOS refusal now carries a structuredcode: "KEYBOARD_OCCLUDED", both runners report the guard step's native duration (lifted tometa.timings_ms.keyboardGuard), andsurfaceKeyboardGuardhardens its never-throws contract against non-object JSON envelopes.
0.63.0
Minor Changes
- de8f1c1: Story 14 (#407): runner transport recovery — every /command carries a commandId; on an ambiguous post-send failure the client issues one short status probe against the runner's outcome journal before invalidating. Recovered results return with meta.transportRecovery; mutating verbs are never auto-resent, eliminating double-fired taps; read-only verbs may be resent once. Unresolvable probes fall through to the existing invalidation path unchanged. Both native runners (iOS rn-fast-runner, Android rn-android-runner) gained a bounded command-outcome journal (32 entries, 8 KB UTF-8 body cap, snapshot/screenshot recorded state-only, error outcomes journaled) and the read-only
statusverb that replays a prior command's retained outcome.
0.62.3
Patch Changes
- dc5a87b: Harden observe-recorder screenshot ingestion (GH #429): the recorder now only reads screenshot files the capture pipeline itself just wrote (single-use trust grants registered by
device_screenshot), instead of any absolute image path named in a tool observation — closing an arbitrary local-file read surface on the observe server. The read itself is now TOCTOU-safe: one descriptor for the size check and the read,O_NOFOLLOW(no symlink following), and a hard byte cap enforced on the bytes actually read.
0.62.2
Patch Changes
-
dba5eb7: Observe UI test confidence (#438, audit P1-A): the web SPA and the observability server now share one wire-types module, the UI carries stable
data-testidselectors, and a Playwright e2e suite exercises the real server against the committed bundle on every PR.src/observability/wire-types.ts(pure types, zero Node imports) is the single source forAgentEvent/AgentEventFamily, the e2e run shapes (E2eFlowResult,E2eRunRecord,E2eRunIndexEntry, verdict/classification unions),ActionSummary, and the action-run result. The server modules re-export it andweb/src/types.tsre-exports it too — the hand-copied twins are gone, and the web-bundle CI gate now runstsc --noEmiton the SPA so server↔UI drift is a compile error (previouslyvite buildonly transpiled, so nothing checked).- 27
data-testidattributes across Header, FilterBar, Timeline, DevicePane, StatePane, ActionsPanel, and E2ePanel. - 10 Playwright specs (headless chromium) boot the real
ObservabilityServerwith a seededRecorder+ stub e2e deps on an ephemeral port: timeline render + family/errors/search filters, event detail, device hero screenshot, SSE live update, regression history + drill-down, and the CSRF-guarded suite/action run round-trips (including a 403 negative). - Server hardening from review: oversized
POST /api/e2e/*bodies now return a bounded 413 instead of becoming an unhandled rejection, and the CSRF token is injected viaJSON.stringify+<escaping so it can never break out of the inline bootstrap script.
0.62.1
Patch Changes
-
78700be: Golden wire-contract tests from captured runner payloads + named CI gate (#437, audit P0-B).
The biggest escaped-bug cluster (#396, #353, #418) was host↔runner wire-contract drift where hand-written fixtures encoded the wrong shape, so green tests certified broken behavior. This closes that hole:
test/contract/capture-goldens.tsrecords REAL/health, rawPOST /command snapshot, error-envelope, and bridgedevice_snapshotpayloads from live rn-fast-runner / rn-android-runner sessions into committed fixtures undertest/fixtures/goldens/<platform>/, each stamped with capture provenance (device, OS, runner version, date). Goldens are captured, never hand-written.gh-437-golden-contract.test.tspins the TS parsing layer (classifyRunnerCompatibility,findRefByTestID, the ref-map oracle + snapshot verdict) against those captured payloads for both platforms, and pins the capturedvstamp toRUNNER_PROTOCOL_VERSION— a protocol bump fails CI until goldens are re-captured against the new runner (refresh cadence, enforced).- New named CI step "Runner wire-contract gate" runs the #418 tri-surface
command-enum sync, the #383 protocol-version sync, and the golden contract
tests via
yarn workspace rn-dev-agent-core test:contract, so wire-contract drift fails a visible gate instead of hiding in the unit blob.
0.62.0
Minor Changes
-
3b27e7d: Story 16 (#409) — snapshot quality verdicts: degraded captures must say so.
Every tree/snapshot capture now carries a structured quality verdict computed once at capture time, so a sparse or empty result caused by a degraded walk is no longer indistinguishable from a legitimately empty screen:
cdp_component_treereturnsmeta.treeVerdict(state: ok|degraded|failed,path,reasons,rootsSeeded,scannedNodes,effectiveDepth,droppedSubtrees,collapsedChildLists,rendererErrors,unscannedRendererIds). Previously-silent drop classes are now counted: per-renderer exception swallows, registered-but-unscanned renderers (the #126 early-exit class), depth-cap subtree drops, scan-budget/wall-clock exhaustion, and output truncation. Requires injected helpers v34 — a stale bundle simply omits the verdict.device_snapshot(iOS + Android runners) returnsmeta.snapshotVerdict(state,source,nodeCount,refMapUpdated,reasons).- Sparse captures never overwrite the last-known-good @ref map: a zero-node
snapshot leaves refs bound to the last verified capture
(
meta.snapshotVerdict.refMapUpdated: false, reasonempty-capture) instead of wiping the map self-healing taps depend on. - Interactive consumers fail closed:
device_find(exact + fuzzy) anddevice_focus_nextrefuse a zero-node capture withSNAPSHOT_DEGRADEDrather than asserting NOT_FOUND / "nothing on screen" on evidence that cannot support it.
0.61.9
Patch Changes
- 2cc8c82: fix(device-system-dialog): make SpringBoard-owned iOS dialogs reachable (#545).
device_accept_system_dialog/device_dismiss_system_dialogwere Maestro-only, and Maestro's iOS driver only sees the app under test — the deeplink "Open in ?" confirmation and other SpringBoard dialogs timed out on every label probe (DIALOG_NOT_FOUND while the dialog sat on screen), and theidb ui tapescape hatch crashes upstream ("no current event loop"). With an open iOS session the tools now route through rn-fast-runner first: its snapshot returns a blocking SpringBoard modal exclusively as an Alert-rooted payload, and press resolves to a coordinate tap that lands on whatever owns the pixels. When the modal is up but no probed label matches, the tool returns the dialog's actual buttons (DIALOG_BUTTON_NOT_FOUND+availableButtons) instead of burning N×4s Maestro probes that can never match.device_deeplinkon iOS now best-effort auto-accepts the "Open" confirmation before its picker check and annotatesmeta.openDialogTapped; the iOS DIALOG_NOT_FOUND hint documents the last-resort SpringBoard restart recovery (launchctl kickstart -k system/com.apple.SpringBoard). Maestro stays as the fallback for Android, in-app alerts, and session-less iOS calls. (The issue's third finding — picker dismiss being Android-only — already shipped in #523/#531.)
0.61.8
Patch Changes
- 6be3bca: fix(rn-android-runner): align Android
hittablesemantics with iOS (#520). Both Android sources now route through a single shared predicate implementing the #395 definition — "enabled AND visibly on-screen": the snapshot path (window-hierarchy XML) was reporting barevisible-to-user(a DISABLED but visible control counted as hittable), and the find path (UiObject2) was reporting bareisEnabled(an enabled element with an empty visible region counted as hittable). Divergent semantics meant platform-dependentdevice_findranking (+1000 hittable boost) anddevice_batchdead-control annotation for identical screens. The newHittableSemanticsobject lives in the main sourceset so the JVM CI lane pins it deterministically; a TS grep-sync test pins the dispatcher wiring (gh-397/gh-418 style). The Android runner's/healthnow advertisesHONEST_HITTABLElike iOS. Device-verified on a Pixel 9 Pro emulator: snapshot distribution non-uniform (62/63 hittable; the fixture's new deliberately-disabled button reportshittable=false, which the old path reportedtrue), andfindTextdiscriminates enabled ("Increment" → true) vs disabled ("Disabled" → false). No wire-shape change (capability list is additive, no protocol bump); existing runner artifacts pick the semantics up on their next rebuild/upgrade.
0.61.7
Patch Changes
- 41924c4: Refresh the committed package-lock.json and major-cap the security-floor
overrides(GH #441). Marketplace installs stopped consuming this lock when the dependency-free bundled host runtime shipped (ensure-cdp-deps.shearly-exits), but the lock remains a committed artifact: CI's packaged-artifact smoke installs against it, and any future npm resolve inherits the overrides. The stale v0.38-era resolution is refreshed with in-range updates (ws 8.21, yaml 2.9, hono 4.12.29, @hono/node-server 1.19.14, fast-uri 3.1.3), and the open-ended>=override floors are capped at each dependent's declared major —>=1.19.13alone resolved @hono/node-server 2.x against the MCP SDK's^1.19.9on a fresh regen. Re-staleness tripwires: a gh-441 unit test plus a sync-versions.sh check (CI) and--fix(release version bumps) keeping the lock's version fields tracking package.json.
0.61.6
Patch Changes
- 74da26f: Fix #523: break the expensive iOS recovery chain. (1)
cdp_reloadthat ends with zero targets now auto-chainssimctl terminate + launchand reconnects instead of returning RECONNECT_TIMEOUT (recovered_via: terminate_launchin meta). (2) The last-connected bundleId is persisted per platform in.rn-agent/state/last-bundle-ids.json, socdp_restart hardReset:truecan relaunch even after a bridge worker restart wiped the in-memory cache. (3)cdp_dismiss_dev_client_pickernow works on iOS (snapshot/press route through rn-fast-runner — the legacy-daemon guard was obsolete), also clears the stale-server "Error loading app" dialog, prefers the picker row matching the project's Metro port, and deprioritizes stale link-local (169.254.x) entries;device_deeplinkauto-dismisses the picker on iOS too.
0.61.5
Patch Changes
- 15def1d: fix(rn-fast-runner): honest
hittablein iOS snapshots (#395).hittablenow means "enabled and its center is on-screen" (plausibly tappable, half-open viewport bounds). The old occlusion heuristic counted trailing transparent full-screen containers (gesture-handler roots, portal hosts) as occluders and marked every nodehittable=falseon real RN screens — poisoningdevice_findcandidate ranking,device_batch's dead-control annotation, and starving the hittable-first screen-rect union (PR #517) into its all-nodes fallback. Real modal occlusion was never representable anyway: RN modals get their own UIWindow, so occluded content is absent from the XCUI tree entirely. Snapshot filtering (compact/interactiveOnly) is now explicitly hittable-independent, so snapshot sizes must not grow (small decreases expected: trailing contentless overlay wrappers the old algorithm marked hittable are no longer included). Intentional behavior change: a contentless, non-interactive-typed control rendered LAST in the tree (e.g. an identifier-less icon-only Image) was previously included by position-dependent luck (no later siblings → old hittable=true → included via the hittable escape hatch) and is now consistently excluded — give such controls a testID. Consumer-side calibration for the honest flag:device_findranking now uses type priority first with hittable as a same-type tiebreak, the settle hash no longer includes hittable (it is derived from enabled + rect, both hashed, and its unquantized edge bit defeated the 4px jitter absorption), the screen-rect union is capped by Application/Window extents on iOS (center-on-screen elements can legitimately straddle the edge), and a healthy runner artifact missing the new compiled-inHONEST_HITTABLEcapability queues a one-shotmeta.noteadvisory that its hittable values are stale. The refusal half of the original #395 report ("no longer hittable" errors on modal screens) was a stale-ref message fixed by #396. No wire-shape change; new plugin releases pick this up via their per-version runner artifact. Dev checkouts: deletepackages/rn-fast-runner/build/DerivedDatato rebuild.
0.61.4
Patch Changes
- f5beabb: Story 06 Phase C.2 (#387): the LLM-behavior evals now run on headless Claude Code (
claude -p) funded by a Claude subscription — locally via the logged-in CLI, in CI via aCLAUDE_CODE_OAUTH_TOKENsecret. Themcp-server-testerdependency (and its judge-model patch) is retired; fixtures, baseline semantics, and the compare-baseline gate are unchanged.
0.61.3
Patch Changes
- 1f07b3f: Post-merge review fixes for the Phase B device-smoke surface (two independent reviewers, findings cross-validated): (1) the screen rect used by direction device_scroll/device_swipe and scrollintoview's viewport check is now a hittable-first union — off-screen mounted content (RN FlatList windowing keeps rows past the fold in the tree with real coords, marked hittable:false) can no longer inflate the viewport and push gestures off the physical screen; all-nodes union remains as fallback for snapshots without hittable data. (2) The three direct fastSwipe call sites fall back to resolveBundleId('ios') when a legacy session lacks appId, closing the reopened host-app-drag gap. (3) The nightly integrity lane captures zip listings before grepping (grep -q + pipefail could SIGPIPE-false-fail a successful match). (4) The smoke's counter assertion is anchored (/^count: 1$/) and the screenshot check documents its encoding-only scope.
0.61.2
Patch Changes
-
abf974f: B269 (remaining half): treat idb client health, not PATH presence, as the source of truth. fb-idb installed under an incompatible Python (e.g. 3.14) crashes on every invocation; previously it counted as "present" everywhere, so the auto-installer never repaired it and the observe mirror selected the doomed idb tier and died ("idb video-stream keeps exiting", B263) instead of using the working simctl fallback.
detectIdb()(mirror tier selection) now probes a realidb --helpinvocation — ENOENT, a crash, or a hang all resolve to the simctl tier.ensure-idb.sh's foreground check health-probes the client and flags a present-but-broken one; the background worker replaces it (uninstall → reinstall → re-probe) and, if the reinstalled client still crashes, uninstalls it and marks the attempt failed — a crash-on-invocation client is never left on PATH, and the 24h backoff retries when a fixed fb-idb release ships./doctor's idb row now scores the client by the health probe instead of PATH presence.
0.61.1
Patch Changes
- e4cdf48: Fix idb-companion installation on current Homebrew: brew now refuses formulas from untrusted taps, so
brew tap facebook/fb && brew install idb-companionfails with "Refusing to load formula … from untrusted tap" — the plugin's auto-installers (ensure-idb.sh,ensure-idb-companion.sh) silently failed every session while pipx still installed the (Python-3.14-broken) client, leaving the worst combination: broken client on PATH, no companion (B269). The install commands now runbrew trust facebook/fbfirst (tolerant no-op on older Homebrew without thetrustsubcommand), and all ~10 user-facing hint surfaces (doctor, rn-setup skill, mirror hints insources.ts, SessionStart warning, physical-device probe) show the trusted three-step command.
0.61.0
Minor Changes
- 272c113: Add Codex plugin metadata and Yarn workspace package boundaries alongside the existing Claude Code plugin surface so rn-dev-agent can be used from both agents.
0.60.2
Patch Changes
- 8c18951: Observe UI: surface the idb install hint as a banner under the device pane header while mirroring runs on the ~6fps simctl fallback, instead of an ellipsized footer line that truncated the brew command. Error hints stay in the footer. The idb install command is corrected everywhere to include the required tap (
brew tap facebook/fb && brew install idb-companion) — including the executed installs inensure-idb.sh/ensure-idb-companion.sh, which previously failed on untapped machines./rn-dev-agent:setupnow diffs an already-injected CLAUDE.md template block against the plugin's current CLAUDE-MD-TEMPLATE.md and offers an in-place refresh when stale (new<!-- rn-dev-agent:template-end -->sentinel delimits the block; legacy blocks are upgraded on refresh).
0.60.1
Patch Changes
-
f74b5b7: Observe UI: make the right state pane fit its width, and slim the timeline column.
The right pane is a fixed ~26% column (~340-450px), but the actions tab rendered a 5-column table and the e2e tab 3- and 4-column tables. Tables cannot shrink below their column content, so at typical window widths the Status/Params/Run columns were clipped clean off the pane — the Run button was unreachable — and action ids line-wrapped mid-word. Both tabs now render stacked rows designed for a narrow column:
- Actions: one item per action — id (truncating, full value on hover) + status badge + Run on the first line, intent wrapped below (2-line clamp), param inputs flex-wrapping to the available width instead of fixed 110px columns, result/output underneath.
- E2E: suite results and run history as one-line rows — pass/fail mark,
truncating test/run id, duration, classification badge or
2✓ 1✗totals + verdict — with error excerpts wrapping below and the expanded run detail reusing the same row layout. - Pane guards:
.pane.rightgetsmin-width: 340px, tabs wrap instead of overflowing, long live routes break instead of pushing the pane wide. - Layout rebalance: the left timeline column drops from 40% to 33%
(
min-width: 380px; summaries already ellipsize), and the device pane no longer greedily takes all remaining width — the mirror is a portrait phone screen capped at ~100vh, so the pane is capped at 400px and the state pane absorbs the surplus instead.
0.60.0
Minor Changes
- 24842f8: Story 13 (#397) Phases 1–2: maestro-runner engine pinning and a proactive blind-probe. The installer now installs the tested pin (
1.0.9) exactly, verifies its checksum fail-closed on fresh downloads, and warns on local drift;cdp_status.replayEngine+/doctorreport engine, version-vs-pin, and known quirks;maestro_runcarriesenginePinmeta and warns once on drift (opt-in hard enforcement:RN_ENGINE_PIN_STRICT=1).cdp_run_actionon at-risk iOS runtimes (>= 26, or a recent device-matchedTRANSPORT_BLINDwith clean-pass reset) probes the CDP tree first and, when the action's anchor is visible, skips the doomed ~40s WDA attempt and replays via CDP/JS directly —RunRecordgains additivedeviceId/blindProbe, probe-routed failures classify asFALLBACK_REPLAY_FAILED(never falseTRANSPORT_BLIND), probe-routed passes never auto-promote, and the DB mirror persists the new fields. Opt out withRN_BLIND_PROBE=0.
0.59.2
Patch Changes
-
d041bac: Harden the Android raw screenshot capture path (
device_screenshot, GH #428), mirroring the iOS hardening from #427:- Truncate-before-success: raw capture now stages
adb exec-out screencapbytes in a unique sibling temp file andrenameSyncs onto the caller's path only after both the write stream drains and adb exits 0. A failed or timed-out capture can no longer truncate-then-delete an existing file the tool never created. - Multi-emulator first-pick: with several emulators booted and no session
binding, resolution now refuses (exactly-one-or-null via
resolveAndroidEmu) instead of silently grabbing the first emulator — matching iOS exactly-one-or-refuse. Sessions still bind to their device id. - adb child leak on stream error: a write-stream error (ENOSPC/EACCES) now
unpipes and kills the
adbchild before settling, instead of leaving it running blocked on stdout.
- Truncate-before-success: raw capture now stages
0.59.1
Patch Changes
- f583249:
cdp_dev_settingsgains ahideDevMenuaction that dismisses the iOS expo-dev-client dev menu bottom sheet over CDP viaExpoDevMenu.hideMenu()(#335). Because it runs throughclient.evaluateinstead of a coordinate tap/swipe, it never triggers the touch-induced Hermes detach the issue describes — the JS thread stays attached and the in-memory store survives.cdp_reloadnow also best-effort auto-dismisses the menu on iOS after reconnect, so the agent lands on the app instead of behind the sheet. The dismiss resolves theExpoDevMenunative module through a multi-tier chain (globalThis.expo.modules→NativeModules→ TurboModule proxies) and is a silent no-op on non-expo builds.
0.59.0
Minor Changes
- d6f72f7: Story 05 (#386) self-healing taps: stale
@reftaps re-resolve inline by identity signature (unique-match only; ambiguous/absent STALE_REF now lists candidates), swallowed taps retry exactly once via settle-hash change detection (meta.reResolved/meta.tapRetried/meta.noUiChange), 3 consecutive no-change taps on distinct targets surface a wedged-runtime hint, anddevice_batchtestID resolution refuses ambiguous matches (AMBIGUOUS_TESTID). Opt-outs:retryIfNoChange: falseper call,RN_SELF_HEAL=0global.
0.58.0
Minor Changes
- dabe8cc: Prebuilt runner artifacts (Story 01, #382): the iOS rn-fast-runner and Android
rn-android-runner now resolve from a verified prebuilt artifact — a SHA-256-checked
local cache, then a download of the release asset for the exact plugin version —
before falling back to the on-machine build. This removes the multi-minute cold
xcodebuild/ Gradle build from the firstdevice_snapshot action=openonce a release ships the artifacts. Resolution is fail-open: any missing manifest, offline state, 404, checksum mismatch, or unsafe archive falls back to the local build with a one-linemeta.note, never a hard failure.RN_RUNNER_BUILD=localforces the local build.cdp_status//doctornow report runner provenance (prebuilt v<X>vslocal-built). Until a release ships the artifacts, builds resolve tolocalby design.
0.57.0
Minor Changes
- 8740f75: Observe UI: single-page layout — the Live/Regression view split is gone. The right column now has five tabs (route | store | tree | actions | e2e): learned actions run from the main page next to the live mirror, and E2E suite runs + history live in the e2e tab. The mirror status/hint moved to a slim footer so the device pane keeps its full height.
0.56.0
Minor Changes
- a33f19d: Observe UI: continuous live mirroring of the simulator/emulator screen (Maestro-style MJPEG). New
GET /api/device/mirrorstream — idb (20–30fps) or simctl loop (~6fps) on iOS, adb screenrecord+ffmpeg on Android emulators and physical devices. Zero capture cost with no tab open; per-tool-call screenshots are skipped while the mirror streams. Config:observe.mirror.enabled/observe.mirror.fps, envRN_AGENT_OBSERVE_MIRROR=0to disable.
0.55.1
Patch Changes
- 396e862: rn-android-runner
findTextrefuses missing/blanktextwith a typedINVALID_ARGUMENTerror (#444). PreviouslyoptString("text")silently defaulted to"", falling through toBy.textContains("")— which matches an arbitrary node — so a malformed request reportedfound: truefor whatever element UIAutomator visited first instead of surfacing an argument error. The guard runs in the dispatch when-branch before any selector is constructed; a source-sync test (gh-418 style) enforces it in CI without an emulator.
0.55.0
Minor Changes
- 683a132: Story 04 (#385): shared two-tier settle engine. Every mutating device_* verb now waits for the UI to actually stabilize instead of relying on fixed sleeps: Android gates on a new
isWindowUpdatingrunner probe (capabilityWINDOW_UPDATE) then falls back to snapshot-hash equality polling; iOS polls a new on-runnerisScreenStaticSHA-256 screenshot compare (capabilitySCREEN_STATIC, Maestro's 3s screen-settle budget) with the same snapshot-hash fallback. Results surfacemeta.settle: {method, settled}+meta.timings_ms.settle.device_filldrops its fixed 150ms focus delay when settle ran and pins its target coordinates once up front (--at-x/--at-y) so the settle's ref-map refresh can never retarget the fill mid-call; its corrective retypes skip settle (their stability check is the CDP read-back).device_batchsettles between steps by default at a batch-scoped 2500ms budget (per-stepsettle: falseescape hatch) and its blanket 300ms inter-step delay defaults to 0 while settle is on. Legacy runner artifacts (no new capabilities) transparently degrade to snapshot polling — no rebuild required, the new verbs are deliberately NOT in the required-command gate. Opt out globally withRN_SETTLE=0or per batch step withsettle: false; tune the per-call budget withsettleTimeoutMs(a budget knob, not a disable switch). A perpetually-animating screen settles via hierarchy stability or returnsmethod: 'timeout'at budget — bounded, never hanging.
0.54.1
Patch Changes
- c15bc52: iOS
device_screenshothonors the caller'spath(#422): iOS pixels now route toxcrun simctl io screenshoteven with an rn-fast-runner session open — the runner's screenshot verb writes inside its own sandbox and returns a relativetmp/…path the host can never serve, which blanked the observe UI panel and brokesipsresizing (meta.resize.reason: no-dimensions). simctl was already the flow-active and runner-down backend; it is now the sole iOS pixel path ("pixels → simctl", D1249). Android is unchanged (its runner honorsoutPathhost-side). Defense-in-depth: the observe recorder rejects relative screenshot paths instead of resolving them against the bridge cwd. - c15bc52:
cdp_run_actionno longer dead-ends in an opaque UNKNOWN when WDA dies at launch (#423). Root cause chain from the field failure: the #317 CDP/JS replay fallback covers this exact case, but its single tree probe ran while CDP was mid-reconnect (the failed flow had just relaunched the app), was silently swallowed, and the fallback never engaged. The probe now retries (bounded, default 3×1.5s) until the probe testID is actually present — tolerating both a reconnecting CDP and a still-mounting app — and every skip is surfaced asmeta.cdpJsFallback: { attempted: false, reason }(no-replay-deps | no-probe-testid | cdp-unreachable | testid-not-in-tree). Acdp-unreachableskip appends actionable guidance (checkcdp_status, reconnect, stop foreign XCUITest automation) instead of a bare "failure not auto-repairable". Also (#422 hardening): the simctl UDID parsers now only consider iOS runtimes (a booted paired watchOS/tvOS simulator can neither win the screenshot UDID pick nor make the single iPhone look ambiguous toresolveIosUdid), and raw captures bind to the open device session's UDID when platforms match instead of picking the first booted device. - c15bc52: iOS cold start persists a reusable
.xctestrun(#424):startFastRunner()now runsxcodebuild build-for-testingfirst when no test product exists and then launches via the sametest-without-buildingpath as every warm start, instead of a single barexcodebuild test— which never writes a.xctestrun, so self-built runners were permanently "not prebuilt" and every runner death cost another multi-minute cold build. The build phase keeps the 360s cold timeout; the launch phase uses the standard 30s ready window. The #418 stale-artifact rebuild tier funnels through the same path, so it also leaves a reusable artifact now.
0.54.0
Minor Changes
- 8a21532: Command-surface gate (#418, B235): both native runners enumerate their supported
commands in
/health.commands(iOS derives it fromCommandType.allCases, Android from a sync-testedSUPPORTED_COMMANDSlist) and the liveness gate classifies a runner missing any bridge-required verb as stale (missing-commands). Remediation is tiered:device_snapshot action=openauto-invalidates the stale artifact and rebuilds — iOS deletes DerivedData and cold-builds (once per plugin version, behind a checkout-scoped build lock), Android deletes the runner APKs so self-install Gradle-rebuilds; mid-flow device tools refuse fast withRUNNER_COMMANDS_STALEinstead of silently building. An unknown verb reaching the iOS runner now returns a typedUNSUPPORTED_COMMANDerror instead of a raw Swift decode failure. Root cause of B235 fixed: the explicit iOS keyboard-dismiss path posteddismissKeyboard, which no Swift artifact ever accepted — the wire verb is nowkeyboardDismiss.cdp_statussurfacesdeviceSession.runnerProtocol.missingCommands. Hardening from per-edit review: the iOS runner validates client-supplied Content-Length (400 on invalid instead of crash/hang) and Android foregrounds alias verbs (press/fill/scroll) before dispatch.
0.53.0
Minor Changes
- d5acd6b: Observe web UI overhaul: session header (connection, app, route, duration, call/error stats), filterable + searchable timeline with follow/pause autoscroll, device-screenshot hero pane with route chip, guided empty states, inline param inputs for learned actions (server now honors UI-provided params), expandable action output, and E2E run-history drill-down with per-flow error excerpts. The SPA is split from one 670-line file into focused modules.
0.52.0
Minor Changes
- d12f18f: feat(rn-fast-runner): quiescence bypass — make XCTest's private quiescence wait a no-op inside the iOS runner (#384, Story 03). RN apps with Reanimated worklets/looping animations never report idle, so XCTest queries and snapshots stalled until per-symptom patches (runner-timeout shim, HID-synthesis scroll, 35s budgets) caught them; the bypass removes the idle-wait at the root — the same WebDriverAgent-lineage approach Maestro uses. Probes both private selector variants (
waitForQuiescenceIncludingAnimationsIdle:and the Xcode-16:isPreEvent:form), swizzles exactly one (classic preferred), and degrades loudly (RN_FAST_RUNNER_QUIESCENCE_UNAVAILABLE) when Apple drifts the API — the runner keeps working without the bypass. Default ON; opt out withRN_QUIESCENCE_BYPASS=0(resolved at runner spawn; threaded asTEST_RUNNER_RN_QUIESCENCE_BYPASSbecause xcodebuild only forwardsTEST_RUNNER_-prefixed vars). Note:XCUIElement.typeTextruns its own internal sync, so the type-timeout shim remains as a safety net. Auditable viameta.quiescenceBypasson the first command after boot,QUIESCENCE_BYPASSin/health.capabilities, andcdp_status.deviceSession.runnerCapabilities. - 0cfa78a: The observe web UI now autostarts when the MCP worker boots in an RN project, listening on a
stable default port (7333,
http://127.0.0.1:7333) with an ephemeral fallback on collision. New.rn-agent/config.jsonblock{ "observe": { "autoStart": boolean, "port": number } }plusRN_AGENT_OBSERVE_AUTOSTARTenv override (precedence env > config > default, matchingcdp.autoConnect). Theobservetool gains arestartaction;stopis session-scoped. The live URL is recorded in a per-project state file and announced at SessionStart.
0.51.1
Patch Changes
- 3cf6787: fix(device_batch): testID steps failed with a misleading STALE_REF on the in-tree runners (#396).
findRefByTestIDpassed the envelope's ref through verbatim; the in-tree iOS/Android runners emit@-prefixed refs (@e68), so the testID branches ofdevice_batch(find+tap / press / fill) composed@@e68, which missed the ref-map (lookupRefstrips exactly one@) and surfaced asElement at ref @@e68 no longer hittable — UI re-rendered since snapshoteven though the snapshot was taken fresh that same step.findRefByTestIDnow returns the canonical bare id in both the flat-nodes and nested-tree envelope shapes, restoring the documented "re-resolve at execution time" contract; the GH #114 producer-consumer contract tests are updated to pin the bare-id contract for the in-tree producers.
0.51.0
Minor Changes
- 694a57d: feat(protocol): version the native runner /command wire protocol + move runner state out of /tmp (#383). Both runners'
GET /healthnow reports{protocolVersion, runnerVersion, capabilities}and every response carries a"v"stamp; the bridge classifies a reachable runner with a missing/older/newer protocol or a skewedrunnerVersionas stale and transparently reaps + reinstalls it (the first device tool call after upgrading from a pre-protocol plugin pays one runner restart —meta.note: "runner upgraded (protocol/version mismatch)"). Only a mismatch that survives reinstall surfaces the new typed errorRUNNER_PROTOCOL_MISMATCHwith exact rebuild commands. Runner state files move from fixed shared/tmppaths to per-device hardened files (0600, symlink-refusing, atomic) under the app-support state dir (runner-state/ios-<udid>.json,android-<serial>.json; Android persists only under a resolved serial) via a sharedutil/secure-state-file.tsalso adopted by the session file; a live pre-upgrade runner pointed at by the legacy/tmpstate is adopted once, reaped, and relaunched before the/tmpfiles are deleted, and a grep-enforced test keeps/tmpout of the runner clients.cdp_status→deviceSession.runnerProtocolsurfaces the handshake.
0.50.4
Patch Changes
- b1e0ad6: feat(keyboard-guard): in-runner keyboard-occlusion guard for live
device_press/device_longpresstaps on iOS + Android (#370). Before a guarded tap, the runner probes for a visible software keyboard whose frame contains the tap point (containment on a sane rect — non-empty, min height 120pt iOS / 150px Android, so accessory bars don't false-trigger) and auto-dismisses first when occluded. Android dismissal ispressBack+ a boundedwaitForIdle(1500)(≈3.6s measured incl. bounded idle), gated on a TYPE_INPUT_METHOD window with sane bounds so it never navigates back otherwise — requiresFLAG_RETRIEVE_INTERACTIVE_WINDOWS, now enabled at dispatcher init. iOS is verify-or-refuse: only the safe dismiss-control tap ("Hide keyboard"/"Dismiss keyboard"/"Done") is used, then re-verified; on iPhone standard QWERTY, which has no such control, the runner REFUSES the tap withKEYBOARD_OCCLUDED … keyboardGuard=dismiss_failedinstead of tapping the keyboard, because XCTest'sswipeDownon the keyboard triggers QuickPath slide-typing and corrupts the focused field (device-proven). Every guarded gesture returnsmeta.keyboardGuard:"off" | "no_keyboard" | "not_occluded" | "dismissed"(plusdismiss_failedinside the iOS refusal error). Opt out withRN_KEYBOARD_GUARD=0/false, resolved TS-side per command (guardKeyboardon the wire; absent → guard stays ON, so older clients keep guarding). Scope is command-handler tap/longPress only —tapSeries, by-text taps, element-center taps, the focus-tap inside type/fill, swipes/scrolls/drags, anddoubleTapare explicitly unguarded. Follow-up #379 tracks a JS-first (Keyboard.dismiss()) auto-heal for the iOS refusal case; #378 tracks a pre-existing Androidforeground()pre-flight stall surfaced (not fixed) during verification.
0.50.3
Patch Changes
- a6112e6: fix(record):
device_record stopno longer crashes on macOS withadb_args[@]: unbound variable(#374). Inrecord_proof.shthe Android stop branch expanded an emptyadb_argsarray unguarded ("${adb_args[@]}"); underset -euo pipefailon bash 3.2 (the macOS default/bin/bash) that is an unbound-variable error, aborting the stop before the pull/convert — so recording finalize (and, via a leftover Android.pid, even iOS stops) failed. All three expansions now use the+-default guard already present elsewhere in the file. Regression-guarded by a static invariant test (effective on bash 5.x CI) plus a behavioral reproduction gated to bash < 4.4.
0.50.2
Patch Changes
- 0a9a732: fix(interact): cdp_interact no longer corrupts react-hook-form Controller-wrapped inputs (#336).
setFieldValuekeeps a string a string for string-typed fields (a digit-string injected as a number is coerced back to string only when the field currently holds a string — number/boolean fields are untouched).pressgains an optionalvalue: when provided,onPressreceives the value instead of a synthetic event, so radio/chip-style controls whose onPress sets a form value select correctly. HELPERS_VERSION bumped to 33.
0.50.1
Patch Changes
- d61985f: fix(actions): inject
- hideKeyboardbefore button taps that follow text entry when generating/saving Maestro action flows, and route Android hideKeyboard replays to the official Maestro CLI (#356, Phase 1). Bottom-pinned taps (submit/continue) previously landed on the soft keyboard during replays — the single biggest source of flaky replays.generateMaestronow tracks soft-keyboard state and emits ahideKeyboardstep before atap/long_pressthat follows aninputText, reset on navigation.hideKeyboardis a no-op when no keyboard is showing and Maestro re-resolves the selector after dismiss, so the injection is safe. Device verification surfaced that maestro-runner v1.0.9 silently no-opshideKeyboardon Android (B223), somaestro_runnow prefers the official Maestro CLI for Android flows containinghideKeyboard(verified to dismiss the keyboard on-device), warning when the CLI is unavailable; iOS is unaffected (maestro-runner honors hideKeyboard there). Livedevice_*taps (the in-runner guard) and existing-corpus backfill are deferred to later phases.
0.50.0
Minor Changes
- 98d3fb7: Add an RNTL-style discovery resolver to the injected helpers.
resolveLadderfinds elements bybyRole(+name)/byText/byPlaceholder— ported from React Native Testing Library (matcher + normalizer, accessible-name, role, hidden, host-kind) — with fail-closed truncation and fail-closed multiplicity (never silently picks the wrong element), hidden-element exclusion by default, and a selector bundle (testID/text/accessibleName/role/placeholder/anchors).interact()routesrole/name/text/placeholderselectors through the ladder. Includes RNTLmatchDeepestOnlyso a composite+host fiber pair (e.g.Text+RCTText) resolves to a single on-device element instead of fail-closing as ambiguous.
0.49.0
Minor Changes
- 5fe66c9: Action corpus run/repair history now persists in a derived, gitignored node:sqlite store (.rn-agent/state/actions.db) alongside the per-action JSON sidecars (Phase 1 dual-write: sidecars stay authoritative, the DB is a rebuildable mirror), with graceful degradation to sidecar-only when node:sqlite is unavailable. The worker enables node:sqlite via a version-gated --experimental-sqlite flag (Node 22.5–23.5); the engines floor stays >=22. cdp_status now reports the active backend as
actionStore. The learned-actions inventory script is migrated from JavaScript to TypeScript (compiled to dist/).
0.48.0
Minor Changes
- d3be838: #317 Phase 2: when an action fails on iOS 26.x because WebDriverAgent is blind (empty accessibility tree),
cdp_run_actionnow replays the action's id-based steps through the CDP/JS transport and returns a real pass/fail verdict — restoring action replay (and the observe Regression Run button) on iOS 26.x. The fallback fires on both observed blind failure modes —SELECTOR_NOT_FOUND(probe = the failed selector) andUNKNOWN/WDA-died-at-launch (probe = the action's first testID) — guarded by an exact-match CDP-tree oracle so genuine drift still routes to repair. Fallback verdicts are labeledtransport:'cdp-js'(handler-level semantics) and failed replays recordfailureCode:'TRANSPORT_BLIND'; unsupported step types (e.g. text-based selectors) fail loudly rather than passing silently.
0.47.2
Patch Changes
- 8cf8d4e: Fix the observe Regression tab's per-action Run button doing nothing. The observe
runActionwiring resolved the correct project root forloadActionbut then called the innerrunActionHandler(cdp_run_action) without passingprojectRoot, so the runner re-derived it fromprocess.cwd()(the plugin repo) and failed instantly withNO_PROJECT_ROOTbefore ever reaching the device. The resolved root is now threaded intorunActionHandler, so a clicked action runs its Maestro flow on the connected app's project. (Follow-up to #348, which fixed the same root-resolution family for the actions list and suite.)
0.47.1
Patch Changes
- 6dc02a8: Fix the observe Regression tab showing an empty actions list and "Run E2E Suite" always reporting PASS. The observe e2e surface now resolves the project root of the connected app by its bundleId (
findProjectRoot({ bundleId })), so a stray sibling React Native repo can no longer hijack the heuristic filesystem scan and point the actions list / locked-test discovery at the wrong project. A suite that discovers zero locked tests now reports a distinctemptyverdict ("NO TESTS") instead of a false-green pass.
0.47.0
Minor Changes
- 33db4be: feat(e2e): Actions panel in the observe page — list the project's actions and run any one (repairable
cdp_run_action) with params resolved from.rn-agent/e2e.config.json, viaGET /api/e2e/actions+POST /api/e2e/actions/run. - 042280b: feat(e2e): params source —
.rn-agent/e2e.config.jsonsupplies per-test param values (with shareddefaults+ secret redaction) so parameterized actions can be locked and run as e2e tests.cdp_lock_e2e_testnow accepts a param-needing action when the config covers all its params (elseMISSING_PARAMSlisting the gaps);cdp_run_e2e_suiteruns param tests with their resolved values (else skips with a clear reason). Secret param values (names insecretParams) are redacted to***in failure output and run records, and only an action's declared params are passed to Maestro (unrelated defaults never leak). - 33db4be: feat(e2e): observe Regression page + CSRF-guarded control endpoint — a top-level Live|Regression toggle with a Run button, live progress, verdict badge, per-test table, and run history, backed by
POST /api/e2e/run+GET /api/e2e/runs[/:id](host + Sec-Fetch + CSRF + method/content-type guarded; one flow lease).
0.46.0
Minor Changes
- 8f0b7ff: feat(e2e): regression runner engine —
cdp_lock_e2e_testpromotes a verified (param-free) action into a frozen, executable locked e2e test, andcdp_run_e2e_suiteruns all locked tests strict (no auto-repair) on the booted sim, persisting a suite-run report with verdict, per-test classification (regression vs infra, params skipped), and a newly-failing-since-last-green diff. Engine only; observe page + CSRF HTTP trigger land in a follow-up.
0.45.8
Patch Changes
- 7731024: chore: adopt oxlint + oxfmt as the lint/format layer, format the codebase (code only — prose docs excluded), and add a blocking CI lint-format gate.
0.45.7
Patch Changes
-
8305bbd:
maestro_runnow returns structured per-step results and partial progress on timeout (GH #211).The result gains
steps[]({index,name,verb,status,durationMs}),failedStep,reason(sanitized{kind,selector}— never the raw runner log),lastStep(progress marker),timedOut, andoutputTruncated. On timeout the partial steps are returned instead of a bare failure, and the failure headline names the failing/last step. Parsed from maestro-runner stdout (the JVM Maestro CLI fallback degrades fail-open to empty steps);tapOnlatencies for #263 now derive from the shared parser. Additive —outputis preserved forrun-actionconsumers.
0.45.6
Patch Changes
-
16f0a0d:
maestro_runnow flags a wedged simulator runtime (GH #263).When a flow fails AND the median latency of its successful
tapOnsteps exceeds a floor (default 1500ms,RN_RUNTIME_DEGRADED_FLOOR_MS), the result gains aRUNTIME_DEGRADEDhint andmeta.runtimeDegraded— "the simulator test runtime is likely wedged; reboot it (xcrun simctl shutdown/boot), relaunch, and retry." This replaces the misleading "Element not found" that previously sent the agent chasing app code when the real cause was a degraded simulator (taps reported success butonPressnever fired). Detection is purely additive — it never changes a pass/fail verdict, never fires on a passing run, and only counts successful taps (a failed tap's duration is the step timeout, which would otherwise false-positive an ordinary element-not-found failure). Fail-open: unparseable output → no hint.
0.45.5
Patch Changes
-
6c77108:
/observedevice panels now refresh live (GH #206).The observability layer was a passive recorder of tool observations — the screenshot only updated on
device_screenshotcalls and the route only on navigation-family tools, so driving the app withcdp_interact/cdp_navigateleft both panels stale. A fire-and-forget hook now captures a fresh screenshot (simctl/adb, OS-level) + route (CDP nav-state) after each state-mutating tool and delivers them via a dedicated live SSE channel ({type:'live'}+/api/live-screenshot), so the timeline stays clean. Platform resolves from the active device session or the connected CDP target (so a purely CDP-driven flow with no agent-device session still refreshes). Gated on a connected/observetab, skipped during Maestro flows, single-flight trailing-coalesce, opt-out withRN_OBSERVE_LIVE=0.
0.45.4
Patch Changes
-
64531c8: Bump esbuild to 0.28.1 across the build toolchains to clear the HIGH Dependabot advisory (GHSA-gv7w-rqvm-qjhr).
The advisory is in esbuild's Deno installer (binary-integrity RCE via
NPM_CONFIG_REGISTRY) — a code path this repo never executes (esbuild is consumed as an npm transitive dep via Vite/Astro, not Deno), so it was never exploitable here. Still, both the observability web UI (scripts/cdp-bridge/src/observability/web/) and the docs site carried the vulnerable transitive esbuild, so both now pin it to the patched 0.28.1 via an npmoverrides. The observability Vite build also setsbuild.target: 'esnext'(it's an internal localhost-only dev tool viewed in a modern browser) to sidestep an esbuild 0.28 regression that refused to downlevel destructuring to Vite's default old-browser baseline; the single-file bundle was rebuilt.npm auditis clean in both subtrees.
0.45.3
Patch Changes
-
a88d139:
cdp_network_logno longer returns two entries per request (GH #214).Root cause: setup sends
Network.enable(modecdp), thenprobeNetworkDomainfires a probe fetch and watches the buffer. On RN ≥ 0.83 the CDP Network domain does deliver events, but when they don't flush within the probe window — a false negative documented after platform switches / reloads (GH #59 #9) — the probe returnsnoneand setup injects the fetch/XHR hook without disabling the still-enabled Network domain. Both paths then capture every request (CDP numeric-id entries + hook UUID-id entries), and the existing exact-id dedup can't collapse them because the two id schemes never collide.Fix: when setup falls back to the hook, it now disables the CDP Network domain first, so the hook is the single capture source. This also makes
cdp_status'snetworkDomain: falsetruthful instead of a label over a still-running domain — the "capability flag out of sync" symptom in the report was the same root cause. Read-time fuzzy dedup was deliberately rejected: it would collapse legitimately-identical rapid requests (a real double-mutation) and hide bugs — the opposite of what the reporter needed.
0.45.2
Patch Changes
-
0386204:
cdp_mmkvdelete and boolean reads now work on the Nitro react-native-mmkv line (GH #209).deletewas callingmmkv.delete(key)— a JS-wrapper-class method that doesn't exist on the raw Nitro hybrid object the tool actually talks to (createHybridObject('MMKVFactory').createMMKV(...)), whose spec exposesremove(key). The generated expression now prefersremove(), falls back todelete()for wrapper-shaped objects, and reports a named error (instead of a bare TypeError) when neither exists. This unblocks first-class auth/storage resets for logged-out replays on iOS — previously a rawcdp_evaluateescape hatch every time.getwithtype: 'boolean'emittedmmkv.getBool(key), which exists on no MMKV surface (hybrid object and wrapper both spell itgetBoolean) — broken since the tool shipped. Now fixed.- The follow-up enhancement from the issue (a
clearKeys:action-YAML directive for self-contained auth-gated replays) is tracked as GH #286.
0.45.1
Patch Changes
-
bd5d585: Recovery paths now detect "app not installed" and resolve their relaunch target truthfully (GH #262, absorbs #194 BUG 2).
cdp_statusAPP_DETACHED auto-relaunch: whensimctl launchfails ANDget_app_container's stderr carries theNSPOSIXErrorDomain code=2marker (allowlist-only, stderr-only — argv-spoof-proof), the tool returns a distinctAPP_NOT_INSTALLEDcode with install advice — including a shell-quotedsimctl installline for the newest matching.appsnapshot from the last clearState (GH #201 dir, mtime-sorted budgeted scan). Ambiguous probe verdicts fail open to the existingAPP_DETACHEDbehavior. Concurrent recoveries are serialized, and a confirmed missing bundle is cached (with a cheap re-probe) so the diagnosis is never masked bybudget-exhausted.cdp_restart hardReset=true: the relaunch target resolves throughexplicit arg > connectedTarget > cache > active-session appId > strict per-platform app.json(no iOS←Android fallback), simctl targets the active session's UDID when one exists, failed launches are classified the same way inhardResetSteps, and a successful hard reset resets the detached-recovery budget.
-
81c386a:
device_screenshotno longer blames "device transitioning state" when the target directory doesn't exist (GH #265).captureAndResizeScreenshotnowmkdir -p's the parent of the derived output path before any dispatch tier runs (simctl raw, rn-fast-runner, agent-device daemon/CLI, adb stream) — new directories are the expected case, since the tool's own advisories steer agents toward freshdocs/proof/<slug>/paths. The fix coversdevice_screenshot,device_batchauto-captures, andproof_step, all of which funnel through the same helper.- When the directory itself cannot be created (e.g. a file blocks an intermediate path segment), the tool short-circuits before probing any device and returns an honest
SCREENSHOT_FAILEDwithreason: 'target-dir-unavailable'naming the offending path — never the device-state guess. - A leading
~/in the screenshot path is now expanded to the real home directory (Node never expands~, so mkdir would otherwise create a literal./~/under the bridge cwd and report success into the wrong location). Unexpandable forms (~user/..., bare~) are refused with an actionable error.
0.45.0
Minor Changes
-
eff45cd: #202 Phase 6 / #186 — foreign Maestro sessions become arbiter refusals; plugin maestro_run is the canonical surface.
While a foreign Maestro/XCUITest session drives the target simulator (UDID-scoped detection, 5 s TTL, fail-open), local
device_*and flow tools refuse fast withBUSY_FOREIGN_FLOW(~50 ms measured) — pointing at the safe L1 reads — instead of colliding into the ~44 s runner-leak cascade. L1 introspection stays free;device_screenshotserves pixels via its simctl fallback; a ~10 s teardown grace after the plugin's own flows prevents self-false-positives while WDA dies. The two historical reasons to leave the plugin surface are live-gate-verified closed and #201 is closed — including a new fix: the clearState--app-fileresolution is snapshotted outside the device container (the installed-container path used to be deleted by clearState itself before the reinstall could read it).RN_IOS_FOREIGN_GUARD=0disables both the warning and the refusal (RN_IOS_FOREIGN_WARN=0remains a deprecated alias). The foreign-runnerpsscan now uses-ww(command-column truncation could silently drop the UDID → false negatives).
0.44.0
Minor Changes
-
c05c058: #202 Phase 5 / #264 — the bridge now survives Metro restarts (supervisor split).
The MCP entry point is now
dist/supervisor.js: a thin stdio shim holding zero network sockets (immune tolsof -ti tcp:8081 | xargs kill -9, which used to SIGKILL the whole server and cost the session all 77 tools). It spawns the real bridge as a worker, and on worker death: errors in-flight calls with-32000("retry the call"), respawns it (max 3 per rolling 60 s, then a terminal crash-loop error), and replays the cached MCPinitializehandshake so the session continues seamlessly. Visibility:cdp_status→bridge: { supervised, workerRestarts, lastWorkerExit }. Opt out withRN_BRIDGE_SUPERVISOR=0(legacy single process).SIGUSR2now performs a real hot-reload (worker restart + handshake replay).
0.43.0
Minor Changes
-
abe4411: Expose
paramsin themaestro_runandcdp_run_actionMCP tool schemas.Both handlers have accepted
paramssince GH #116 (forwarded to maestro as-e KEY=VALUEon the first attempt AND the post-repair retry), but the zod registrations omitted the field — and zod strips unknown keys by default, so a caller's parameter bindings were silently dropped at the tool-call layer and a parameterised action failed at runtime with unset${VAR}placeholders. Found by Codex review on PR #272 (the newcreating-actionsskill recommendscdp_run_action({ actionId, params, trigger }), which was un-callable as advertised;commands/run-action.mddocumented the same call shape). Key-format validation (/^[A-Z_][A-Z0-9_]*$/) stays in the handler. Wiring test pins both registrations.
0.42.0
Minor Changes
-
73c6bf4: #202 Phase 4 — eradicate legacy runner apps, not just processes.
At iOS device-open,
ensureSingleRunnernow detects the legacy upstream runner apps installed on the target simulator (com.callstack.agentdevice.runner+.uitests.xctrunner) andsimctl uninstalls them. Killing the host processes (Phase 1) was insufficient: iOS relaunches an installed XCUITest runner into the foreground mid-maestro_run, backgrounding the app under test and wedging CDP. Scanned at every device-open (onesimctl listapps, ~150–350 ms measured — no memo, so a reinstall by another session is always caught); error-safe (warnings, never a blocked session); opt out withRN_DEVICE_KILL_LEGACY=0. Results surface asremovedApps+meta.timings_ms.appEradication.
0.41.0
Minor Changes
-
58c4886: Debugger-seat coexistence with React Native DevTools + silent hook-mode network capture.
- New opt-out for background auto-reconnect:
RN_CDP_AUTOCONNECT=0or.rn-agent/config.json{ "cdp": { "autoConnect": false } }. In passive mode the bridge yields the single RN debugger seat to the visual DevTools and reconnects only on explicit tool calls. Resolved mode is visible incdp_status→autoConnectand/doctor. - Hook-mode network capture (RN < 0.83 fallback) no longer transports entries via
console.log("__RN_NET__:…")— entries go to an in-app ring buffer drained on demand, so Metro logs and the user's DevTools console stay clean.
- New opt-out for background auto-reconnect:
0.40.5
Patch Changes
- 6190178: fix(#253):
cdp_repair_actionno longer hardcodestargetPlatform='ios'— Android auto-repair works against an emulator. The repair orchestrator now derives the platform from the active device session viadetectPlatform()(booted-device probe fallback when no session is open;'ios'only as the final no-session, no-device fallback). Previously an Android repair foregrounded the app viaxcrun simctl, snapshotted through the iOS short-circuit, and bootstrapped the iOS fast-runner — so Android selector drift always escalated as a hard failure instead of self-healing.
0.40.4
Patch Changes
- e5404ed: fix(#249): Maestro pass detection no longer flips passing flows to failed when app logs contain the substring
FAILED. The exit-0 secondary guard inmaestro_run,maestro_test_all, and the inline maestro fallback used a bareoutput.includes('FAILED')over combined stdout+stderr — app/console output like aFETCH_FAILEDRedux action or aLOGIN_FAILEDanalytics event marked a genuinely passing flow as failed and triggered pointless auto-repair. All three call sites now shareoutputIndicatesFlowFailure, which keys on Maestro's own terminal status lines (Test FAILED/Flow FAILED/ a[FAILED]step marker / a bareFAILEDline) instead of a substring. - 070586d: fix(#250):
cdp_interactno longer reports success when the app's own handler throws. The injected interact dispatch caught handler exceptions (onPress/onChangeText/setValueraising — unmounted component, missing context, thrown validation) and returnedsuccess: true, action_executed: true, which the tool layer surfaced as a non-error warning — so agents proceeded against a screen that may be in an error state. The helper now reportssuccess: false(keepingaction_executed: trueto distinguish "dispatched but handler threw" from "couldn't dispatch"), and the tool layer maps it to a structured error withmeta.actionExecuted,meta.handlerError, and a check-cdp_error_loghint. HELPERS_VERSION bumped to 25 so connected sessions re-inject. - 8269476: fix(#251,#252): startup hardening. The project single-instance lock (
Lockfile.acquire) now uses the same atomicopenSync('wx')exclusive-create pattern asDeviceLock— the previous read-then-write let two bridges starting in the same instant both "acquire" the lock, with the second silently truncating the first; the loser now gets a structured conflict, stale-holder reclaim narrows the steal window with a re-read before unlink, and fs infra errors fail open (degraded: true) instead of crashing the bridge at boot. Separately, SessionStart is now bounded: the hook declares an explicit 120s timeout and the maestro-runner installer'scurl | bashcarries--connect-timeout 10 --max-time 90, so a stalled CDN can no longer block session start indefinitely; a CI guard (session-start-bounded.test.sh) pins both.
0.40.3
Patch Changes
- 609c825: fix(B191,B192): post-flow lifecycle hardening follow-ups to #243/#244.
isAndroidConnectionFailurenow also classifiesstartAndroidRunner's startup-failure shapes (exited before readiness,Failed to spawn Android runner instrumentation) into the structured retryableRN_ANDROID_RUNNER_DOWNinstead of letting a startup crash escape as a raw exception. AndisBenignSessionGoneErrorno longer runs its session-gone regex over unparseable (non-JSON) close payloads — with no error field to scope the match to, they surface unchanged, so a real close failure whose raw text merely mentions "no active session" can't be silently swallowed.
0.40.2
Patch Changes
- c9d447d: fix(#243,#244): Android post-flow lifecycle.
rn-android-runnerreadiness is now gated on its ownGET /healthinstead of theadb logcatring buffer — a prior runner's stale ready line (same tag + fixed port) used to fire readiness before the new socket bound, so the firstdevice_*after a Maestro flow returned a barefetch failed. When the runner genuinely can't come up,runAndroidnow surfaces a structuredRN_ANDROID_RUNNER_DOWNwith a retry hint. Separately,device_snapshot action=closenow tolerates an underlying session that a flow already tore down (the #237 slot-release): it cleans up local state and returns ok, soopen → flow → closeround-trips cleanly instead of erroringSESSION_NOT_FOUND.
0.40.1
Patch Changes
- 51976e8: fix(#237): Android instrumentation-slot handoff —
runFlowParkednow releases the single AndroidUiAutomationslot before a Maestro flow (maestro_run/maestro_test_all/cdp_auto_login), fixingUIAutomator2 server not ready after 30s. It stops the in-treern-android-runner,am force-stops our two instrumentation packages (the decisive device-side release), and — gated byRN_DEVICE_KILL_LEGACY— kills a stale legacyagent-devicedaemon by its specific PID (neverpkill, guarded against our own process tree so the MCP server is never collateral). Best-effort and idempotent; iOS behavior is unchanged.
0.40.0
Minor Changes
- de6a8d8: fix(#191): JS-first text entry —
device_fillnow prefers the deterministic ReactonChangeTextpath when CDP is connected and the ref resolves to a testID (via its cached snapshot identifier), settle-polls the field value to verify it (defeating the debounced-onChangeTextread race), and on the native fallback runs a bounded clear+retype (realclearFirst+ per-character delay) when the value is corrupted, escalating to a verified maestro fallback before erroring. Adds best-effort iOS predictive-keyboard suppression at session-open and a newTEXT_ENTRY_UNVERIFIEDerror code for the exhausted-and-still-corrupted case. Additivemetaonly (textEntryPath,verify,timings_ms); no breaking change for existing callers. NOTE:device_batchfills are not yet JS-first (they call the runner directly) — tracked as a follow-up.
0.39.4
Patch Changes
-
72d17b5: Fix #210: iOS device-session visibility + self-healing.
cdp_statusnow reportsdeviceSession: { sessionOpen, rnFastRunner: 'alive'|'stale'|'dead', appId?, deviceId?, foreignRunner? }so the agent can see the XCUITest runner state before callingdevice_*(iOS-gated — Android leavesrnFastRunner:'dead'and skips the probe/scan).device_find/press/fillnow auto-spawn the runner from the dispatch choke point when a session or booted simulator exists and the rig is prebuilt — cold-build-safe: a missing prebuilt rig returns an actionableRN_FAST_RUNNER_DOWNerror namingdevice_snapshot action=openinstead of a silent multi-minutexcodebuild.device_screenshotnow falls back toxcrun simctl io screenshot(oradb) whenever the runner can't serve it — including while a Maestro flow owns the device — so it never hard-fails on iOS. Also fixes a latent bug where an omitted-platformdevice_snapshot action=openstoredplatform: undefined, skipping the iOS dispatch branch.Reframes the issue's "ride Maestro's WDA" suggestion (rejected: WDA is per-flow/ephemeral with no session to ride, and a WDA client would add a second XCUITest backend rather than unify; mid-flow pixels use simctl, mid-flow state uses CDP introspection). (GH #210, B186, D1249)
0.39.3
Patch Changes
-
75a9573: Fix #182: the CDP MCP no longer fails with
-32000: Connection closedwhen an orphaned bridge from a dead Claude Code session holds the single-instance lock.Root cause: when CC dies abnormally (SIGKILL/crash/window-close on macOS) without closing the child's stdin or signaling it, the bridge becomes a live orphan — still running, still holding the project lock. The existing reclaim (PID-dead / mtime>24h / process-name) can't recover a live owner, so the next session hard-failed for up to 24h. Four composing fixes:
- Parent-death self-exit (prevent). A
getppid()poll (lifecycle/parent-watch.ts) captures the bridge's PPID at startup and self-exits (releasing the lock) when it changes — i.e. the original Claude Code host died and the bridge was reparented. This catches the abnormal-death cases stdin-EOF + signal handlers miss. It compares against the startup PPID rather than testing=== 1so a bridge whose host runs as PID 1 (a container with no init system) is never falsely killed. - Orphaned-owner reclaim (recover).
Lockfile.isLockLivereclaims a live owner whose parent changed from the PPID it recorded at acquire (ps -o ppid=) — so a new session self-heals past an existing orphan instead of hard-failing. A null PPID lookup fails safe; pre-0.39 locks with no recordedppidfall back to a legacyPPID===1reclaim. - Heartbeat (recover wedged). The lock body carries
lastHeartbeat, refreshed every ~10s; a live owner whose heartbeat goes stale (>90s) is wedged and reclaimable — mirroring the device-lock's self-healing. Pre-0.39 locks withoutlastHeartbeatfall back to the existing mtime check (back-compat). - Usurp self-terminate (sleep/wake safety).
Lockfile.touch()now returns whether we still own the lock. If a contender reclaimed our slot while the laptop slept (heartbeat expired → reclaimed → we wake), the next heartbeat detects the foreign PID and self-terminates instead of running as a second bridge on the same device. This also makes the (pre-existing, non-atomic) reclaim path self-correcting within one tick.
Together these eliminate the manual
kill <pid> && rm <lock>workaround. 15 #182 unit tests (incl. container-safety, sleep/wake usurp, and a realps -o ppid=check); unit suite 1744/1744;tscclean. (GH #182, B185, D1246) - Parent-death self-exit (prevent). A
0.39.2
Patch Changes
-
bc577e9: Fix the CDP connection wedge (GH #208):
cdp_statusno longer dead-locks on "Already connecting to Metro..." and no longer misreports a detached app as "Metro not found". Three root causes were addressed:- RC1 — reconnect-storm wedge. When the app detaches but Metro stays up, the WS-close reconnect loop holds
isReconnecting()true for up to ~12 min (30 attempts × 30s cap, then re-armed indefinitely by the background poll).autoConnect's guard threw "Already connecting" for everycdp_status/cdp_*call in that window.cdp_statusnow preempts an active reconnect storm viasoftReconnect()(the existing 3ssoftReconnectRequestedhandshake) for one fresh attempt instead of refusing, and surfaces the livereconnectState(attempt N/30) on any connect failure so it reads as progress, not a dead end. - RC2 — misleading error. "Metro up but 0 Hermes targets" now throws a typed
AppDetachedError("Metro is up … advertises 0 Hermes debug targets — the app isn't attached") instead of being conflated with the genuine "Metro not found" (now reserved fordiscoverMetroPortreturning null). - RC3 — no auto-recovery. New
recoverDetached()cold-restarts a detached iOS app (simctl terminate+launch) → reconnects → confirms with a real CDP liveness probe. Bounded to 3 consecutive attempts/session, skips while a Maestro flow holds the arbiter lease, iOS-only, opt-out viaRN_AUTO_RELAUNCH_ON_DETACH=0. Cold-restart (vs recover-wedge's bare launch) is acceptable because it only fires when the app is ALREADY detached — never against a working app.
Hardened via a Codex + Gemini multi-review:
cdp_statusnow honors an explicitargs.platformduring a storm (tears down + reconnects rather than reusing the storm's target), auto-relaunch is skipped when the caller pinned a non-iOS platform (never cold-restarts an unrelated iOS session),simctl launchfailures are surfaced instead of hidden behind "still detached", and the post-recovery status read can no longer throw out of the handler.19 new unit tests; full suite 1729/1729;
tsc --noEmitclean. Live false-positive guard verified: the realdiscover()against Metro does not fireAppDetachedErrorwhile a target is present. Scoping: the literal-0-targets case is fixed; the RN-0.85 "C++ target present, 0 Hermes" flavor remains B156/B184 territory (recover-wedge path). (GH #208, B181, D1245) - RC1 — reconnect-storm wedge. When the app detaches but Metro stays up, the WS-close reconnect loop holds
0.39.1
Patch Changes
-
6e8af52: Fix a batch of bugs, regressions, and reliability issues surfaced by a multi-agent repo audit.
Security
- Redaction no longer leaks private-key material.
redactStringnow applies secret patterns BEFORE truncating (a >2000-char PEM previously had its-----END-----marker severed by truncation so the key body passed through), and the PEM rule now matches multi-word labels likeRSA PRIVATE KEY/OPENSSH PRIVATE KEY(the old single-word pattern never matched the most common headers).
Device interaction
device_scrollno longer throws on Android (and on the iOS fast-runner fallback): a direction-form scroll is now converted to coordinates before dispatch, matchingdevice_swipe.device_batchscroll steps no longer crash the whole batch on either platform (same root cause).- A coordinate
device_swipewith--count/--patternbut nodurationMsno longer mis-parses the flag value as a 3 ms duration on iOS (the positional extractor now strips flag values, matching Android). - The Android runner is no longer reused across emulators:
shouldReuseAndroidRunnerchecks the bounddeviceId(parity with iOSshouldReuseRunner), so a runner bound to one emulator can't silently drive another. - A wedged-but-alive fast-runner is now reaped:
ensureFastRunnerprobes tri-state liveness instead of PID-only, so a hung HTTP listener no longer makes every subsequent command burn the full timeout. ensureSingleRunneris now awaited at session-open so the stale-runner kill completes before the first interaction, and itspsfailure surfaces as a warning instead of a silent no-op.
Actions / Maestro
- Actions now auto-promote
experimental → activeon the first clean replay (the documented lifecycle was defined + tested but never wired). - The GH#186 route-drift guard is now active in production (
cdp_run_actionis wired with a CDP-backed live-route reader; it previously defaulted to a no-op). maestro_test_alland the inline Maestro fallback no longer mark passing flows as failed when app/console output merely containsError:, and both now auto-resolve--app-filefor iOSclearStateflows (previously onlymaestro_rundid).clearStatedetection also recognises the standalone- clearStatecommand.- All Maestro
execFilecalls raisemaxBufferto 10 MB so a large flow log can't kill the child and mask a passing run. cdp_repair_actionRUNNER_LEAKrefusals are now bucketed asSNAPSHOT_FAILEDin MTTR telemetry instead ofINTERNAL_ERROR.- A bare-form
id:repair now emits a quoted scalar, so a testID containing YAML-special characters can't corrupt the action.
Reliability / correctness
collect_logsno longer double-shifts Android logcat timestamps by the host UTC offset (which corrupted both the time and the cross-source merge order).- CDP freshness/dev probes attach a no-op catch to the raced
evaluate()promise so a mid-probe WebSocket close can't surface as an unhandledRejection. - The observability server keeps a small
headersTimeout(slow-loris guard), broadcasts ashutdownevent so the browser stops auto-reconnecting after stop, andRecorder.clear()notifies subscribers instead of orphaning live SSE streams. - Action IDs now accept dots (
v2.0-login) per their documented contract while still rejecting... - The post-edit health-check hook's "app not installed → skip" guard works again (
grep -c || echo "0"produced a two-line0\n0). learned-actionsresolves the project memory dir correctly for paths containing a dot, and its${VAR}extractor accepts digit-bearing keys.- The injected-helpers version is a single source of truth (the post-injection log no longer reports a stale
v11). sync-versions.shdrops a dead, misleading variable and documents thatrn-dev-agent-cdpis independently versioned.
Hardened the previously flaky
proof_stepunit tests (they depended on a machine-global session file) with a dependency-injection seam, making the suite deterministic. - Redaction no longer leaks private-key material.
0.39.0
Minor Changes
- 5c4ca04: Add the read-only observability UI (D1226 "watch the agent live"): an in-process recorder + opt-in SSE server serving a React SPA (timeline | device | state). New
observeMCP tool +/rn-dev-agent:observeslash command. Deep-redacted (args + payload, fail-closed), localhost-only with Host-header + Sec-Fetch-Site guards.
Patch Changes
- c4804dc: Add
cdp_dismiss_dev_client_pickerMCP tool (Android) and best-effort Dev Client picker dismissal after Android deep links (#136 sub-3). Routed through a single guardedclearDevClientPickerIfPresent()helper; iOS returns an actionable manual-select message instead of touching the legacy agent-device path. Cross-platform iOS support tracked as a follow-up.
0.38.40
Patch Changes
-
Deliver the GH #186 maestro-interop fixes that merged in #188 without a version bump (closes #189).
cdp_run_actionnow allowsrunFlow(includingwhen:conditionals and{file}sub-flows) through the Maestro command allowlist, so actions with conditional dialog-handling (Expo dev-server picker, iOS "Open in" dialog) replay through the canonical runner instead of hard-failing withCommand not in allowlist: runFlow (Phase 134.1).- Non-destructive runner-leak
reacquirerecovery tier + cross-tool CDP re-pin, avoiding the ~44s relaunch / ~47s STALE_TARGET when maestro-mcp and rn-dev-agent contend for the same iOS device. - Structural route-drift detection: a stale-selector failure on an inserted screen is classified
ROUTE_DRIFTinstead of triggering a wasted fuzzy-repair.
#188 shipped these to
mainwith no version bump, leaving them undeliverable to marketplace installs; this patch publishes them.