Skip to content

refactor(daemon): query the iOS selector through the request-bound runtime - #3430

Open
thymikee wants to merge 3 commits into
mainfrom
refactor/direct-ios-selector-binding
Open

thymikee wants to merge 3 commits into
mainfrom
refactor/direct-ios-selector-binding

Conversation

@thymikee

@thymikee thymikee commented Oct 11, 2026 •

Copy link
Copy Markdown
Member

Summary

Removes the last daemon → platform-mechanics value import (R76 leaked-platform-mechanics): src/daemon/direct-ios-selector.ts called queryAppleRuntimeSelector from root src/platform-runtime-apple-resources.ts directly. The Apple runtime now exposes a querySelector operation (SelectorQueryRuntimeOperations in @agent-device/contracts); the daemon obtains it through the request-bound BindDeviceRuntime and the iOS off-screen rescue (offscreen-target-probe.ts) consumes the bound operation only. The rescue's runner call now forwards the full execution projection including runnerLeaseContext (the logical-lease identity the retired edge never sent), so a leased-runner host routes the query to the right lease. querySelector is declared conditional on the captured touch/long-press/hover/fill and gesture-tier uses: owners that don't advertise it refuse exactly as pre-rescue, so Android/web/HarmonyOS keep the verdict path unchanged. Apple is the only owner binding it (iOS-family, simulator + device). The CONDITIONAL fact is the rescue's whole gate: provider-owned and macOS leaves declare the read unavailable in their facts. 31 files.

Validation

Head 33ee013ed. Green: check:quick, check:layering, check:di-seams, eager-closure budgets, test-file-size-ratchet, check:affected --run.

Live (iPhone 17 sim, iOS 27.0, fixture app Form screen): raw snapshot marked shipping-pickup @E81 off-screen (y=1060 > 874); press @e81 (requestId ca8d7890dd8c87c3) produced a runner querySelector command (runner-46eb14c5…, XCUITest predicate identifier ==[c] "shipping-pickup" in runner.log) and the response was COMMAND_FAILED with details.reason: offscreen_ref — the rescue read fired and the genuine refusal stood. Same-shape Android leg (emulator-5554, android-helper 0.21.26-dev): press @e108 refused offscreen_ref with zero extra queries in the request log. Daemon-route tests pin both sides of the conditional (live-rect rescue, zero-query refusal) plus the Apple fact/binding table; three wiring mutations each killed a test. Physical iPhone unavailable (locked), so the kind=device half ran in tests only. Cleanup verified: sessions closed, sim/emulator down, device status --stale clean.

View guided diff Turn on auto-fix

@github-actions

github-actions Bot commented Oct 11, 2026 •

Copy link
Copy Markdown
Contributor

Size Report

Metric Base Current Diff
Installed (including dependencies) 5.17 MB 5.17 MB +909 B
Package (unpacked) 5.17 MB 5.17 MB +909 B
Package (download) 1.55 MB 1.55 MB +148 B

Startup median (7 runs, lower is better):

Scenario Base Current Diff
CLI --version 17.0 ms 17.1 ms +0.1 ms
CLI --help 47.3 ms 45.3 ms -2.0 ms

@thymikee

Copy link
Copy Markdown
Member Author

At 8821640 the code looks right, but the iOS off-screen rescue has not been exercised live, so I can't call it ready yet. CI is green: 18 checks, none failing.

