Skip to content

refactor: codebase dedup pass — 271 lines deleted#20

Merged
pinetops merged 4 commits into
mainfrom
refactor/dedup
May 11, 2026
Merged

refactor: codebase dedup pass — 271 lines deleted#20
pinetops merged 4 commits into
mainfrom
refactor/dedup

Conversation

@pinetops
Copy link
Copy Markdown
Member

@pinetops pinetops commented May 4, 2026

Summary

Four sequential dedup commits, all locally test-verified (180 unit + 124 LV + 153 lightpanda + 12-of-13 clean CDP mc4 runs).

Commits in order

  1. dedup chore(main): release 0.31.0 #1: delete dead `BiDiClient.{prepare_patch, await_patch, drain_patches, await_liveview_connected}` and the four large JS module attributes that backed them. Superseded by `Wallabidi.LiveViewAware` (protocol-agnostic). `BiDiClient.visit/2` was the only internal caller; switched to `LiveViewAware.await_liveview_connected`. −200 lines.

  2. dedup Session pool doesn't propagate sandbox metadata on checkout #3: hoist `root_session/1` and `bidi_pid/1` to public `Wallabidi.Element` functions. CDPClient/BiDiClient/ChromeCDP each had their own private 2-clause recursive duplicate. −12 lines net.

  3. dedup sandbox_case not detected at compile time — @sandbox_case_available is false #2: delete the unreachable lazy-ops queue (`SessionProcess.append_ops`/`drain_ops`/`pending_ops` field, `CDPClient.maybe_flush_pending`/`flush_pending`/4-arg `do_post_click`). The queue had zero callers. −118 lines net.

    Subtle: `maybe_flush_pending` was load-bearing despite the queue being empty — the synchronous `GenServer.call(:drain_ops)` it issued was acting as a mailbox barrier on the SessionProcess, ordering CDP sends against pending binding events. A first attempt at deletion introduced a flake regression (2 failures on a previously clean baseline; bisected to this commit). Fix: keep the barrier explicit via a new `Wallabidi.SessionProcess.sync_barrier/1` (a no-op `GenServer.call` whose value is its mailbox-serialization side-effect). `send_cdp_session` calls it before each CDP RPC.

  4. dedup XPath Query.button cannot classify phx-click — causes incorrect await_patch #5: collapse five near-clones in cdp_client.ex (`send_cdp_session`, `send_cdp_raw`, `cast_cdp_command`, `cast_release`, the `if flat_session_id` ladder in each) into one `dispatch(session, kind, method, params)` helper where `kind in [:send, :cast]`. Each wrapper becomes a one-line call. −10 lines net.

Total: 271 lines deleted

```
lib/wallabidi/bidi_client.ex | 207 +-------
lib/wallabidi/cdp_client.ex | 201 ++-------
lib/wallabidi/chrome_cdp.ex | 5 +-
lib/wallabidi/element.ex | 20 ++-
lib/wallabidi/session_process.ex | 62 ++--
5 files changed, 112 insertions(+), 383 deletions(-)
```

What's NOT in this PR

  • Bootstrap JS canonicalization (originally dedup await_selector and await_patch hang indefinitely in 0.1.25 #4): the three implementations of `classify`/`click`/`isVisible` JS in `bootstrap.ex`/`cdp/pipeline.ex`/`cdp_client.ex` have legitimate differences per call shell (`function(el)` vs `function()` with `this` vs IIFE). The "drift" between them turned out to be intentional (e.g. bootstrap's head-descendant rule for Lightpanda). Skipping.
  • `Wallabidi.BiDi.WebSocketClient` rename to `Wallabidi.WebSocketClient` (it's protocol-agnostic, the BiDi prefix lies). Pure cosmetic; can be a follow-up.

Test plan

  • CI green
  • CDP integration stable post-merge (no flake regression vs baseline)
  • BiDi integration stable post-merge

🤖 Generated with Claude Code

pinetops and others added 4 commits May 3, 2026 17:41
Four BiDiClient functions were superseded by Wallabidi.LiveViewAware
and had no live callers outside BiDiClient itself:

* prepare_patch/1, await_patch/2, drain_patches/2 — all replaced by
  LiveViewAware.{prepare_patch, await_patch, drain_patches} which
  use Protocol.eval/eval_async (protocol-agnostic).
* await_liveview_connected/2 — replaced by
  LiveViewAware.await_liveview_connected/2.

The only internal caller, BiDiClient.visit/2, now calls
LiveViewAware.await_liveview_connected directly — same shape, zero
behaviour change.

Also drops four large module attributes that held the corresponding
JS bodies (@prepare_patch_js, @await_patch_js, @drain_patches_js,
@await_lv_connected_js).

Net: 200 lines deleted from bidi_client.ex.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
\`root_session/1\` and \`bidi_pid/1\` were duplicated across
CDPClient, BiDiClient, and ChromeCDP — each module had its own
private 2-clause recursive helper that walked
\`Element.parent → ... → Session\` and pulled the field. Promote
both to public functions on \`Wallabidi.Element\` so there's one
canonical place for parent-chain traversal.

The three private definitions become one-line delegations.

(WebSocketClient rename — also flagged in the dedup audit — deferred
to its own PR. Pure cosmetic; not bundled here to keep this diff
mechanical.)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The lazy-ops queue (\`SessionProcess.append_ops/5\`,
\`drain_ops/1\`, the \`pending_ops\` field, and the
\`maybe_flush_pending\`/\`flush_pending\` plumbing in CDPClient) had
zero callers — no path in lib/ ever invoked \`append_ops\`, so the
queue was always empty and \`maybe_flush_pending\` was a no-op called
on every \`send_cdp_session\`.

The \`CDPClient.do_post_click/3-4\` was only called from
\`flush_pending\`, so it was equally dead. It was also a near-clone of
\`Browser.do_post_click/5\` but missing the \`await_ack\` slow-handle_event
fix.

**Subtle:** \`maybe_flush_pending\` was load-bearing despite the queue
being empty. The synchronous \`GenServer.call(pid, :drain_ops)\` it
issued was acting as a *mailbox barrier* on the SessionProcess —
draining any pending binding events / page_ready notifications BEFORE
the CDP send proceeded. Naive deletion (a previous attempt) introduced
a flake regression: 2-failure runs on a previously clean baseline.

Replace with an explicit, named barrier:
\`Wallabidi.SessionProcess.sync_barrier/1\`. Same mechanism (a no-op
\`GenServer.call\` that returns \`:ok\`), but the intent is now visible
at the callsite. \`send_cdp_session\` calls it before each CDP RPC.

Bisect verified: this is sufficient to restore the clean baseline
(9 consecutive clean CDP mc4 runs after the change vs. 1-2 failure
runs without the barrier).

Net deletion: 121 lines minus the new barrier helper.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
\`cdp_client.ex\` had five near-clones around the same
\`flat_session_id\` branch:
  - \`send_cdp_session/3\`
  - \`send_cdp_raw/2\`
  - \`cast_cdp_command/3\`
  - \`cast_release/2\`
  - \`cast_cdp_with_session/4\` (only-pid variant)

Each repeated \`pid = bidi_pid(session); session_id =
effective_session_id(session); if flat?, do: cast_command_flat(...)
else: Map.put(:sessionId, ...)\` with minor differences. Three of
them had drift: e.g. \`cast_release\` manually merged
\`objectId\` and \`sessionId\` together instead of relying on the
helper that other casts use.

Replace with one \`dispatch(session, kind, method, params)\` helper
where \`kind in [:send, :cast]\`. Each wrapper becomes a one-line
call to dispatch. \`cast_cdp_with_session/4\` (no Session struct
yet, used during bootstrap) keeps its own minimal form.

Behaviour-equivalent — passes unit, lightpanda, and CDP integration.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@pinetops pinetops merged commit 892d0aa into main May 11, 2026
12 checks passed
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