Skip to content

refactor: complete the find cutover onto the request-bound runtime - #1944

Merged
thymikee merged 2 commits into
mainfrom
agent/wave4-find-r35
Aug 21, 2026
Merged

refactor: complete the find cutover onto the request-bound runtime#1944
thymikee merged 2 commits into
mainfrom
agent/wave4-find-r35

Conversation

@thymikee

Copy link
Copy Markdown
Member

The deferred Wave 4 unit for #1739 — R35, reserved since the wave started, unblocked by focus
(R40, #1925) and type (R41, #1935). Unit record posted to the tracker before work began.
This completes Wave 4: layering now recognizes 26 migrated commands.

What was actually left of legacy find

Less than the reservation implied, which is why this is a small PR: the read-only legs ran bound
since #1877, the focus leg since #1925, the type leg since #1935, and click/fill re-invoke their
own commands. The one remaining direct platform execution was the mutating-target capture
createFindTargetCapture built the selector capture runtime without a bound capture and fell
through the legacy dispatch branch whose own comment reserved it for "the last one to migrate".

What changed

  • The mutating path enters resolveBoundSelectorCapture — the shared admit-then-bind entry that
    already named find in its intent table — and threads the bound capture into the target
    capture. No new operation, binder, or facts: admission flip plus deletion.
  • Find was the last one, so the seam's debt is collected: capture on
    SelectorCaptureRuntimeParams is required and the legacy fallback branch is deleted. The
    backend's bound becomes required-to-state; the one declared absence is the observation-free
    duration wait (wait 400), whose runtime now carries no capture backend at all — an
    accidental capture fails loudly instead of falling through to anything.
  • Descriptor: device-runtime with findRuntimePlanUses (the selector-text plans shared with
    get, plus focusRuntimeUse and typeTextRuntimeUse). The capability bucket and both overlay
    memberships (HARMONYOS_SUPPORTED_COMMANDS, WEB_QUERY_COMMANDS — the latter is now
    ['audio']) are deleted.
  • R35's operationOwners are the selector family's shared owners — find introduces no parallel
    route to any operation.
  • The capture/backend tests move off the dispatchCommand mock onto the bound seam, where the
    poll-deadline and private-ax-pin assertions actually live now.

Live evidence

All five legs on a real iPhone 17 Pro simulator at this head:

Leg Result
find General exists Found: true
find General get text General
find General click (delegated) tapped, navigated to the General page
find Search --first focus (bound R40) Focused (201, 822), search overlay opened
find Search --first type "r35 live" (bound R41) route synthesized-first-responder, and the tree shows [search] "r35 live" — committed

The other platform/provider cells inherit their evidence from the shared owners: every operation
this unit's plans admit (captureSnapshot, readTextAtPoint, focusPoint, typeText) carries
per-cell live evidence from the R32/R36/R40/R41 rounds, and this unit adds no new operation or
route — it changes who admits, not what executes.

Gate

pnpm check:affected --run all runnable checks passed; layering green with R35 among 26
single-path migrated commands. 15 files, +214/−136 — deletion-dominated, per the unit record.

The deferred Wave 4 unit for #1739 (R35), unblocked by focus (R40) and type
(R41). Find's read-only legs, focus leg, and type leg already ran bound; the
one remaining direct platform execution was the mutating-target capture, which
built createSelectorCaptureRuntime without a bound capture and fell through
the legacy dispatch branch reserved for "the last one to migrate".

- The mutating path now enters resolveBoundSelectorCapture — the selector
  family's shared admit-then-bind entry, which already named find in its
  intent table — and threads the bound capture into the target capture.
- Find was that last one: `capture` on SelectorCaptureRuntimeParams is now
  required and the legacy fallback branch is deleted. The backend's `bound`
  becomes required-to-state, with the observation-free duration wait
  (`wait 400`) as the one declared absence — its runtime now carries no
  capture backend at all, so an accidental capture fails loudly instead of
  falling anywhere.
- The descriptor flips to device-runtime with findRuntimePlanUses (the
  selector-text plans shared with get, plus focusRuntimeUse and
  typeTextRuntimeUse); the capability bucket and both overlay memberships
  (HARMONYOS_SUPPORTED_COMMANDS, WEB_QUERY_COMMANDS) are deleted.
- R35 lands with the selector family's shared operation owners; the capture
  and backend tests move off the dispatch mock onto the bound seam, which is
  where the poll-deadline and private-ax-pin assertions actually live now.

Wave 4 is complete: layering recognizes 26 migrated commands.
@github-actions

github-actions Bot commented Aug 21, 2026

Copy link
Copy Markdown

Size Report

Metric Base Current Diff
JS raw 2.36 MB 2.36 MB +1.2 kB
JS gzip 790.9 kB 791.1 kB +241 B
npm tarball 907.9 kB 908.1 kB +252 B
npm unpacked 3.15 MB 3.15 MB +1.2 kB

npm unpacked components

Component Base Current Diff
JS / dist source 2.51 MB 2.51 MB +1.2 kB
Apple runner source/project 543.4 kB 543.4 kB 0 B
macOS helper source 54.5 kB 54.5 kB 0 B
Android helper artifacts 0 B 0 B 0 B
Other package files 41.9 kB 41.9 kB 0 B

Startup median (7 runs, lower is better):

Scenario Base Current Diff
CLI --version 25.7 ms 26.1 ms +0.4 ms
CLI --help 76.4 ms 77.4 ms +1.0 ms

Top changed chunks:

Chunk Raw diff Gzip diff
dist/src/snapshot.js +26.2 kB +8.3 kB
dist/src/sdk-batch-runner.js +548 B +74 B
dist/src/internal/daemon.js +62 B +25 B

Top changed packed files

Packed file Base Current Diff
dist/src/sdk-batch-runner.js 92.2 kB 92.7 kB +548 B
dist/src/snapshot-runtime-binding.js 6.1 kB 6.7 kB +520 B
dist/src/find.js 7.0 kB 7.2 kB +190 B
dist/src/provider.js 15.5 kB 15.4 kB -133 B
dist/src/internal/daemon.js 98.9 kB 99.0 kB +62 B
dist/src/dispatch.js 26.2 kB 26.2 kB -14 B
dist/src/selector-runtime.js 22.1 kB 22.1 kB +7 B
dist/src/type-text-runtime.js 1.3 kB 1.3 kB +7 B

@thymikee

Copy link
Copy Markdown
Member Author

P1 — R35 still violates the ADR 0019 one-bind-per-handler invariant. A mutating find first calls resolveBoundSelectorCapture; find focus then calls resolveBoundFocusRuntime, and find type calls that plus resolveBoundTypeTextRuntime. The type leg therefore performs three separate facts/admit/bind projections in one handler. The request binding cache may reuse the broad owner, but that does not make this the single action-selected execution use required by ADR 0019 section 9, and the new tests do not assert the find handler bind count. Resolve an action-specific combined plan (capture + focusPoint, or capture + focusPoint + typeText) and bind once, then project those operations to the existing capture/focus/type executors. Add a regression proving one facts inspection and one bindDevice call for find focus and find type.

Review P1 on #1944: a mutating find performed up to three separate
facts/admit/bind projections — capture, then focus, then type re-admitted
per leg. The request handler now resolves ONE action-selected plan and binds
once:

- New selector intents find-focus / find-type carry combined uses
  (capture + focusPoint, capture + focusPoint + typeText) through the same
  admit-then-bind path and plan machinery every selector capture uses; the
  new bind arms reuse the existing capture selectors, so the shared operation
  owners stay single. Delegated click/fill resolve targets on the plain
  capture pair.
- The handler threads the one bind's operations to the shared executors:
  executeFocusPoint is extracted as the single lexical owner of the focusPoint
  call (R40's owner claim follows it), and executeBoundTypeText's runtime
  param narrows to the operations it actually uses so find can pass its own
  broader bind through it.
- findRuntimePlanUses becomes the full action-selected set (eight uses), and
  the descriptor test pins each use's exact requirement list.
- Regression: find focus and find type each assert exactly one facts
  inspection and one bindDevice call — the pre-fix handler fails both (two
  and three binds respectively).

Live re-verified on iPhone 17 Pro at this head: find focus and find type both
execute through the single bind, route synthesized-first-responder, typed text
visible in the captured tree.
@thymikee

Copy link
Copy Markdown
Member Author

P1 addressed at 63af68907 — one action-selected plan, one bind, with the regression you asked for

You were right, and the violation was worse than stated for the type leg: capture bind, then the
focus executor's own admit+bind, then the type executor's — three projections in one handler.

The fix follows the plan machinery that already existed. Two new selector intents,
find-focus and find-type, carry combined uses (capture + focusPoint,
capture + focusPoint + typeText, each with the active-app split) through the same
resolveSelectorCaptureRuntimePlanadmitAndBindSnapshotCapture path every selector capture
uses. The handler resolves the plan from the parsed action and binds exactly once; delegated
click/fill resolve targets on the plain capture pair, since the re-invoked command carries its own
admission. findRuntimePlanUses becomes the full eight-use action-selected set, and the
descriptor test pins each use's exact requirement list.

Single lexical owners survive the projection. The new bind arms reuse
selectActiveAppSnapshot/selectSnapshotWithoutActiveApp, so the capture owners are unchanged.
executeFocusPoint is extracted as the one owner of the focusPoint call (R40's
operationOwners claim follows the name), and executeBoundTypeText's runtime param narrows to
the operations it actually uses so find's broader bind passes through the same single call site —
the cutover gate verified both counts.

The regression tests are in (find-single-bind.test.ts, its own file because find.test.ts
sits at its ratchet pin): find focus and find type each assert exactly one inspectFacts and
one bindDevice call. The pre-fix handler fails both — two and three binds respectively — so the
invariant is now enforced, not just restored.

Live re-verified on a real iPhone 17 Pro at this head: both legs execute through the single bind,
route synthesized-first-responder, typed text visible in the captured tree. Local
check:affected --run green; exact-head lanes running.

@thymikee

Copy link
Copy Markdown
Member Author

Re-reviewed exact head 63af689. The prior multi-bind blocker is resolved: each mutating find action selects one complete plan, performs one facts inspection and bind, and threads capture plus the direct focus or type operation through the shared executors. The single-bind regression is non-vacuous and completed checks are green. Code review is clean; iOS and macOS Smoke are the remaining in-progress readiness gates.

@thymikee thymikee added the ready-for-human Valid work that needs human implementation, judgment, or maintainer merge label Aug 21, 2026
@thymikee
thymikee merged commit d0547dc into main Aug 21, 2026
28 checks passed
@thymikee
thymikee deleted the agent/wave4-find-r35 branch August 21, 2026 16:43
@github-actions

Copy link
Copy Markdown
PR Preview Action v1.8.1
Preview removed because the pull request was closed.
2026-08-21 16:43 UTC

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

Labels

ready-for-human Valid work that needs human implementation, judgment, or maintainer merge

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant