Skip to content

Commit f1d4efe

Browse files
authored
refactor(daemon): inline the touch sub-switch into the interaction dispatcher (#2302)
* refactor(daemon): inline the touch sub-switch into the interaction dispatcher interaction-touch.ts was a 31-line pass-through re-switching on press/click/longpress/hover/fill only to call the same five handlers handleInteractionCommands already imports transitively. Inline the five cases into the existing switch and delete the file, dropping one hop from the press/click/longpress/hover/fill dispatch path. Point the response-construction-guard sentinel at interaction-touch-press.ts (a file that still exists) so it keeps watching for a hand-rolled responseData branch across the touch handler set. * fix(daemon): close ADR-0011 guard gap and refresh the stale hop trace PR review findings on #2302: - The ADR-0011 responseData guard scanned files by an interaction-touch*.ts name prefix, which stopped covering the touch dispatch switch once it moved into interaction.ts. Added a targeted second check that extracts the press/click/longpress/hover/fill case bodies from interaction.ts and scans them for hand-rolled responseData, without widening the whole-file scan onto interaction.ts (which also hosts the unrelated `type` command's legitimate local responseData construction). Planted red: reverted one switch case to a hand-rolled responseData construction twice (press, then click) and reran `npx vitest run src/daemon/interaction/internal/__tests__/interaction-response-construction-guard.test.ts`; both times the new test failed with the expected "Hand-rolled interaction responseData found in the touch dispatch switch" message, then passed clean again after reverting. - docs/adr/0019-end-state-hop-trace.md pinned itself to a commit and claimed the press/Android route was untouched by this PR, but the PR's whole point is merging two of that table's traced files (interaction.ts, interaction-touch.ts). Merged the corresponding table rows, renumbered the remaining hops, updated the file/class summary counts and every downstream hop-range reference in the same doc, and did the same for the one line in docs/adr/0019-request-bound-platform-runtime.md that also cited the now-stale 24-hop figure. * test(daemon): hold the touch dispatch switch to structural delegation The ADR-0011 guard extracted each touch case body with a regex that stopped at the first nested case/default, so a hand-rolled responseData placed after a nested switch was invisible. Parse interaction.ts with oxc-parser instead and require every press/click/longpress/hover/fill case to be exactly one return await <handler>(...) where the handler is imported from an interaction-touch*.ts module. Inline planted-red cases cover the nested-switch shape, an inline literal, a foreign callee, and a missing command. * style(daemon): oxfmt the interaction response construction guard * test(daemon): fold the guard's delegation check under the complexity threshold
1 parent 0c8227e commit f1d4efe

6 files changed

Lines changed: 263 additions & 75 deletions

File tree

docs/adr/0019-end-state-hop-trace.md

Lines changed: 36 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -26,39 +26,42 @@ both routes file-by-file at HEAD, and replaces the unauditable numbers.
2626
first call that continues toward the platform call (not every branch — e.g. `handleSnapshotCommands`
2727
routes `alert`/`settings`/`diff`/`wait` too; only the `snapshot` arm is traced), record the file
2828
and the one line/function that hands off to the next hop.
29-
- **Commit measured at**: `132ffe1da296717c268e836fc02558d00e61cfbd` (this branch's base,
30-
`origin/main` fast-forwarded; the fix in this PR does not touch any traced source file).
29+
- **Commit measured at**: originally `132ffe1da296717c268e836fc02558d00e61cfbd` (this branch's
30+
base, `origin/main` fast-forwarded). PR #2302 (refactor(daemon): inline the touch sub-switch
31+
into the interaction dispatcher) merged `interaction-touch.ts` into `interaction.ts`, collapsing
32+
hop 6 into hop 5 of the `press`/Android table below; that table (and the counts and hop
33+
references that depend on it) has been updated in the same PR so the trace does not go stale
34+
on merge.
3135

3236
## `press` / Android: HTTP entry → `adb input tap`
3337

34-
24 files, 7 pass-through, 9 thin, 7 substantive, 1 terminal.
38+
23 files, 6 pass-through, 9 thin, 7 substantive, 1 terminal.
3539

3640
| # | File | Hand-off | Class |
3741
|---|------|----------|-------|
3842
| 1 | `src/daemon/server/http-server.ts` | parses the HTTP request, calls `handleRequest` | thin |
3943
| 2 | `src/daemon/request-router.ts` | `createRequestHandler``runRequestHandlerChain` | pass-through |
4044
| 3 | `src/daemon/request-handler-chain.ts` | routes `command: 'press'` to `runInteractionHandler`, lazy-loads `interaction/index.ts` | thin |
4145
| 4 | `src/daemon/interaction/index.ts` | re-exports the lazy-loaded internal module's `handleInteractionCommands` | pass-through |
42-
| 5 | `src/daemon/interaction/internal/interaction.ts` | `handleInteractionCommands``handleTouchInteractionCommands` | pass-through |
43-
| 6 | `src/daemon/interaction/internal/interaction-touch.ts` | switches `press`/`click`/`longpress`/`hover` to `dispatchTargetedTouchViaRuntime` | thin |
44-
| 7 | `src/daemon/interaction/internal/interaction-touch-press.ts` | admits the touch, tries the direct-iOS fast path (no-op on Android), calls `dispatchRuntimeInteraction` with a `run` callback that calls `runtime.interactions.press` | substantive |
45-
| 8 | `src/daemon/interaction/internal/interaction-touch-prepare.ts` | `prepareTouchDispatch``resolveBoundTouchRuntime` | thin |
46-
| 9 | `src/daemon/touch-runtime.ts` | resolves the touch plan, calls `bind(device, tapPointUse)`, wraps the result as `BoundTouchExecutor.tapPoint` | substantive |
47-
| 10 | `src/daemon/runtime-admission.ts` | `admitRuntimeOperations``requireDeviceBinding(bindDevice)` | thin |
48-
| 11 | `src/daemon/request-runtime-binding.ts` | `bindDevice` → per-device-cached `gateway.bind(...)` | substantive |
49-
| 12 | `src/platform-runtime-gateway.ts` | composed gateway's `loadLocal`: loads the host, calls `module.loadRuntime(host)` | substantive |
50-
| 13 | `src/platform-runtime.ts` | declared boundary (ADR §1/§2): wires `androidRuntimeModule` into `platformRuntimeModules` and supplies `loadHost` | thin |
51-
| 14 | `src/platform-runtime-operation-host.ts` | `createPlatformRuntimeHost` builds `host`, incl. `localInteractors: createLocalApplicationInteractorHost()` | thin |
52-
| 15 | `src/platform-runtime-local-application-interactors.ts` | `resolve()` lazy-imports `core/interactors.ts`, calls `getLocalInteractor` | pass-through |
53-
| 16 | `src/core/interactors.ts` | `getLocalInteractor``getPlugin(device.platform).createInteractor` | pass-through |
54-
| 17 | `src/core/interactors/register-builtins.ts` | plugin registry; the `android` entry lazy-imports `./android.ts` | thin |
55-
| 18 | `src/core/interactors/android.ts` | `createAndroidInteractor` builds the `Interactor`, incl. `tap: (x, y) => pressAndroid(device, x, y)` | substantive |
56-
| 19 | `packages/platform-android/src/index.ts` | declared boundary (ADR §2 `loadRuntime` pairing): lazy-imports `./runtime.ts`, calls `createAndroidPlatformRuntime` | pass-through |
57-
| 20 | `packages/platform-android/src/runtime.ts` | `createAndroidPlatformRuntime`'s `bind()` builds `operations` via `androidInteractionOperations` | substantive |
58-
| 21 | `packages/contracts/src/local-interactor-operation-set.ts` | `bindLocalInteractorOperationSet``bindLocalTouchInteractor` | pass-through |
59-
| 22 | `packages/contracts/src/touch-runtime.ts` | `bindTouch`'s `tapPoint` op resolves the interactor (re-enters hop 15's `resolve`), then `executeGenericPress` calls `interactor.tap(x, y)` | substantive |
60-
| 23 | `packages/platform-android/src/input-actions.ts` | `pressAndroid(device, x, y)` builds the adb argv, calls `runAndroidAdb` | thin |
61-
| 24 | `packages/platform-android/src/adb.ts` | `runAndroidAdb` issues `adb shell input tap <x> <y>` | **terminal** |
46+
| 5 | `src/daemon/interaction/internal/interaction.ts` | `handleInteractionCommands` switches `press`/`click`/`longpress`/`hover` to `dispatchTargetedTouchViaRuntime` (`fill` to `dispatchFillViaRuntime`) | thin |
47+
| 6 | `src/daemon/interaction/internal/interaction-touch-press.ts` | admits the touch, tries the direct-iOS fast path (no-op on Android), calls `dispatchRuntimeInteraction` with a `run` callback that calls `runtime.interactions.press` | substantive |
48+
| 7 | `src/daemon/interaction/internal/interaction-touch-prepare.ts` | `prepareTouchDispatch``resolveBoundTouchRuntime` | thin |
49+
| 8 | `src/daemon/touch-runtime.ts` | resolves the touch plan, calls `bind(device, tapPointUse)`, wraps the result as `BoundTouchExecutor.tapPoint` | substantive |
50+
| 9 | `src/daemon/runtime-admission.ts` | `admitRuntimeOperations``requireDeviceBinding(bindDevice)` | thin |
51+
| 10 | `src/daemon/request-runtime-binding.ts` | `bindDevice` → per-device-cached `gateway.bind(...)` | substantive |
52+
| 11 | `src/platform-runtime-gateway.ts` | composed gateway's `loadLocal`: loads the host, calls `module.loadRuntime(host)` | substantive |
53+
| 12 | `src/platform-runtime.ts` | declared boundary (ADR §1/§2): wires `androidRuntimeModule` into `platformRuntimeModules` and supplies `loadHost` | thin |
54+
| 13 | `src/platform-runtime-operation-host.ts` | `createPlatformRuntimeHost` builds `host`, incl. `localInteractors: createLocalApplicationInteractorHost()` | thin |
55+
| 14 | `src/platform-runtime-local-application-interactors.ts` | `resolve()` lazy-imports `core/interactors.ts`, calls `getLocalInteractor` | pass-through |
56+
| 15 | `src/core/interactors.ts` | `getLocalInteractor``getPlugin(device.platform).createInteractor` | pass-through |
57+
| 16 | `src/core/interactors/register-builtins.ts` | plugin registry; the `android` entry lazy-imports `./android.ts` | thin |
58+
| 17 | `src/core/interactors/android.ts` | `createAndroidInteractor` builds the `Interactor`, incl. `tap: (x, y) => pressAndroid(device, x, y)` | substantive |
59+
| 18 | `packages/platform-android/src/index.ts` | declared boundary (ADR §2 `loadRuntime` pairing): lazy-imports `./runtime.ts`, calls `createAndroidPlatformRuntime` | pass-through |
60+
| 19 | `packages/platform-android/src/runtime.ts` | `createAndroidPlatformRuntime`'s `bind()` builds `operations` via `androidInteractionOperations` | substantive |
61+
| 20 | `packages/contracts/src/local-interactor-operation-set.ts` | `bindLocalInteractorOperationSet``bindLocalTouchInteractor` | pass-through |
62+
| 21 | `packages/contracts/src/touch-runtime.ts` | `bindTouch`'s `tapPoint` op resolves the interactor (re-enters hop 14's `resolve`), then `executeGenericPress` calls `interactor.tap(x, y)` | substantive |
63+
| 22 | `packages/platform-android/src/input-actions.ts` | `pressAndroid(device, x, y)` builds the adb argv, calls `runAndroidAdb` | thin |
64+
| 23 | `packages/platform-android/src/adb.ts` | `runAndroidAdb` issues `adb shell input tap <x> <y>` | **terminal** |
6265

6366
## `snapshot` / iOS Simulator (XCTest): HTTP entry → runner fetch
6467

@@ -97,11 +100,12 @@ The façade PRs the plan sheet named as a candidate explanation (#2178, #2222, #
97100
**before** `e624ef9d3f` — the commit the ADR cites for its 38/29 measurement — so they cannot be
98101
why that count is higher than this one; they were already in effect when 38/29 was recorded.
99102

100-
This trace lands at 24 hops for both routes. The ADR text names no ordered chain, command/artifact,
101-
or counting definition for 38/29, so the discrepancy cannot be resolved against it. The most likely
102-
explanation is a different counting unit (for example, named exports or every static/type import
103-
touched rather than distinct production files on the call path). **Treat 38 and 29 as superseded by
104-
the auditable 24/24 measured here**, not as a second data point to reconcile.
103+
This trace lands at 23 hops for `press`/Android and 24 hops for `snapshot`/iOS. The ADR text names
104+
no ordered chain, command/artifact, or counting definition for 38/29, so the discrepancy cannot be
105+
resolved against it. The most likely explanation is a different counting unit (for example, named
106+
exports or every static/type import touched rather than distinct production files on the call
107+
path). **Treat 38 and 29 as superseded by the auditable 23/24 measured here**, not as a second data
108+
point to reconcile.
105109

106110
## The ≤14 target
107111

@@ -110,22 +114,22 @@ Splitting each traced route into stages:
110114
- **Router spine** (shared by every command): hops 1-3, `http-server.ts``request-router.ts`
111115
`request-handler-chain.ts`. 3 files, fixed.
112116
- **Admission/binding** (resolve the session's device, admit the plan, obtain a bound
113-
capture/tap closure): press hops 4-11 (8 files, 2 substantive); snapshot hops 4-9 (6 files, 3
117+
capture/tap closure): press hops 4-10 (7 files, 2 substantive); snapshot hops 4-9 (6 files, 3
114118
substantive).
115119
- **Gateway resolution** (the declared `platform-runtime.ts` boundary and its `loadRuntime`
116-
pairing): hops 12-14/10-13 (3 files, 2 substantive) on both routes — these files are pinned as
120+
pairing): hops 11-13/10-13 (3 files, 2 substantive) on both routes — these files are pinned as
117121
boundaries by Decision §1/§2 and do not collapse regardless of target.
118122
- **Interactor resolution** (the local-interactor plugin lookup, a second object graph parallel
119123
to the operations bind above): 3 pass-through/thin files on both routes.
120-
- **Platform glue + terminal call**: press hops 18-24 (7 files, 4 substantive incl. terminal);
124+
- **Platform glue + terminal call**: press hops 17-23 (7 files, 4 substantive incl. terminal);
121125
snapshot hops 20-24 (5 files, 5 substantive incl. terminal, because the XCTest runner protocol
122126
— session lifecycle, recycle budget, transport — has no Android equivalent to `adb`'s
123127
single-process-call shape).
124128

125129
Collapsing every pass-through and thin file in the admission/binding and interactor-resolution
126130
stages down to one hop each (folding `interaction-touch-prepare.ts` into `interaction-touch-press.ts`,
127131
`runtime-admission.ts` into `touch-runtime.ts`, the interactor-resolution three-file chain into
128-
one lookup, etc.) removes roughly 10 files from `press` (24~14) and roughly 9 from `snapshot`
132+
one lookup, etc.) removes roughly 9 files from `press` (23~14) and roughly 9 from `snapshot`
129133
(24 → ~15), leaving mostly the substantive hops plus the fixed spine and declared boundaries.
130134

131135
That arithmetic makes ≤14 plausible for `press`/Android under an aggressive but not obviously

docs/adr/0019-request-bound-platform-runtime.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -878,7 +878,7 @@ there, not about retiring the directory.
878878
**Entry-to-platform hop count.** Corrected 2026-09-03: the counting definition, ordered chains,
879879
and commit for this measurement are in
880880
[`0019-end-state-hop-trace.md`](./0019-end-state-hop-trace.md), which supersedes the number
881-
below. A file-by-file re-trace at HEAD measured 24 hops for both `press`/Android and
881+
below. A file-by-file re-trace at HEAD measured 23 hops for `press`/Android and 24 hops for
882882
`snapshot`/iOS. The previously stated 38/29 named no ordered chain, counting definition, or
883883
artifact and does not reproduce; treat it as superseded, not as a second data point.
884884
`src/platform-runtime.ts` (the

0 commit comments

Comments
 (0)