Skip to content

fix(ios): distinct lineage for in-place system-surface captures (verify/settle) #2450

Description

@thymikee

Follow-up to #2438 / #2448.

Purpose. A capture served from a system surface (SafariViewService sheet) must not be comparable to an app-baseline capture, or --verify/--settle could diff a sheet against the app and misreport.

Required behavior. Carry the runner's systemSurface provenance into the iOS snapshot comparisonIdentity/lineage so a system-surface capture and an app capture are never treated as the same presentation. Post-tap corroboration (hasMatchingPresentation) and --settle diffs must refuse to compare across the app↔sheet boundary.

Observable completion. A --settle/--verify around an interaction that crosses into or out of the sheet does not produce a spurious diff; a unit test pins the identity mismatch.

Dependencies. #2448 already threads systemSurface through readAppleSnapshotResult; this wires it into lineage. See Fable rule 5 in the #2448 ADR amendment.

Activity

  1. thymikee commented on Sep 10, 2026

    @thymikee
    MemberAuthor

    Folded into #2448 following review there, rather than shipping separately: the maintainer's read was that comparison across this boundary must not be left open while the in-place serve lands.

    The surface identity now reaches SnapshotState as iosSystemSurfaceBundleId, and hasMatchingPresentation refuses outright when a baseline and a post-action capture disagree about it — so an app capture and a sheet capture can no longer meet in legacy same-presentation matching, including recorded-tap failure corroboration. Covered by a regression test in interaction-ios-tap-outcome.test.ts.

    Closing once #2448 merges.

  2. thymikee commented on Sep 21, 2026

    @thymikee
    MemberAuthor

    Closed against main at 45e4c594a1. Implemented in 0feb4e26a0 (#2448) and extended by 6ce8657146 (#2639).

    Lineage carries the surface. A system-surface capture and an app capture no longer share a comparison identity: post-tap corroboration refuses across the boundary through the comparison key itself, at src/daemon/interaction/internal/interaction-ios-tap-outcome.ts:151-181, with the fixture shape pinned at src/daemon/__tests__/ios-comparison-key-fixture.ts:19-21.

    Settle and verify refuse the cross-boundary diff. src/commands/interaction/runtime/post-action-surface.ts:26-72 exposes surfaceScopedNodes, resolvePostActionSurfaceChange, the cross-surface changedFromBefore and crossSurfaceSettleHint; src/commands/interaction/runtime/settle.ts:181-201,428-432 refuses the diff on that verdict instead of comparing, and src/daemon/generic-settle.ts:80-84 stamps the baseline surface on the generic routes so they get the same guard.

    Pinned both directions. src/daemon/interaction/internal/__tests__/interaction-ios-tap-outcome.test.ts:328 ("a capture of a system surface cannot corroborate a tap taken against the app"), src/commands/interaction/runtime/post-action-surface.test.ts:49,87,120,150 (app→sheet and sheet→app, with and without --verify), src/daemon/__tests__/generic-settle.test.ts:378,406,440,474 (the same boundary on the generic scroll/back routes).

    Observable completion is met: a --settle/--verify that crosses into or out of the sheet does not report a spurious diff, and the refusal keys on surface identity rather than on captured text, which keeps it honest if the sheet's content changes.

    The model unification for the two channels that carry this fact is still open as #2489; that is a wider ask than this issue's contract and is the right place for it.

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

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions