Skip to content

CI overhaul (1/9): route iOS 26 device-contract pointer scenarios through Detox #65

Closed

Description

@thiagobrez

Part of the CI overhaul execution plan (#59, map #53). Order: 1 of 9 — lands first; it fixes the pipeline's dominant failure before anything else builds on candidate green streaks.

Decision: #60 (diagnosis in #54).

Scope

Route every pointer scenario in the ios26.auto-fallback device-contract configuration through Detox instead of Agent Device. Scenario definitions, terminal public-outcome assertions, and the four-engine parity comparison stay unchanged — only the pointer harness route changes. Do not implement the Agent Device input-readiness probe, widen gesture timings, or add scenario retries.

Resulting harness ownership: iOS 27 native and iOS 27 forced-fallback keep Agent Device for pointer scenarios (multi-selection on ios27.native stays Detox); iOS 26 automatic and Android use Detox throughout.

Files

  • e2e/contracts/pointer-drivers.json — delete the five ios26.auto-fallback overrides (free-form-reorder, virtualized-list-reorder, section-list-reorder, multi-selection-reorder, scoped-drop) so those scenarios fall through to the default Detox driver. scripts/run-device-contract-isolated.mjs already skips the Agent Device runner preflight lazily (driver === 'agent-device' && !agentDevicePrepared), so no script change is needed.
  • docs/adr/0006-automated-publication-confidence.md — append an amendment recording the per-configuration harness ownership table and the rationale: the first pointer gesture synthesized by the Agent Device XCTest runner into a freshly erased, cold-booted iOS 26.5 simulator on hosted runners arrives seconds late or never while injection reports success (Diagnose the iOS 26 auto-fallback free-form-reorder replay divergence #54); the iOS 26 cell's unique responsibility is proving automatic engine selection picks fallback, which Detox pointer synthesis proves equally.

Ruleset / required-check changes

None.

Acceptance criteria

  • All ios26.auto-fallback scenarios list driver: detox in the resolved plan; no Agent Device daemon/runner is started in that job.
  • ADR 0006 amendment appended, matching the style of the 2026-09-01/2026-09-02 amendments.
  • Five consecutive successful ios26.auto-fallback contract runs on main/nightly, each with the four-engine parity job passing and no pointer-test retry — the acceptance streak decided in Decide the iOS 26 device-contract input-readiness fix #60. The task closes when the streak completes.

Activity

  1. thiagobrez commented on Sep 4, 2026

    @thiagobrez
    OwnerAuthor

    AC1's second clause isn't satisfiable through pointer-drivers.json

    Implemented in b14997c. The first clause holds — resolving the plan the way scripts/run-device-contract-isolated.mjs:89 does now yields driver: detox for all five ios26.auto-fallback pointer scenarios, and no Agent Device pointer runner or preflight starts for them.

    The second clause — "no Agent Device daemon/runner is started in that job" — can't be met by the pointer route alone. Two non-pointer scenarios on that job shell out to Agent Device from inside the Detox run, independent of any pointer driver:

    • e2e/contracts/portable-contract.e2e.cjs:183 — prepareIndependentIosSystemSession() runs yarn agent-device open for interrupt: 'rotate' and interrupt: 'inactive'.
    • e2e/contracts/portable-contract.e2e.cjs:239 — runIndependentIosSystemAction('orientation', 'landscape-left').

    Both gesture-system-interruption (rotate) and ios-inactive list ios in platforms, so both run on ios26.auto-fallback. The scope line "only the pointer harness route changes" and this clause are in tension: the job still starts an Agent Device daemon, just not for pointer input.

    Suggested amendment: re-scope the clause to "no Agent Device pointer harness is started in that job — no runner preflight and no run-agent-device-pointer.mjs invocation," which is what the change actually guarantees and what #54 diagnosed.

    Consequence for the AC3 streak

    Worth deciding before anyone starts counting the five runs. ios26.auto-fallback previously ran scripts/prepare-agent-device-ios-runner.mjs because free-form-reorder — the first applicable scenario — routed to Agent Device. It now never runs, so the first Agent Device use in that job is a cold open inside gesture-system-interruption (scenario 7 of 13), under Detox's 180s test timeout.

    Two reasons I'd rate this a low risk rather than a blocker, but not zero:

    1. The preflight was never warming the daemon those interruption scenarios use. It runs under AGENT_DEVICE_STATE_DIR (prepare-agent-device-ios-runner.mjs:56) with session issue39-ios-runner-preflight, and explicitly calls stopDefaultDaemon() both before (:82) and after (:141). The interruption path uses the default state dir and session issue39-detox-interruption. The only shared warm-up was the XCTest runner build.
    2. Diagnose the iOS 26 auto-fallback free-form-reorder replay divergence #54 diagnosed synthesized pointer touches being dropped. orientation and open aren't synthesized touches, so the specific failure mode shouldn't transfer.

    If the streak does break on gesture-system-interruption or ios-inactive rather than on a pointer scenario, that's the cause to look at first — and the fix would be keeping the runner preflight for the job independent of pointer routing, which is a script change this ticket ruled out.

    Two smaller notes

    • The change had to flip routes, not just delete the overrides, since run-device-contract-isolated.mjs:89 dispatches on routes. Deleting the overrides alone would have left the preflight running. b14997c adds a test asserting each route equals the driver its plan resolves to, so the two maps can't drift.
    • No changeset: CONTRIBUTING.md:26 exempts test/CI/doc changes. But isReleaseSurface (scripts/release-policy.mjs:15) matches any src/ prefix and the updated test lives at src/__tests__/issue39-device-contract.test.ts, so the changeset-requirement check will need the No changeset needed maintainer comment. Pre-existing gap between the documented rule and the script; left alone per "Ruleset / required-check changes: None."
  2. thiagobrez commented on Sep 4, 2026

    @thiagobrez
    OwnerAuthor

    Update from PR #87's first CI run, correcting my previous comment's risk assessment in both directions:

    The Detox route works. All five ios26.auto-fallback pointer scenarios passed via Detox, and the two Agent-Device-driven interruption scenarios (gesture-system-interruption, ios-inactive) passed cold, without the runner preflight — the cold-start concern I flagged did not materialize.

    But the job still failed, in the screenshot-feedback apparatus — a different scenario on each attempt (free-form-reorder, then virtualized-list-reorder), both at drag released before feedback sampling. That message was misleading: the drags held for 8000ms, past the ~4.9s sampling window, so the assert could only fire when the Detox gesture promise settled early — and its sampling-first ordering swallowed the real error. Since Approve exact tarball for publication workflow gates on contract+feedback+parity and is required on main, this deadlocked the whole plan: the PRs scheduled to delete the apparatus (#68) could not merge either.

    Resolution: #68 was pulled forward into PR #87 (see the comment there). Consequences for this ticket:

  3. thiagobrez commented on Sep 4, 2026

    @thiagobrez
    OwnerAuthor

    Streak accounting — run 1 (push bc68489, run 33866714821, PR #87 merge): does not count. Streak: 0/5.

    The ios26.auto-fallback contract job itself passed (first-ever green on main via Detox, no pointer retry), but the four-engine parity job failed, and AC3 requires parity green per run. Parity fell to two failures unrelated to the iOS 26 route:

    Next chance to score run 1: tonight's nightly (03:17 UTC) or the next push to main.

  4. thiagobrez commented on Sep 5, 2026

    @thiagobrez
    OwnerAuthor

    Streak accounting update — 3/5, with run 4 in flight.

    # Run Trigger / SHA ios26 contract Parity Pointer retry Counts
    — 33866714821 push bc68489 (Sep 4) ✓ ✗ (ios27.native unmount launch flake + geometry budget) none ✗ streak 0
    — 33912402913 push e15ad0a (Sep 4) ✗ gesture-system-interruption 180s timeout, both attempts ✗ none ✗ streak 0
    1 33951437527 push 9a2bcbb (Sep 5) ✓ single attempt ✓ none ✓ 1/5
    2 33953330578 nightly 9a2bcbb (Sep 5) ✓ single attempt ✓ none ✓ 2/5
    3 33962748103 push 9a50e91 (Sep 5) ✓ single attempt ✓ none ✓ 3/5
    4? 33984737081 push 6f6f04a (Sep 5) in flight — — —

    Note on the e15ad0a break: gesture-system-interruption is not a pointer scenario — it's the Agent-Device-driven rotate interruption, running cold now that no pointer scenario triggers the runner preflight on iOS 26 (the residual risk flagged when this ticket landed). One occurrence in six runs so far. If it recurs and keeps resetting the streak, the candidate fixes are a lightweight preflight for the interruption session or moving rotate to a non-Agent-Device input — either needs a scope decision beyond this ticket.

    Closes when runs 4 and 5 land green consecutively — potentially tonight's nightly.

  5. thiagobrez commented on Sep 5, 2026

    @thiagobrez
    OwnerAuthor

    Streak accounting — 4/5. Run 4: 33984737081 (push 6f6f04a, 2026-09-05 18:39 UTC) completed green; ios26.auto-fallback succeeded in 24m41s on a single attempt, parity green. One more consecutive qualifying run closes this — tonight's nightly (03:17 UTC) or the next push to main.

  6. thiagobrez commented on Sep 5, 2026

    @thiagobrez
    OwnerAuthor

    Acceptance streak complete — 5/5. Closing.

    # Run Trigger / SHA (all 2026-09-05) ios26 contract Parity Pointer retry
    1 33951437527 push 9a2bcbb 07:02 ✓ single attempt ✓ none
    2 33953330578 nightly 9a2bcbb 07:43 ✓ single attempt ✓ none
    3 33962748103 push 9a50e91 11:13 ✓ single attempt ✓ none
    4 33984737081 push 6f6f04a 18:39 ✓ single attempt ✓ none
    5 33991393559 push ff7505a 20:52 ✓ single attempt ✓ none

    All acceptance criteria met:

    This unblocks #74 (7/9). Residual watch item, recorded earlier in this thread: the cold gesture-system-interruption Agent Device timeout (one occurrence in eight main runs, e15ad0a). If it recurs, spec a lightweight preflight for the interruption session or a non-Agent-Device rotate input as a follow-up ticket.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    ready-for-agentFully specified, ready for an AFK agent

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions