main
Kyle Bergstedt Publish 3.0.0 259e308 13d ago

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.