fix(ios): prepare shared synthesized input without contacts - #2362
fix(ios): prepare shared synthesized input without contacts#2362thiagobrez wants to merge 6 commits into
Conversation
Size Report
Startup median (7 runs, lower is better):
|
CI validation (draft run)All code-quality gates pass: Lint & Format, Typecheck & Package, Repo Guards, Coverage, Compatibility & Provenance, CodeQL, Bundle Size, and the iOS/macOS/Linux smoke lanes. The iOS lane exercised the change end to end on a simulator, all green:
On this warm CI simulator the one-time attach is cheap (433 ms), so the warm-up is a near-no-op — which is the intended behavior on a warm host. The cold-boot case it targets is not reproducible on an already-booted smoke simulator; that is what the external reproduction branch demonstrates the defect on, and what a maintainer cold-boot device run (this lane on The two red checks are pre-existing flakes unrelated to this iOS-only change and both re-run: Android Smoke ( |
|
Update: CI Integration passed on re-run (it was a flake). Android Smoke is a pre-existing repo-wide failure, not this PR: the same |
|
The warm-up at 5d4fd44 sends a real tap to the top of the app window. That point is not guaranteed to be inert in a status-bar-hidden or edge-to-edge app, so it can activate content before the requested gesture. Use a non-delivering warm-up and prove that it cannot change app state. It also runs only for gesture, while scroll and synthesized drag reach the same timed-input pipeline without it. Put the once-only preparation at the shared input boundary and test ordering across those routes. The reported warm CI run does not establish that the cold-start failure is fixed; a cold-start red/green run is still needed. |
|
Reviewed 5d4fd44. Agreeing with @thymikee on the two structural points, plus four things in the code itself. 1. The warm-up point is computed in one frame and delivered in another. 2. The warm-up adds AX and a screenshot ahead of the gesture it exists to speed up. 3. Non-delivering warm-up, concretely. The status-bar band is not inert even when the tap does reach the status bar: a status-bar tap triggers 4. Policy mismatch, and the flag is set before the attempt. The warm-up runs under On the shared boundary (@thymikee's second point) — the three synthesis entry points are On evidence: the three unit tests are Generated by Claude Code |
|
Addressed the structural feedback in 1cf7e5f:
I also restored Agent Device's iOS 26 pointer route in thiagobrez/react-native-reorderable#101, preserving the simplified candidate pipeline. Its workflows now accept an immutable upstream commit for source-build validation. Cold-start evidence is still in progress. The first local cold boot with this build passed (empty preparation took 5.2 s; all three drag results were prompt), but the local released baseline did not reproduce a silent drop. I have corrected the PR body to avoid claiming the causal diagnosis is established and am running the hosted baseline/source comparison before claiming resolution. |
|
The shared contact-free preparation at 444cfeb addresses the earlier structural findings, and the reported red/green bridge test covers ordering and retry. Coverage now fails because nightly skips this regression without running it separately. Add the isolated test invocation to nightly too, preserving the selection guarantees. Comparable cold-start baseline/candidate evidence is still pending, so this is not merge-ready yet. |
|
One correction to the original diagnosis from the local cold-run trace: the Recap digitizer is attached/detached per real gesture, rather than remaining attached for the whole runner process. In the revised run: There is no attachment during that empty preparation interval. This does not rule out preparation of other shared XCTest state, but it does rule out treating these logs as proof of the original persistent-digitizer explanation. Two valid local cold boots passed, but the source-baseline/fixed hosted comparison is still needed to establish whether this prevents the failure instead of just adding delay. Keeping the causal claim explicitly unproven. |
|
Addressed the nightly coverage gap in 211f232. Nightly now runs the preparation regression in its own test invocation, then the remaining suite. It merges both xcresult bundles before the existing source-derived executed-count assertion. The selection guard now unions separate invocations while respecting skips within each invocation; regressions cover removing the isolated run and conflicting flags in one command. No coverage invariant was waived. The 40 selection/summary tests and |
|
The nightly merge was verified locally: the isolated regression plus remaining suite merge to The first hosted candidate cold sample is not green: job 101549746436 has no usable first-gesture measurement. Boot 1 hit a 5 s toolchain-query timeout during prepare; boot 2 rejected both gestures before synthesis because viewport was 134 × 291.33 while selector points were in the 402 × 874 coordinate space; boot 3 failed selector resolution. Boot 2's post-relaunch gesture did eventually commit, with a 13.6 s post-command observation wait and a 10.45 s empty preparation. This does not establish the empty preparation solves cold input. The exact-source baseline and second candidate sample are still running. Downstream iOS 27 fallback passed, while the iOS 26/native contract lanes are still pending. I am examining the pre-synthesis blockers without folding them into the warm-up claim. |
|
Two further corrections from the recordings/logs:
Neither correction turns the hosted candidate red into green. The iOS 27 fallback and native downstream contracts passed; iOS 26 and the remaining cold runs are pending. |
|
The nightly selection fix looks good at 211f232. It runs the isolated regression and retains the full-suite count check; the reported local merged result contains all 226 passing tests. The remaining blocker is comparable cold-start evidence: the first hosted candidate sample does not establish that preparation fixes the original issue. Keep this draft until that comparison is usable. Android smoke is still red and iOS smoke is pending; the changed nightly workflow also needs a hosted run. |
|
The requested hosted nightly is running at 211f232: 34060558728. The iOS smoke retry has passed fixture compilation and the targeted XCTest regressions and is progressing through replays. Android main is also red (at a different smoke assertion); I started one unchanged Android rerun and am retaining the failures. All four downstream device lanes, consumer/runtime checks, and parity passed in 34056536337. iOS 26 used its existing one whole-job retry after the first attempt failed the pre-drag scenario wait. The optional final npm dry-run then failed on the already-published 1.0.0 version. A fresh complete matrix is running at the latest source SHA through the existing validation-only channel: 34060592647. Cold comparison is still not usable as a green claim. Exact base bd08e6 produced two unobserved first drops and one delayed observation. Candidate sample 2 had one prompt first observation, one delayed observation, and one falsely classified “lost”: its recording clearly shows the committed drop, while the observer failed with an invalid viewport. The harness now separates observer errors from established target absence and rejects failed alert setup. Fresh same-harness runs are underway: base, candidate. Keeping the PR draft as requested. |
|
The changed hosted nightly workflow passed at 211f232ee: isolated regression, remaining suite, merged results, and the unchanged source-derived count check ( Android attempt 2 repeats a pre-input selector failure. Its |
|
The Android smoke positioning correction is isolated in #2369, with required local checks passing at |
|
The separate Android smoke correction #2369 is now green at |
|
The corrected same-harness comparison finished; neither run passes validation. Downstream harness SHA is
Both candidate first-command rejections use viewport 134 × 291.33 with a selector point at 201,389. They occur before the preparation/real synthesis. Their recordings start with a black app surface even though setup's AX text checks succeeded. The later relaunches render the app. This is another readiness/coordinate-evidence problem, not evidence that the warm-up ran and lost those first two gestures. Candidate boot 1's post-relaunch gesture is also concerning: empty preparation completes at All three candidate preparation markers report success (~5.2–5.3 s). One clean candidate boot passes, but these data do not establish that empty preparation fixes the original symptom. Keeping this draft. I am tracing the viewport mismatch and the first actual synthesis after relaunch before another comparison. Is there an existing contact-free XCTest bootstrap that initializes the event-delivery machinery beyond an empty record? The current preparation does not attach a virtual digitizer in the retained traces. |
|
The retained simulator logs narrow this further: the app does receive UIKit touch events in the unobserved candidate boot-1 relaunch gesture. That contradicts a complete “no touch stream reached the app” explanation for this sample. Using timestamps from
These are logged UIKit dispatches, not a count of every hardware sample or an exact touch-down delivery measurement. The delayed first app dispatch and reduced dispatch count are consistent with queued/coalesced delivery disrupting the scripted 650 ms source hold and 1,200 ms movement; that causal link still needs touch-phase/timestamp evidence. In the candidate failure, BackBoard logs attachment at This strengthens the reason to keep the PR draft rather than interpreting its successful empty record as demonstrated readiness. |
|
The completed comparison does not establish the fix at 211f232. As reported, neither baseline nor candidate passed validation, and one candidate gesture still produced no observed drop after successful empty preparation. Keep this draft until a controlled reproduction demonstrates the intended improvement; successful preparation alone is not evidence of input readiness. |
|
The iOS 26 versus iOS 27 comparison deserves to be explicit, because it is the starting observation for this report. The closest control is iOS 27 fallback, not just iOS 27 native: iOS 26 auto-selects the fallback engine, while the iOS 27 fallback lane explicitly selects that same engine. They run the same RN 0.85 Scenario Lab, public-outcome scenarios, semantic drag targets/timings, and Agent Device revision. In completed matrix 34060592647, Agent Device is
That final iOS 26 failure corrects my earlier interim progress report. It is a toolchain-query/preflight failure, not a missing-touch measurement. Separate retained iOS 26 cold runs demonstrate unobserved app outcomes, delayed UIKit dispatch and viewport errors, as detailed above. iOS 26 also has passing runs; this is intermittent. This is not an OS-only controlled experiment: iOS 26 uses the The subsequent touch-phase diagnostic run collected no measured gestures: two preparation errors and a setup timeout. It was observation-only, so its green workflow status is not a successful fix validation. The proposal remains draft, consistent with the latest maintainer feedback. |
211f232 to
84af5b1
Compare
|
Rebased onto main
A fresh comparison uses the same diagnostic app/harness revision for main baseline and candidate, three cold boots each. A temporary app probe records first observed touch phase, first movement, termination, event/arrival timing, target ancestry and recognizer state to distinguish delayed/coalesced delivery from wrong targeting or recognizer readiness. It is isolated from the downstream production PR. The prior diagnostic attempt never reached gestures, so it provides no touch-phase evidence. Keeping this draft as requested. The previously failed comparison remains failed evidence at its original commits; this rebase is not a fix claim. |
|
The rebase at 84af5b1 preserves all five commits without changes; no new code findings. The controlled cold baseline and candidate runs, downstream matrix, and nightly validation are still running. Keep this draft until the comparison demonstrates the intended improvement in observed app behavior; successful empty preparation alone does not establish input readiness. |
|
All active PR checks now pass at The rebuilt package passed a local cold boot with all three drag outcomes and the selector press observed (post-command drag observation waits 132–186 ms). The same-harness hosted baseline/candidate cold comparison is still running, as is the downstream matrix. Keeping this draft: these completed checks do not replace that controlled behavior evidence. |
|
The fresh main baseline 34205356736 completed at The candidate comparison is still running. I have started a small independent host probe to time the exact toolchain commands before/after cold boots and on immediate repeats, retaining selected Xcode and actual outputs. It omits our app build and Agent Device so it can distinguish slow valid toolchain queries from environment selection or wrapper behavior. No production retry, cache fallback or timeout change has been made. On the local Xcode 26.6 host the queries succeed in milliseconds. The recurring hosted preparation failure needs resolution before this baseline/candidate comparison can establish anything about the proposed gesture preparation. |
|
The candidate run 34205555155 completed and fails validation. The touch-phase probe finally captured a discriminating failed/successful pair in cold boot 2:
The failed touch targets Empty preparation had succeeded at Other results must remain separate:
The baseline failed all three preparations, as reported above, so there is still no usable red/green comparison. The proposed empty record is insufficient on its own. I am preserving these artifacts while resolving the separate SDK-query preparation blocker; the PR remains draft. |
|
The independent host probe reproduced severe toolchain slowness without Agent Device or our app. It runs the exact commands directly through Python subprocesses on
This establishes a slow valid toolchain operation outside the Agent Device wrapper, rather than proving every earlier SDK failure has the same cause. I am comparing process-creation timing and host resource snapshots on the iOS 26/27 runner images next. No production timeout/cache change has been made. The probe workflow's success means evidence collection completed, not that every query met its timeout. |
|
The rebase at 03fec1c preserves the reviewed input proposal; no new code findings. Keep this draft until a controlled cold-boot comparison shows that empty-event preparation improves the missing first reorder. The new nightly failed on the Xcode SDK-version probe before XCTest ran, so the 228 selected tests are not a passing execution result; that setup failure appears unrelated to this change. |
|
Continuing at The completed downstream matrix on merged main The main-run-loop diagnostic on For this exact PR head, the isolated regression plus 82 remaining native PR tests passed. Live iOS smoke failed locating New matched cold comparisons use the same app, single-link setup and run-loop probes: base |
|
All PR checks at I also found and corrected a diagnostic oracle error. In run 34225370081, boot 1's first gesture physically committed, but its observer returned Diagnostic correction: observe Separately, our downstream PR now pins published |
|
The matched comparison has completed; both jobs failed overall, so this remains draft.
No runtime change has been added on the strength of these results. The published-package downstream matrix continues; the PR's own checks and 228-test nightly are green. |
|
Downstream restoration now has a complete passing matrix with published The iOS 26 lane passed on attempt 1 (all five Agent Device pointer scenarios plus retained contracts); both iOS 27 lanes, Android, consumer/minimum-runtime checks, 16 shared outcomes across four configurations, and the exact-candidate gate also passed. The npm package contains the merged alert-routing fix from #2398. This is evidence that our restored workflow can pass on iOS 26. It does not supersede the failed matched cold-boot comparison or demonstrate an improvement from empty-event preparation. Keeping this draft as requested. While investigating the remaining dispatch delay, I found and separately fixed a profiler process-selection bug in #2406: a resolved executable path could still fall back to the basename and include another simulator’s app. The live two-simulator check selects only the requested process after that fix; trace stop still timed out, so there is no successful CPU trace to claim yet. |
|
The focused run with the corrected per-drag assertion executed zero gestures across three cold boots. Boots 1–2 failed the 5-second Xcode/SDK version probes. Boot 3 recovered through alert timeouts, then failed waiting for the initial order. The new failure screenshot made boot 3 more informative: the scenario and all six cards were visibly present, but the app itself displayed “Order: Seeded initial order” ( I reproduced a downstream app bug: resetting a pristine scenario or opening the same deep link clears the parent’s outcome, but unchanged child state does not publish it again. Both regression tests failed with that exact placeholder; resetting after an actual reorder already passed. Downstream fix a6951c2 gives resets a fresh scenario instance. All 17 example tests, lint, typecheck, and the Release build pass. Live iOS 26.5 with published 0.21.0 passes repeated links, pristine Reset, a real drag, and post-drag Reset. The next candidate run uses this app fix and the corrected assertion. The warmup proposal is unchanged; no new delivery result or improvement is established yet. |
|
The latest candidate still reproduces a missed first reorder after fixing the downstream reset bug: run 34295525695, candidate All three cold boots reached gestures. Nine gesture commands returned success; seven committed-source outcomes were observed, and two waits ended with observation errors. All three selector presses passed. Boot 3 provides the counterexample:
The system log also records that same touch timestamp ( Boot 2's “late” classification refers to a 3.573-second observation wait; its physical input timing was normal. This is separate from boot 3's delayed down. A matching baseline run is running with the same updated app/harness. Keeping this draft: successful empty preparation does not prevent the original failure. |
|
The controlled comparison now has three usable cold boots on both revisions, using the same app/harness
These small samples on separate hosted machines do not establish that preparation causes a regression. They do provide a usable comparison with no demonstrated benefit, plus a direct counterexample. This remains draft. Separately, the complete downstream matrix passes at the current app-fix commit with published 0.21.0, without this proposal. It required one failed-job rerun; iOS 26 then used its retained infrastructure retry after an alert timeout. Apple's The missed activation is consistent with the recognizer implementation: our fallback config requires 350 ms. RNGH 3.0.0 starts its timer in |
|
The first stack-capture run did not yield usable stacks. Boot 1 stopped during scenario setup on a main-thread timeout. Boots 2–3 reached all six gestures; five committed outcomes and both selector presses were observed. Boot 2's first reorder is visibly missing in the recording: preparation completed in 5.223 seconds, down reached application entry 1,891 ms late, and the first observed move followed about 329 ms later. Its outcome observation failed on typed Both sampler processes finished their 20-second collection but exceeded the independent 45-second limit while processing symbols, producing no report. In boot 2, collection also ended before the actual first down because the gesture command took about 71 seconds overall. This instrumented run therefore cannot locate the blocking stack or establish uninstrumented timing. The next diagnostic run uses a 60-second collection window and a bounded 180-second completion allowance, keeping the first app instance alive until its report is written. It also records whether the report contains actual stack samples. A local cold run passed all three gestures plus selector press and produced a report with real main-thread stacks. This changes diagnostic overhead and follow-up spacing only; the passing production workflow and input implementation are unchanged. |
|
The corrected capture run produced usable app stacks in two failing cold boots at candidate
Both main-thread call graphs contain this pre-application-dispatch stack: The This points to synchronous feature-flag file I/O in UIKit's touch preprocessing as a concrete lead. These are aggregated stacks from an instrumented run, so the counts are not exact delay measurements and do not identify the file or establish sole causation. The next useful probe is the read's path and duration. No feature settings or input-validation behavior have been changed; the empty-event proposal remains unproven and draft. An app-scoped file-trace run is now running on diagnostic revision |
|
The file-trace run identifies the read behind the earlier stack lead. Candidate All three exact-app-PID captures succeeded with actual file events. All nine gesture commands returned success; eight committed outcomes and all three selector presses were observed. Boot 1's first reorder was lost with 30/30 readable captures ( In boot 1, the app's main thread ( The trace therefore connects the same descriptor and main thread to the runtime's The corresponding main-thread reads in successful boots 2 and 3 take 21 µs and 8 µs. Their first-down delays are 17.2/21.2 ms, with 661.7/666.1 ms arrival holds and active pan recognition. Together with the previous UIKit preprocessing stacks, this localizes nearly all of this failure's delay to the runtime feature-file read. It still does not explain why that read is slow, or prove a portable agent-device fix. These are instrumented observations; no feature values, validation behavior, or input timings were modified. Evidence: The next run tests a diagnostic-only, read-only prefetch of that exact runtime file. If it removes both the slow app read and missing first reorder, that supports a host file-cache/readiness cause; it would not validate the current empty-event proposal. Keeping this draft. |
|
The first read-only prefetch control is inconclusive, not a passing comparison. The host read the exact 11,051-byte runtime
The diagnostic now collects logs from five seconds before the first gesture, using the native The next control, harness Update: both fresh-runner controls passed: six drag outcomes and two selector presses. First-down delays were 170.8 / 81.1 ms; arrival holds were 517.8 / 601.7 ms, with active pan recognition in both. Host prefetch took 1.102 / 1.826 ms. Sample 1's app-process discovery failed, so it has no file trace. Sample 2 captured the same main-thread UIKit read at 51.476 ms (PID 38660; app thread The matching no-prefetch controls now run on the same harness and input commit, again one cold boot on each of two fresh hosts. These small, separate-host samples do not yet establish causality or validate an upstream implementation change. The no-prefetch run’s first attempt measured zero gestures on both hosts: sample 1 failed the five-second SDK build-version query; sample 2 rebuilt the runner but failed to connect with Final matching-control result: the one allowed rerun still had an SDK-query setup failure on sample 1, but sample 2 passed all three drags and selector press without prefetch. Its first-down delay was 39.7 ms, arrival hold 647.9 ms, and pan state active. The captured main-thread This comparison does not demonstrate a prefetch benefit. The usable no-prefetch control was faster than the two prefetched controls. I am not promoting the file-prefetch experiment into production or treating it as an upstream fix. The identified 1.14-second read remains concrete failure evidence; why it sometimes stalls remains unresolved. No further rerun of these same controls is queued. |
|
The app-only kernel timeline captured the missing first reorder and localizes the wait inside AppleDiskImages2's request pool. Candidate In sample 1, empty preparation succeeded in 5.215 seconds. The first down reached app entry 2,063.9 ms late, and the first observed move followed only 16.5 ms later, without the pan recognizer. The recording retains the initial order, no committed event, and callback count 0 after this gesture. Its automated observation failed on Main thread The first 118 samples reach that wait through APFS block mapping/data-hash metadata lookup; the next 83 reach it through the data-read strategy. Re-reporting the saved binary with The report starts at 05:59:34.268 UTC; the selected interval starts about 6.34 seconds later and ends about 8.38 seconds later. App down is logged at 05:59:42.669 after its 15.8 ms handler, aligning the wait with the delayed input. This timeline does not record the filename. The separate file trace identified @thymikee, does this disk-image request-pool wait suggest a supported preparation hook worth testing here? The current empty-record proposal still has no demonstrated benefit. I would like to avoid turning an OS-specific file-cache experiment into agent-device behavior without evidence that it reliably addresses this boundary. |
|
The new timeline localizes the delayed first input to a blocked disk-image I/O wait, but it also reproduces the missed reorder after empty preparation succeeded. Green checks do not establish a benefit for this change. Keep this draft until a matched comparison demonstrates that the proposed preparation improves first-input delivery; the separate prefetch controls do not show that benefit. |
XCTest attaches its HID digitizer lazily, on the first synthesized event a runner process posts. On a cold or loaded simulator that attach can lag several seconds behind the synthesizeWithError call that triggers it. When the first synthesized gesture of a process is timed (a drag with an activation hold, a paced pan), the touch-down then lands seconds into a window whose later samples were scheduled relative to the intended touch-down, so the app reconstructs a malformed gesture even though synthesizeWithError reported success. Once attached, the digitizer stays attached for the runner process, so every later gesture — including after a target relaunch — lands on schedule. Force the one-time attach with a throwaway synthesized contact before the first real gesture. The gesture's own timings are unchanged; the warm-up only moves the unavoidable one-time attach cost off the first user gesture, and is a no-op on a warm host. Runs once per runner process, before the first `gesture` command. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…t process The process-scoped preparation flag forced its regression into its own xcodebuild invocation on the PR and nightly lanes, with an xcresult merge and a multi-invocation selection parser to keep the executed-count guarantee. A test-only reset on RunnerSynthesizedGesture makes the test self-contained instead, so it runs like every other runner unit test and the workflows, selection script and its tests return to main. Also drops the iOS-only guard (every synthesized route is already `#if os(iOS)` in Swift) and the lock (commands run on a serial queue), and names the preparation consistently in the log marker and test file.
03fec1c to
d3a651c
Compare
|
Took this over and force-pushed d3a651c, rebased on main fda41b4 (the five reviewed commits are preserved, one commit on top). Kept: the contact-free preparation at the shared record boundary, the flag set only on success, and the swizzle-spy regression. Changed: a test-only The PR body now states plainly that this does not fix the cold-boot missed reorder; the diagnosis in this thread places that delay in the app process. Diff vs main is 4 files, +141/−9. |
|
The simplified implementation and self-contained ordering test look sound at d3a651c. The remaining question is benefit: the recorded cold-boot miss still occurs after successful empty preparation. Please provide a matched comparison showing that moving setup ahead of the first timed path improves delivery or timing before making this preparation permanent. The iOS alert-test failure appears unrelated; the new preparation test passed. |
|
On benefit: the matched data already in this thread answers the question, and the answer is that moving setup ahead of the first timed path does not change delivery or timing.
So no matched comparison can show an improvement from this change. What the current head does is pay XCTest's one-time synthesis setup before the first timed record instead of inside it, at no added cost per command. Merging it rests on that alone, not on the cold-boot symptom. On CI: the |
|
The rerun is green, and the new comparison settles the benefit question: moving synthesis setup does not improve delivery or timing. I would close this change rather than add permanent preparation solely to move that cost. The cold first-touch delay needs work in the target app path identified above. |
|
Summary
Synthesize one empty XCTest event record, with no pointer path, per runner process before the first real record. It runs at the shared record boundary (
RunnerCreateEventRecord), so gesture/sequence, scroll, synthesized drag, swipe and coordinate tap all pass through it. No contact reaches the target, and the flag is set only after a successful preparation, so a failure is retried on the next record.What this does and does not establish
UIKit.pliston a disk-image I/O wait, before application event entry. That is outside this runner.Regression
testSynthesizedInputPreparationDoesNotDeliverContactsAndOrdersMixedRoutesswizzlesXCSynthesizedEventRecordto record the submitted path count per record across all six entry points, with the first preparation rejected:[0, 1, 0, 1, 1, 1, 1, 1]. A test-only+resetSynthesizedInputPreparationonRunnerSynthesizedGesturemakes it self-contained, so it runs in the shared test process like every other runner unit test. The earlier isolated xcodebuild invocation, xcresult merge and multi-invocation selection parsing are gone; the workflows and selection script match main apart from the PR-lane entry.Validation (head d3a651c, rebased on main fda41b4)
pnpm check:xctest-selection: 230 declared, PR list 83, nightly 228, 0 dark.-D AGENT_DEVICE_RUNNER_UNIT_TESTS: clean, no warnings.testSinglePointerFlingFallsBackToXCTestCoordinateDragWhenPrivateSynthesisFailson an iOS 26.2 simulator: 6/6 passed, with the failed-then-performed preparation sequence logged each iteration.