pup-bridge-macos
Public Made by Adomby adom
The pup for macOS SKILL PACK: the AI skills that teach a cloud/container AI how to drive the macOS Puppeteer (pup) bridge (pup_* verbs): the everyday `pup` skill plus focused sub-skills for windows/sessions/tabs, screenshots+recording, credentials, lanes, settings, browsers+Chrome install, vendor logins and the Adom wiki. Installs into ~/.claude/skills + ~/.codex/skills. DOCS ONLY: this is NOT the bridge runtime. The bridge itself is one signed macOS binary that Adom Bridge for macOS installs an
name: pup-why-cft user-invocable: true description: "Parent skill: pup. MAINTAINER detail (a user hears 'pup window'; do not volunteer the build, pup-bridge#114): WHY pup drives its own automation build of Chrome instead of installed Chrome/Edge, the five dated reasons (automation banner, --load-extension removal being the forcing one, version determinism, none of the consumer chrome, binary isolation), the honest trade-offs (~160 MB first fetch, the window title, no Widevine), and what did NOT change (profiles/logins are pup-owned either way). The policy flipped twice; docs/WHY-CFT.md on the wiki page is the dated canonical record and this skill is its AI-discoverable form. Read before questioning the browser choice, proposing native-first, or explaining a 'Google Chrome for Testing' window title to a user. Trigger words: why chrome for testing, why CfT, why not real chrome, why not installed chrome, why not edge, chrome for testing reasons, automation banner, controlled by automated test software, load-extension removed, browser policy pup, native-first, cft vs chrome, what is chrome for testing, google chrome for testing in task manager, widevine pup, drm pup."
Why pup drives Chrome for Testing (the five reasons)
Policy since 2026-08-10 (pup 2.0.53+), and it is final. The policy flipped twice, early pup
was CfT-based, mid-2026 flipped to "drive the browser already on the machine" for instant first
runs, and production experience flipped it back for good. The dated technical record is
docs/WHY-CFT.md on the adom/pup-bridge wiki page (written so the reasons outlive the people
who remember them); this skill is the same content in the form an AI thread can discover. If you
are about to propose "just use the installed Chrome", these are the five things that proposal
re-breaks.
1. The automation banner you cannot turn off
The moment pup does real automation in branded Chrome, synthetic cursor, login fill, captcha
assist, Chrome asserts the infobar: "Chrome is being controlled by automated test software."
It blips in and out as automation starts and stops, reflowing the viewport each time, and it is
deliberately hard to suppress in the branded build: stripping --enable-automation changes input
and focus semantics, and newer branded builds re-assert automation indicators when DevTools
attaches regardless. Users watched the banner flicker over their windows all day. CfT is the build
Google ships FOR automation: no banner arms race.
2. Extensions: the FORCING reason
Branded Chrome 137 (mid-2025) removed --load-extension specifically to stop silent
side-loading. Verified live on branded 148 (2026-08-10), every alternative route for the Adom Pup
toolbar failed or was vetoed:
- CDP
Extensions.loadUnpackedneeds a--remote-debugging-pipelaunch, incompatible with pup's detached-launch architecture (windows outlive the bridge and are re-adopted over the port), and the install is session-scoped anyway. - Driving
chrome://extensions+ the native folder dialog puts a file dialog on the user's screen. Vetoed. - The remaining branded paths are a Web Store click per profile, or an enterprise force-install policy that brands the browser "managed by your organization".
CfT still honors --load-extension, so the toolbar is just a launch argument: every window is
born with it pinned, no install step exists. This gap alone justifies the policy.
3. Version determinism (with a HARD limit: the pin must track the user's Chrome)
Updated 2026-09-14, twice, and the second update retracts the first. Determinism is still what makes pup's tests reproducible. An earlier version of this section claimed that a stale CfT "reads as a bot", citing analog.com answering HTTP 403 from AkamaiGHost on CfT 148 and serving the real page on CfT 152.
That claim is withdrawn. It was not a controlled comparison. The CfT 148 trials were raw
shell_execute Chrome launches carrying a hand-copied flag list; the CfT 152 result came from pup
itself. Those two differ in far more than the build number (pup passes --disable-infobars, a
different feature-disable set and the identity shim; the raw launches visibly showed the automation
infobar). Re-measured with screenshots on 2026-09-14, analog.com rendered fine in pup on CfT 152 on the
adom-you profile. The intended fresh-jar arm was not fresh: before 2.0.659 isolated:true kept the adom-you
profile (pup-bridge#112). Since 2.0.659 it is its own extension-lane browser with an empty jar, so the arm can be
re-run; until it is, profile state remains UNTESTED.
What is actually established: sites in this class load when driven through pup's own launch path.
Which element of that launch is decisive has NOT been isolated, and it has NOT been shown to be the
CfT version. typeof bmak === "undefined" on a denied page is a SYMPTOM, not a cause: the Access
Denied page carries no sensor script.
The rule: keep CFT_BUILD_ID within about one milestone of the user's installed Chrome as
hygiene, because a large version gap is a plausible signal and costs nothing to avoid. Do NOT present
it to a user as the reason a vendor site loads or fails. pup fetches the pinned build in the
background when it is missing, so a bump does land on the fleet.
3. Version determinism
CfT is pinned to an exact build and has no auto-updater. Branded Chrome
updates itself under running profiles on Google's schedule, so CDP behavior can drift mid-session
and two windows opened an hour apart can be driving different browser versions. Deterministic
versions are what make pup's ralph tests reproducible and a CDP bug a fixable fact instead of a
moving target. pup owns CfT's update cadence via its own self-heal (src/chrome.js).
4. None of the consumer chrome
No first-run bubbles, no default-browser nagging, no profile-import prompts, no sign-in/sync pressure, and no Finch field-trial experiments silently flipping behavior per machine. Branded Chrome runs server-assigned experiments; CfT gives every machine the same browser.
5. Isolation from the user's own browser
A different binary means a different process tree: the user's real Chrome can update, crash, or be closed without touching a single pup window, and pup's hidden browser app never tangles with theirs. Profiles were always pup-owned; CfT extends the separation to the binary itself.
The trade-offs: stated honestly (do not hide these from a user)
- First-run download (~160 MB) on a box without CfT cached,
pup_prewarmdoes it ahead of time, and an installed Google Chrome (or Edge) in /Applications serves as the spawn-verified fallback meanwhile, so an open never blocks on the fetch. - The window title, the Dock and Activity Monitor say "Google Chrome for Testing". Expected; the rendered window is otherwise identical. Tell a user asking "what is this browser?" exactly that.
- No Widevine DRM, protected streaming video will not play in pup. That is an nb/nbe job anyway (the user's real browser, the user's real identity).
- A user who explicitly pins branded Chrome (
pup_use {browser:"chrome"}) keeps everything working EXCEPT the toolbar extension and infobar suppression; pup says so when it matters.
What did NOT change with the switch
Profiles, cookies, and logins live in pup's own userDataDirs, independent of the driving binary. The shared adom-you credential store, window ownership, background parking, and recovery are all binary-agnostic; the same profile simply renders under the automation build.
How the policy is implemented (so nobody "fixes" the code backwards)
The browser crate (rust/browser) still lists installed Chrome → Edge → CfT as candidates, on
purpose: that is the fallback ladder, not the policy. The pinned/cached CfT default promoted to the
front of the list is what implements CfT-first. A healthy box reports
browserKind: "chrome-for-testing" in pup_readiness.
---
name: pup-why-cft
user-invocable: true
description: "Parent skill: pup. MAINTAINER detail (a user hears 'pup window'; do not volunteer the build, pup-bridge#114): WHY pup drives its own automation build of Chrome instead of installed Chrome/Edge, the five dated reasons (automation banner, --load-extension removal being the forcing one, version determinism, none of the consumer chrome, binary isolation), the honest trade-offs (~160 MB first fetch, the window title, no Widevine), and what did NOT change (profiles/logins are pup-owned either way). The policy flipped twice; docs/WHY-CFT.md on the wiki page is the dated canonical record and this skill is its AI-discoverable form. Read before questioning the browser choice, proposing native-first, or explaining a 'Google Chrome for Testing' window title to a user. Trigger words: why chrome for testing, why CfT, why not real chrome, why not installed chrome, why not edge, chrome for testing reasons, automation banner, controlled by automated test software, load-extension removed, browser policy pup, native-first, cft vs chrome, what is chrome for testing, google chrome for testing in task manager, widevine pup, drm pup."
---
# Why pup drives Chrome for Testing (the five reasons)
**Policy since 2026-08-10 (pup 2.0.53+), and it is final.** The policy flipped twice, early pup
was CfT-based, mid-2026 flipped to "drive the browser already on the machine" for instant first
runs, and production experience flipped it back for good. The dated technical record is
**`docs/WHY-CFT.md`** on the `adom/pup-bridge` wiki page (written so the reasons outlive the people
who remember them); this skill is the same content in the form an AI thread can discover. If you
are about to propose "just use the installed Chrome", these are the five things that proposal
re-breaks.
## 1. The automation banner you cannot turn off
The moment pup does real automation in branded Chrome, synthetic cursor, login fill, captcha
assist, Chrome asserts the infobar: **"Chrome is being controlled by automated test software."**
It blips in and out as automation starts and stops, reflowing the viewport each time, and it is
deliberately hard to suppress in the branded build: stripping `--enable-automation` changes input
and focus semantics, and newer branded builds re-assert automation indicators when DevTools
attaches regardless. Users watched the banner flicker over their windows all day. CfT is the build
Google ships FOR automation: no banner arms race.
## 2. Extensions: the FORCING reason
Branded Chrome 137 (mid-2025) **removed `--load-extension`** specifically to stop silent
side-loading. Verified live on branded 148 (2026-08-10), every alternative route for the Adom Pup
toolbar failed or was vetoed:
- CDP `Extensions.loadUnpacked` needs a `--remote-debugging-pipe` launch, incompatible with pup's
detached-launch architecture (windows outlive the bridge and are re-adopted over the port), and
the install is session-scoped anyway.
- Driving `chrome://extensions` + the native folder dialog puts a file dialog on the user's
screen. Vetoed.
- The remaining branded paths are a Web Store click per profile, or an enterprise force-install
policy that brands the browser "managed by your organization".
CfT still honors `--load-extension`, so the toolbar is just a **launch argument**: every window is
born with it pinned, no install step exists. This gap alone justifies the policy.
## 3. Version determinism (with a HARD limit: the pin must track the user's Chrome)
**Updated 2026-09-14, twice, and the second update retracts the first.** Determinism is still what
makes pup's tests reproducible. An earlier version of this section claimed that a stale CfT "reads as
a bot", citing analog.com answering HTTP 403 from AkamaiGHost on CfT 148 and serving the real page on
CfT 152.
**That claim is withdrawn. It was not a controlled comparison.** The CfT 148 trials were raw
`shell_execute` Chrome launches carrying a hand-copied flag list; the CfT 152 result came from pup
itself. Those two differ in far more than the build number (pup passes `--disable-infobars`, a
different feature-disable set and the identity shim; the raw launches visibly showed the automation
infobar). Re-measured with screenshots on 2026-09-14, analog.com rendered fine in pup on CfT 152 on the
adom-you profile. The intended fresh-jar arm was not fresh: before 2.0.659 `isolated:true` kept the adom-you
profile (pup-bridge#112). Since 2.0.659 it is its own extension-lane browser with an empty jar, so the arm can be
re-run; until it is, profile state remains UNTESTED.
**What is actually established:** sites in this class load when driven through pup's own launch path.
Which element of that launch is decisive has NOT been isolated, and it has NOT been shown to be the
CfT version. `typeof bmak === "undefined"` on a denied page is a SYMPTOM, not a cause: the Access
Denied page carries no sensor script.
**The rule:** keep `CFT_BUILD_ID` within about one milestone of the user's installed Chrome as
hygiene, because a large version gap is a plausible signal and costs nothing to avoid. Do NOT present
it to a user as the reason a vendor site loads or fails. pup fetches the pinned build in the
background when it is missing, so a bump does land on the fleet.
## 3. Version determinism
CfT is pinned to an exact build and **has no auto-updater**. Branded Chrome
updates itself under running profiles on Google's schedule, so CDP behavior can drift mid-session
and two windows opened an hour apart can be driving different browser versions. Deterministic
versions are what make pup's ralph tests reproducible and a CDP bug a fixable fact instead of a
moving target. pup owns CfT's update cadence via its own self-heal (`src/chrome.js`).
## 4. None of the consumer chrome
No first-run bubbles, no default-browser nagging, no profile-import prompts, no sign-in/sync
pressure, and no Finch field-trial experiments silently flipping behavior per machine. Branded
Chrome runs server-assigned experiments; CfT gives every machine the same browser.
## 5. Isolation from the user's own browser
A different binary means a different process tree: the user's real Chrome can update, crash, or be
closed without touching a single pup window, and pup's hidden browser app never tangles with
theirs. Profiles were always pup-owned; CfT extends the separation to the binary itself.
## The trade-offs: stated honestly (do not hide these from a user)
- **First-run download** (~160 MB) on a box without CfT cached, `pup_prewarm` does it ahead of
time, and an installed Google Chrome (or Edge) in /Applications serves as the spawn-verified fallback
meanwhile, so an
open never blocks on the fetch.
- The window title, the Dock and Activity Monitor say **"Google Chrome for Testing"**. Expected; the rendered
window is otherwise identical. Tell a user asking "what is this browser?" exactly that.
- **No Widevine DRM**, protected streaming video will not play in pup. That is an nb/nbe job
anyway (the user's real browser, the user's real identity).
- A user who explicitly pins branded Chrome (`pup_use {browser:"chrome"}`) keeps everything
working EXCEPT the toolbar extension and infobar suppression; pup says so when it matters.
## What did NOT change with the switch
Profiles, cookies, and logins live in pup's own userDataDirs, independent of the driving binary.
The shared adom-you credential store, window ownership, background parking, and recovery are all
binary-agnostic; the same profile simply renders under the automation build.
## How the policy is implemented (so nobody "fixes" the code backwards)
The browser crate (`rust/browser`) still lists installed Chrome → Edge → CfT as candidates, **on
purpose: that is the fallback ladder**, not the policy. The pinned/cached CfT default promoted to the
front of the list is what implements CfT-first. A healthy box reports
`browserKind: "chrome-for-testing"` in `pup_readiness`.