The PR rewires the call behind the iOS off-screen rescue: a new Apple binding, a new conditional fact, a new executor projection, and lease context is now forwarded (interaction-runtime.ts#L113). The PR body says the querySelector request never fired live. The live run only used the fused tapElementSelector route, which never reaches throwIfOffscreenInteractionTarget. If the binding or wiring is broken, the #1542 rescue disappears on iOS and nothing shows it, because the probe swallows every error and returns null, so a failed call looks like a genuine refusal. Could you run one live test on an iOS simulator (and a physical device if you have one, since the fact also covers kind=device)? Use a selector or @ref click or fill on a target the bulk tree marks off-screen, such as a row below the fold in the fixture app or any long list. Please show three things: runner.log has a querySelector command carrying that click's requestId; the response is either a tap at the live rect or a COMMAND_FAILED with details.reason offscreen_selector or offscreen_ref; and the same click on an Android emulator refuses with no extra query.

readRect in runtime-selector-query.ts#L92 does the same thing as parseRect in @agent-device/kernel/record: it checks for an object, requires four number fields, and returns {x,y,width,height}. platform-apple already imports that module. That leaves two copies of the runner-payload rect parser, so any change to rect validation must be made twice. Please delete readRect and call parseRect in readQuerySelectorNode.

Not blocking, so take or leave: no test pins the new wiring, since deleting conditional: ['querySelector'] from capturedTapUse, deleting ...selectorQueryOperations in the Apple runtime.ts, or making readOffscreenTargetRescue return undefined would leave every test green, so one daemon-route test with an advertising owner and an on-screen live rect, plus a sibling with a non-advertising owner that expects the offscreen_ref refusal with zero queries, would cover it; runnerCallOptions in runtime-selector-query.ts copies field by field the projection that iosRunnerOverrides already builds in interactions.ts, so adding querySelector there and binding it through the local interactor like tapElementSelector would be simpler, or at least build the options with { ...input.execution, signal } as bindAppleFindTextRuntime does; and a few small cleanups remain, namely the identical no-op selectOffscreenTargetQuery wrapper in touch-runtime.ts and gesture-runtime.ts (forward runtime.operations.querySelector directly), the unused signal parameter on confirmIosOffscreenTargetVisible and queryDirectIosSelector, queryDirectIosSelector being a single-caller pass-through, the extra isLocalIosRunnerSession check on top of the fact, the bare .rejects.toThrow() in runtime-selector-query.test.ts (assert name: 'AbortError'), and runnerLeaseContext now being forwarded to the query, which the PR body does not mention.

I did not run the tests or mutation checks; the claim that those mutations stay green comes from reading the changed tests and the existing daemon-route off-screen tests. I also did not confirm that the BrowserStack and AWS owners declare querySelector unavailable, though isLocalIosRunnerSession still gates them, so parity holds either way. Before merge, I need the live iOS run above showing the querySelector command during an off-screen-refused click or fill, and readRect replaced with parseRect.

…ntime

Move the last leaked platform-mechanics edge into the runtime contract: the
off-screen rescue's live read (#1542) is now the CONDITIONAL `querySelector`
operation on the request-bound Apple runtime, executed on the same binding the
command was admitted for (ADR 0019 §2/§9). direct-ios-selector.ts keeps only
the verdict projection; the daemon no longer builds an Apple runner request.

- contracts: QuerySelectorInput/Result + fact cell on
  SelectorObservationRuntimeOperations; conditional declaration on the four
  captured touch uses and the four gesture tier uses (parity side: an owner
  without the read refuses on the capture's verdict unchanged, exactly as
  every non-Apple owner always did).
- platform-apple: bindAppleQuerySelectorRuntime answers the read on the
  runner, projecting the node to rect+hittability; runner-demand classifies
  the operation as runner-host.
- other owners and the unavailable hub: declare the cell unavailable beside
  findText.
- delete src/daemon/apple-runner-options.ts (its last consumer retired).
…end to end

- replace the local readRect with @agent-device/kernel/record parseRect
- build the runner-call options with { ...input.execution, signal } like
  bindAppleFindTextRuntime instead of a field-by-field copy
- forward runtime.operations.querySelector directly; delete the no-op
  selectOffscreenTargetQuery wrappers
- inline queryDirectIosSelector into the probe and drop the unused signal
- let the CONDITIONAL fact be the whole rescue gate: the fact already
  refuses provider-owned and macOS leaves, so the isLocalIosRunnerSession
  check added nothing
- daemon-route tests pin both sides of the conditional (live-rect rescue,
  zero-query refusal) and the Apple leaf table pins fact-and-binding
  parity; assert AbortError on the aborted query
@thymikee
thymikee force-pushed the refactor/direct-ios-selector-binding branch from 8821640 to 33ee013 Compare October 11, 2026 17:54
@thymikee
thymikee marked this pull request as ready for review October 11, 2026 17:55
@thymikee

Copy link
Copy Markdown
Member Author

Rebased onto origin/main (11 commits); head 33ee013ed. Ready.

Live evidence (iPhone 17 sim, iOS 27.0, fixture app Form screen; raw snapshot marks shipping-pickup @E81 at y=1060 > 874):

  1. press @e81 (requestId ca8d7890dd8c87c3) — runner.log: AGENT_DEVICE_RUNNER_COMMAND_ACCEPTED command=querySelector commandId=runner-46eb14c5… + XCUITest trace identifier ==[c] "shipping-pickup"; request log: ios_runner_command_send requestId=ca8d7890dd8c87c3 data.command="querySelector" — the query carries the press's requestId.
  2. Response: COMMAND_FAILED, details.reason: "offscreen_ref" — genuine refusal stands (target really is below the fold), and the rescue read fired before it.
  3. Android emulator-5554 (android-helper 0.21.26-dev): same-shape press @e108 (Settings row at y≥2400) refuses offscreen_ref; the request log has only request_start+request_failed — zero extra queries.
    Physical iPhone thymikee-iphone is paired but unavailable (locked), so the kind=device half ran in tests only. Sessions closed, sim + emulator shut down, device status --stale clean.

Points (all in 9366fe64c unless noted; chore(gates) kept last at 33ee013ed):

  • required: readRect deleted, parseRect from @agent-device/kernel/record used.
  • a: two daemon-route tests in interaction-touch-runtime.test.ts (advertising owner → tap at live rect; non-advertising sibling → offscreen_ref with zero queries) + Apple leaf table in runtime.test.ts pinning fact+binding parity.
  • b: options built as { ...input.execution, signal } like bindAppleFindTextRuntime.
  • c: both selectOffscreenTargetQuery wrappers deleted; runtime.operations.querySelector forwarded directly.
  • d: unused signal params removed; queryDirectIosSelector inlined into the probe (projection tests moved to offscreen-target-probe.test.ts).
  • e: isLocalIosRunnerSession gate dropped — verified provider owners start querySelector unavailable via createUnavailablePlatformRuntimeFacts and none overrides it, so the fact already gates BrowserStack/AWS/limrun.
  • f: aborted-query test asserts name: 'AbortError'.
  • g: PR body now notes runnerLeaseContext rides the query.

Mutations (each run, then reverted): delete conditional: ['querySelector'] from capturedTapUse → rescue test fails; drop ...selectorQueryOperations in Apple runtime.ts → 2 leaf tests fail; readOffscreenTargetRescue → undefined → rescue test fails.

Gates serial on the pushed head: format, check:quick, check:layering, check:di-seams, eager-closure budgets, test-file-size ratchet, check:affected --run — all green.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

8 issues found across 31 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="src/daemon/__tests__/interaction-get-runtime-fixture.ts">

<violation number="1" location="src/daemon/__tests__/interaction-get-runtime-fixture.ts:99">
P3: This comment names `querySelectorFixture`, but the binding installs `mockQuerySelector`; correct the identifier so maintainers can find the implementation.</violation>
</file>

<file name="src/daemon/offscreen-target-probe.ts">

<violation number="1" location="src/daemon/offscreen-target-probe.ts:29">
P3: The rescue read is a separate live runner request but carries no AbortSignal, so an aborted interaction request cannot cancel it and the runner query keeps running (interaction-guarantees describes `querySelector` as "a SEPARATE runner request"). `QuerySelectorInput.signal` is part of the contract, and the signal is already available right above this call site: the backend wrapper in `createInteractionBackend` receives `_context.signal` and drops it. Thread `signal` from the backend context through `confirmOffscreenTargetVisible` into the `querySelector` input.</violation>
</file>

<file name="src/daemon/__tests__/offscreen-target-probe.test.ts">

<violation number="1" location="src/daemon/__tests__/offscreen-target-probe.test.ts:37">
P2: The identifier path is no longer asserted: this mock returns a rect regardless of its input, so querying with the wrong selector still passes. Capture the query input and assert `{ key: 'id', value: 'shipping-pickup' }`.</violation>
</file>

<file name="packages/platform-apple/src/__tests__/runtime-selector-query.test.ts">

<violation number="1" location="packages/platform-apple/src/__tests__/runtime-selector-query.test.ts:32">
P2: The PR relies on forwarding `runnerLeaseContext` for leased-runner routing, but neither the input nor this expectation includes it. Add a lease context and assert it reaches `queryAppleRunnerSelector`, or this test will pass if that field is dropped.</violation>

<violation number="2" location="packages/platform-apple/src/__tests__/runtime-selector-query.test.ts:60">
P2: `bind(controller.signal)` sets the binding signal, while this query omits `QuerySelectorInput.signal`; the test never exercises caller-signal cancellation or its composition. Add a separate call with an aborted signal on the query input while retaining binding-abort coverage.</violation>
</file>

<file name="packages/provider-limrun/src/facts-runtime.ts">

<violation number="1" location="packages/provider-limrun/src/facts-runtime.ts:177">
P3: This exposes a custom-snapshot hint for the unrelated live selector read, leaving the reason `querySelector` is unavailable unclear. Give this capability a selector-specific hint.</violation>
</file>

<file name="packages/platform-web/src/runtime.ts">

<violation number="1" location="packages/platform-web/src/runtime.ts:369">
P3: This marks a web-wide unavailable operation as an unsupported device kind and gives it an unrelated `open` hint. Use a selector-specific `unsupported-platform-leaf` fact so consumers inspecting the refusal do not receive misleading guidance.</violation>
</file>

<file name="src/daemon/interaction/internal/__tests__/interaction-touch-runtime.test.ts">

<violation number="1" location="src/daemon/interaction/internal/__tests__/interaction-touch-runtime.test.ts:271">
P2: This route test cannot catch the leased-runner regression described by the PR: its `contextFromFlags` fixture supplies no runner lease, and the query assertion checks only the selector. Give the test a lease context and assert `execution.runnerLeaseContext` reaches the query input.</violation>
</file>

Reply with feedback, questions, or to request a fix.

View guided diff | Turn on auto-fix | Re-trigger cubic

);
test('confirmIosOffscreenTargetVisible: returns the live rect when the owner confirms hittable + inside the viewport', async () => {
const rect = await probeWith(found({ x: 126, y: 136, width: 75, height: 38 }, true), {
identifier: 'shipping-pickup',

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: The identifier path is no longer asserted: this mock returns a rect regardless of its input, so querying with the wrong selector still passes. Capture the query input and assert { key: 'id', value: 'shipping-pickup' }.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At src/daemon/__tests__/offscreen-target-probe.test.ts, line 37:

<comment>The identifier path is no longer asserted: this mock returns a rect regardless of its input, so querying with the wrong selector still passes. Capture the query input and assert `{ key: 'id', value: 'shipping-pickup' }`.</comment>

<file context>
@@ -1,132 +1,129 @@
-  );
+test('confirmIosOffscreenTargetVisible: returns the live rect when the owner confirms hittable + inside the viewport', async () => {
+  const rect = await probeWith(found({ x: 126, y: 136, width: 75, height: 38 }, true), {
+    identifier: 'shipping-pickup',
+  });
 
</file context>

await bind().querySelector({
selector: { key: 'id', value: 'submit-order' },
options: { appBundleId: APP },
execution: {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: The PR relies on forwarding runnerLeaseContext for leased-runner routing, but neither the input nor this expectation includes it. Add a lease context and assert it reaches queryAppleRunnerSelector, or this test will pass if that field is dropped.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At packages/platform-apple/src/__tests__/runtime-selector-query.test.ts, line 32:

<comment>The PR relies on forwarding `runnerLeaseContext` for leased-runner routing, but neither the input nor this expectation includes it. Add a lease context and assert it reaches `queryAppleRunnerSelector`, or this test will pass if that field is dropped.</comment>

<file context>
@@ -0,0 +1,91 @@
+  await bind().querySelector({
+    selector: { key: 'id', value: 'submit-order' },
+    options: { appBundleId: APP },
+    execution: {
+      requestId: 'req-9',
+      logPath: '/tmp/runner.log',
</file context>

controller.abort();

await expect(
bind(controller.signal).querySelector({ selector: { key: 'label', value: 'Go' } }),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: bind(controller.signal) sets the binding signal, while this query omits QuerySelectorInput.signal; the test never exercises caller-signal cancellation or its composition. Add a separate call with an aborted signal on the query input while retaining binding-abort coverage.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At packages/platform-apple/src/__tests__/runtime-selector-query.test.ts, line 60:

<comment>`bind(controller.signal)` sets the binding signal, while this query omits `QuerySelectorInput.signal`; the test never exercises caller-signal cancellation or its composition. Add a separate call with an aborted signal on the query input while retaining binding-abort coverage.</comment>

<file context>
@@ -0,0 +1,91 @@
+  controller.abort();
+
+  await expect(
+    bind(controller.signal).querySelector({ selector: { key: 'label', value: 'Go' } }),
+  ).rejects.toMatchObject({ name: 'AbortError' });
+  expect(queryAppleRunnerSelector).not.toHaveBeenCalled();
</file context>

},
sessionName: RESCUE_ROUTE_SESSION,
sessionStore,
contextFromFlags,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: This route test cannot catch the leased-runner regression described by the PR: its contextFromFlags fixture supplies no runner lease, and the query assertion checks only the selector. Give the test a lease context and assert execution.runnerLeaseContext reaches the query input.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At src/daemon/interaction/internal/__tests__/interaction-touch-runtime.test.ts, line 271:

<comment>This route test cannot catch the leased-runner regression described by the PR: its `contextFromFlags` fixture supplies no runner lease, and the query assertion checks only the selector. Give the test a lease context and assert `execution.runnerLeaseContext` reaches the query input.</comment>

<file context>
@@ -215,6 +217,99 @@ test('press @ref fails closed when stored ref bounds are invalid (ADR 0014)', as
+    },
+    sessionName: RESCUE_ROUTE_SESSION,
+    sessionStore,
+    contextFromFlags,
+    ...getRuntimeBindings(),
+  });
</file context>


/**
* The CONDITIONAL live selector read (#1542 rescue). Off by default so only the tests that pin
* the rescue's wiring flip it; when advertised, the binding answers with `querySelectorFixture`.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: This comment names querySelectorFixture, but the binding installs mockQuerySelector; correct the identifier so maintainers can find the implementation.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At src/daemon/__tests__/interaction-get-runtime-fixture.ts, line 99:

<comment>This comment names `querySelectorFixture`, but the binding installs `mockQuerySelector`; correct the identifier so maintainers can find the implementation.</comment>

<file context>
@@ -90,6 +94,14 @@ const mockFillRef = vi.fn(async (input: FillRefInput) => ({
 
+/**
+ * The CONDITIONAL live selector read (#1542 rescue). Off by default so only the tests that pin
+ * the rescue's wiring flip it; when advertised, the binding answers with `querySelectorFixture`.
+ */
+export const mockQuerySelector = vi.fn(
</file context>
Suggested change
* the rescue's wiring flip it; when advertised, the binding answers with `querySelectorFixture`.
* the rescue's wiring flip it; when advertised, the binding answers with `mockQuerySelector`.

let nodeReading: Awaited<ReturnType<typeof querySelector>>['node'];
try {
result = await queryDirectIosSelector(session, selector, requestOptions);
({ found, node: nodeReading } = await querySelector({

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: The rescue read is a separate live runner request but carries no AbortSignal, so an aborted interaction request cannot cancel it and the runner query keeps running (interaction-guarantees describes querySelector as "a SEPARATE runner request"). QuerySelectorInput.signal is part of the contract, and the signal is already available right above this call site: the backend wrapper in createInteractionBackend receives _context.signal and drops it. Thread signal from the backend context through confirmOffscreenTargetVisible into the querySelector input.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At src/daemon/offscreen-target-probe.ts, line 29:

<comment>The rescue read is a separate live runner request but carries no AbortSignal, so an aborted interaction request cannot cancel it and the runner query keeps running (interaction-guarantees describes `querySelector` as "a SEPARATE runner request"). `QuerySelectorInput.signal` is part of the contract, and the signal is already available right above this call site: the backend wrapper in `createInteractionBackend` receives `_context.signal` and drops it. Thread `signal` from the backend context through `confirmOffscreenTargetVisible` into the `querySelector` input.</comment>

<file context>
@@ -1,25 +1,42 @@
+  let nodeReading: Awaited<ReturnType<typeof querySelector>>['node'];
   try {
-    result = await queryDirectIosSelector(session, selector, requestOptions);
+    ({ found, node: nodeReading } = await querySelector({
+      selector,
+      ...(session.appBundleId === undefined
</file context>

// ownership answer covers the live selector read: no rescue hook on a provider device.
...selectorObservationRuntimeOperationFacts({
findText: customSnapshotUnavailable,
querySelector: customSnapshotUnavailable,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: This exposes a custom-snapshot hint for the unrelated live selector read, leaving the reason querySelector is unavailable unclear. Give this capability a selector-specific hint.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At packages/provider-limrun/src/facts-runtime.ts, line 177:

<comment>This exposes a custom-snapshot hint for the unrelated live selector read, leaving the reason `querySelector` is unavailable unclear. Give this capability a selector-specific hint.</comment>

<file context>
@@ -170,9 +170,11 @@ export function limrunAppLogFacts(
+      // ownership answer covers the live selector read: no rescue hook on a provider device.
       ...selectorObservationRuntimeOperationFacts({
         findText: customSnapshotUnavailable,
+        querySelector: customSnapshotUnavailable,
       }),
       ...viewportRuntimeOperationFacts({ setViewport: viewportUnavailable }),
</file context>
Suggested change
querySelector: customSnapshotUnavailable,
querySelector: unavailableFact(
'unsupported-provider-mode',
'Limrun-owned devices do not expose a live selector read.',
),

// selector read either: the off-screen guard refuses on the capture's verdict unchanged.
...selectorObservationRuntimeOperationFacts({
findText: openTargetKindUnavailable,
querySelector: openTargetKindUnavailable,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: This marks a web-wide unavailable operation as an unsupported device kind and gives it an unrelated open hint. Use a selector-specific unsupported-platform-leaf fact so consumers inspecting the refusal do not receive misleading guidance.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At packages/platform-web/src/runtime.ts, line 369:

<comment>This marks a web-wide unavailable operation as an unsupported device kind and gives it an unrelated `open` hint. Use a selector-specific `unsupported-platform-leaf` fact so consumers inspecting the refusal do not receive misleading guidance.</comment>

<file context>
@@ -362,9 +362,11 @@ function webRuntimeFacts(
+      // selector read either: the off-screen guard refuses on the capture's verdict unchanged.
       ...selectorObservationRuntimeOperationFacts({
         findText: openTargetKindUnavailable,
+        querySelector: openTargetKindUnavailable,
       }),
       ...screenshotRuntimeOperationFacts({ capture: browserDevice }),
</file context>
Suggested change
querySelector: openTargetKindUnavailable,
querySelector: unavailableFact(
'unsupported-platform-leaf',
'Live selector reads are not supported on web targets.',
),

@thymikee

Copy link
Copy Markdown
Member Author

The earlier findings on parseRect reuse and live iOS querySelector evidence are fixed at 33ee013, and I found no new code problems. CI is green: 19 checks, 0 failing. I did not run the tests or the mutation checks. The claim that the capturedTapUse-conditional mutation turns the rescue test red comes from the author and from reading the code. The live evidence is the author's quoted log lines. They show the rescue read firing and the genuine refusal standing, but not a rescue that taps at the live rect. The physical-device kind ran in tests only.

Not blocking, and you can take or leave these: the delta comment at interaction-runtime.ts:87 says macOS and watchOS owners do not advertise the read, but watchOS does advertise querySelector (runtime.ts:294) and only never holds the hook because captured uses are refused there. Dropping "and watchOS kinds" and the sentence about the retired isLocalIosRunnerSession gate would fix it, and the readOffscreenTargetRescue doc block (lines 98-107) narrates that retired gate too. The header comment at interaction-touch-runtime.test.ts:220 says dropping ...selectorQueryOperations from the Apple runtime breaks one of these route tests. These tests bind the fixture owner, so only runtime.test.ts catches that mutation. The comment could name just the two mutations the route tests catch.

On the Cubic threads, the P2 on test names at runtime-selector-query.test.ts:55-60 and the P3 on the fixture doc at interaction-get-runtime-fixture.ts:99 still apply, and the P2 should be settled before merge (rename the tests or reply on the thread); the P3 is optional. These five do not apply, so you can resolve them. The id path is pinned in the route test, the direct-ios-selector unit test and the probe test (thread). Lease forwarding is the spread at runtime-selector-query.ts:37, and a lease assertion would only test object spread (thread). Daemon-side lease forwarding is owned by runtimeExecutionFromContext, which this PR does not change (thread). The Apple binding already composes request.scope.signal, so an aborted request aborts the rescue read (thread). The two threads on the unavailable-fact hint never reach the user, because the guard refuses with offscreen_ref (iOS thread, web thread).

Apart from that P2 thread, nothing stops the merge on the code side.

This branch has not been deployed

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant