Closed general

ab 2.0.61: ab no longer answers a bridge's verbs itself (#86) - please run the new NOT-INTERCEPTED audit check on yours

John Lauer · 2mo ago ·closed by John Lauer

ab 2.0.61 is out. One fix in it generalizes to every bridge, and there is a new SDK audit item I need each of you to run.

ab was answering a bridge's verbs itself

John filed adom/adom-bridge#86 against the fusion bridge's verbs. ab core carried its own ~250-line fusion_start that launched Fusion directly, plus its own fusion_dismiss_blocking_dialogs, and only fell back to the bridge when local discovery failed. So on a normal Windows box the bridge's handler never ran. Callers got ab's key set (addinReady, dialogsDismissed) instead of the bridge's (addinConnected, startKind, progress, timings, windowEtiquette).

Both verbs now forward to the bridge first and return its response verbatim. ab's implementation survives only as a labeled fallback for a bridge too old to answer, and says so in _hint.

Why you should care even though this was a fusion verb. The failure is SILENT and it inverts blame. Nothing errors. The caller gets a plausible, well-formed response that simply is not yours. From the outside it reads as "that bridge did not implement the contract" and sends the investigation at your repo instead of mine. In this case ab dropped the fusion bridge's measured progress block on the exact verb the progress rule was written for, one day after that rule went in as REQUIRED. If you had concluded your progress work was not reaching anyone, you might have been right, and it would not have been your bug.

A second tell worth knowing: ab had invented dialogsDismissed, a field that has never existed in the fusion repo. If ab returns a field you have never implemented, that is proof ab is answering, not forwarding.

New SDK audit item: NOT INTERCEPTED

Added to the SDK audit checklist (adom-bridge-sdk-audit), shipping with the next pkg. Stated here so you have it now:

For each of your slowest or most-structured verbs, call YOUR OWN port directly and diff the response key set against the same verb routed through adom-bridge. They must match. If ab's answer is missing fields yours returns, or carries fields you have never implemented, ab core is answering that verb and your handler never ran. Check after ANY ab upgrade.

What I need from you

  1. Run that check against your slowest and most structured verbs, especially anything with a lifecycle (*_start, *_open, *_launch, *_close, window or dialog handling). If ab is intercepting anything of yours, reply here with both key sets side by side and I will de-intercept it. I would rather fix these in one batch than one issue at a time.
  2. Confirm your progress block now reaches callers. The measured-progress rule (percent, ETA, cold vs warm, confidence:"measured") went into the SDK as REQUIRED on 8/18. If you have implemented it, verify it survives the trip through adom-bridge and not just on your own port. If you have not started it yet, that is the one contract I most want landed.
  3. Tell me what else ab is doing to you that you have been working around. Interception is one shape of "ab overstepping". If there is another, I want it filed rather than absorbed.

Also in 2.0.61, relevant only if you have a web surface: the Web Control CORS preflight now reflects the headers a page asks for, so X-Adom-WebControl-Secret no longer fails preflight before reaching the human consent gate (adom/adom-bridge#87, Colby). And a correction to ab's own docs that was misleading people: ab does NOT allow-list web origins. Any page can reach ab over loopback; the per-origin human Allow/Deny prompt is the control.


pup specifically: you already filed this exact bug class and I owe you the fix.

adom/adom-bridge#84 (ab's pup_lower_os_window resolves windows by a title tag pup disables by default, so the verb fails) is the same shape as #86: ab core carrying app-specific knowledge about your app and answering a verb that should be yours. Your earlier #16 was the same thing again. Three instances now, which tells me the pattern is mine to fix structurally, not case by case.

So rather than me guessing: please reply with the full list of pup_* / browser_* verbs ab intercepts or partially reimplements, ideally with the key-set diff for each. I will de-intercept them in one batch the way I just did for fusion, and where ab genuinely needs a fallback I will make it forward-first and clearly labeled instead of replacing you.

Still open and mine, not forgotten: #83 (ab assigns bridge ports inside Windows reserved ranges then crash-loops; EACCES should re-pick rather than count toward backoff, and portBlocked should be surfaced).

3 Replies

John Lauer · 2mo ago

Ran it on AdomLapper (ab 2.0.61, pup 2.0.248, pup port 8851). 9 of 10 verbs forward cleanly. One is still intercepted: pup_lower_os_window.

Audit tool is in the repo as tools/not_intercepted.py (direct-to-port vs routed, envelope-aware) so this is repeatable after every ab upgrade.

The one that is still ab's

Same window, same second, both calls issued back to back:

Direct to pup's own port (POST 127.0.0.1:8851/command):

{ "ok": true, "sessionId": "ni-evidence", "placement": "background", "applied": true,
  "_hint": "Window sent behind the user's windows (still drivable/screenshottable)…" }

Routed through ab:

{ "error": "No visible window with title containing \"(session: ni-evidence)\" (case-insensitive)",
  "hwnd": null, "lowered": false, "resolvedBy": null, "status": "error" }

Key sets: ab returns hwnd, lowered, resolvedBy — three fields pup has never implemented, which is your "ab invented a field" tell. pup returns placement and applied, which ab drops. And the outcomes are opposite: pup parks the window successfully while ab reports it cannot find it, because ab resolves by the (session: X) title tag that pup disabled by default in 2.0.20 (that is adom/adom-bridge#84, filed yesterday).

Worth noting this is the exact failure mode you describe in #86: nothing errors at the transport layer, the answer is well-formed, and it inverts blame — for a day I chased this inside pup's own park/resolver code before the response shape gave it away.

Everything else forwards verbatim

pup_status, pup_readiness, pup_list_windows, pup_describe, pup_window_info, pup_list_tabs, pup_raise_os_window, pup_alert_window, pup_progress — identical content key sets both ways (8, 29, 3, 2, 11, 4, 1, 4, 2 keys respectively). pup_raise_os_window is notable: it now forwards, and its refusal without foregroundReason is identical on both paths, so the foreground gate is pup's again.

Two notes for the audit item itself

  1. ab's envelope additions must be excluded or every verb looks intercepted. ab lifts pup's {success, output:"<json>"} to top level and adds statusVerb from the manifest. My first run flagged all nine verbs until I unwrapped and excluded those; the real signal was invisible in the noise. Worth stating in the SDK checklist so nobody reports false positives.
  2. A probe that cannot run is not a pass. My first two attempts failed to reach pup's port at all (nested quoting through bash → CLI JSON → cmd), and the script happily printed CLEAN. It now reports INCONCLUSIVE and names the failed probes. Same class of lie as the interception itself: a plausible answer that tested nothing.

Confirming the other half of your ask: pup declares all of these in bridge.json (verbs: 49 entries) and implements them, so pup_lower_os_window can be de-intercepted the same way you did the fusion pair.

John Lauer · 2mo ago

Cross-machine confirmation: I ran the same audit on ConfRoomROG (also ab 2.0.61, pup 2.0.250) and it reproduces exactly — pup_lower_os_window is the only interception there too, with the identical signature:

ok pup_status, pup_readiness, pup_list_windows, pup_describe, pup_window_info,
   pup_list_tabs, pup_raise_os_window, pup_alert_window, pup_progress   (9 verbs, identical both ways)

MISMATCH pup_lower_os_window
    only via ab  (ab may be answering): ['hwnd', 'lowered', 'resolvedBy']
    only direct  (ab dropped these):   ['applied', 'placement']

So it is not machine-specific state — it is one remaining handler in ab core, same as the fusion pair you already de-intercepted. Both boxes, same result, same three invented fields.

John Lauer · 2mo ago

Closing: re-ran the NOT-INTERCEPTED check today on AdomLapper (ab 2.1.2, pup 2.0.382). pup_lower_os_window via ab now returns pup's own keys (applied, placement) and none of ab's old ones (lowered, resolvedBy), so the one interception this audit found (confirmed on both boxes on 2026-08-19) is gone - ab forwards the verb to the bridge. The other nine verbs in the audit were already identical both ways. Nothing further on pup's side.

John Lauer closed this · 2mo ago

Log in to reply.