# ADR-124 Playwright Is the Default Tool for PWA Screenshot/Screencast Automation — Claude-in-Chrome Requires Explicit Approval

## Status

Accepted, 2026-07-29.

## Status History

```yaml
status_history:
  - date: 2026-07-29
    status: Accepted
    changed_by: hkl
    reason: |
      Raised while cleaning up RentVeh dev demo data and fixing a Vehicle Master
      display bug from the DL laptop's Claude Code session. That session used
      Claude-in-Chrome (CiC, an interactive-desktop browser-automation tool) to
      verify the fix visually, and separately to work around a broken Shiprocket
      panel flow. hkl pointed out the project already has a working, scriptable,
      headless Playwright toolchain for PWA screenshots and screencasts
      (ADR-064 tutorial pipeline, ADR-022 E2E specs, the walkthroughs/ framework,
      ad-hoc capture.js scripts) and that CiC is a materially costlier and less
      efficient path for this already-solved class of work. Codified as a
      standing rule rather than a one-off correction, since the same choice
      recurs every time a PWA needs a visual check or a demo capture.
    changed_via: adr-kit (360lm), authored directly per hkl's explicit instruction
```

## Context

This codebase already has a mature, headless-capable, scriptable Playwright toolchain for
working with 360LM PWAs visually, built up across several ADRs and reference implementations:

- **ADR-022** — Playwright is the standing E2E test framework, one spec file per PWA.
- **ADR-064** — the five-stage video tutorial pipeline (`scripts.json` → edge-tts →
  Playwright recording → ffmpeg → `tutorial.html`), fully headless end to end.
- **`walkthroughs/` framework** (`/var/www/360lm/walkthroughs/scripts/*.js`, e.g.
  `custodian_tour.js`) — a reusable `WalkthroughRunner` that drives a real PWA with
  `page.goto()`, auto-detects human PIN entry, pulls live DB snapshots over SSH→psql, and
  saves screenshots — read-only, side-effect-free by design.
- **Ad-hoc capture scripts** (e.g. `tutorials/portal-walkthrough/capture.js`) — plain
  `@playwright/test` `chromium` scripts that inject a session via `localStorage` (no manual
  login step) and call `page.screenshot()` across a list of PWA routes.
- **ADR-108 (SiteCap)** — for *third-party, non-360LM* sites specifically, a permanent
  authenticated-capture service, itself built on the same Playwright core (`capture.js`).

None of this requires an interactive desktop browser session. All of it is scriptable,
reproducible, and can run unattended or from a plain SSH session against the VPS.

**The problem this ADR addresses:** despite this toolchain already existing, AI coding
sessions (in particular the DL laptop's Claude Code session, which also has
`mcp__claude-in-chrome__*` — Claude-in-Chrome, "CiC" — wired in) have reached for CiC by
default for one-off PWA visual verification and capture tasks. CiC drives the user's real,
interactive Chrome browser — it requires an actual desktop session, is slower (each action is
a live round-trip against a real browser UI, not a headless scripted call), cannot run
unattended, and ties up the user's own browser window while it runs. For the class of work
this project already has a pipeline for (screenshot a PWA screen, record a tutorial clip,
verify a UI change rendered correctly), CiC is a strictly costlier and less efficient
substitute for tooling that already exists and already works.

This is a **operator/tooling-practice decision**, not a change to any PWA's own architecture —
it governs how AI sessions working on this codebase (from the VPS itself or from the DL
laptop) choose between Playwright and CiC, not how any PWA is built.

## Decision

**Any AI-driven session working on a 360LM PWA — capturing a screenshot, recording a
screencast/tutorial clip, or visually verifying a UI change — must default to the existing
Playwright-based toolchain (ADR-022 E2E specs, ADR-064 tutorial pipeline, the `walkthroughs/`
framework, or a purpose-written headless `capture.js`-style script) rather than an interactive
browser-automation tool such as Claude-in-Chrome.**

Claude-in-Chrome (or an equivalent interactive-desktop browser tool) may be used only when
**both** of the following hold:

1. **The user has explicitly approved its use for that task** (a standing blanket approval is
   not assumed — ask, unless the user has already said "go" for the specific task at hand), and
2. **A Playwright-based approach is genuinely not feasible** for that specific task — e.g. a
   flow requires a human physically present to solve a CAPTCHA in their own authenticated
   session (see ADR-118's human-in-the-loop CAPTCHA relay pattern), or the user specifically
   wants to watch/drive the browser live rather than receive a scripted result.

A Playwright script failing (e.g. a page erroring, a form rejecting input) is not by itself
grounds to fall back to CiC — first determine whether the failure is in the target site/PWA
(as it was for the Shiprocket NDR ticket flow this ADR's context references) rather than in
the automation approach, since CiC re-driving the same broken flow by hand will usually
reproduce the same failure.

**Decision Maker:** hkl

## Implementation Notes

- **Screenshot a PWA screen/state:** write or extend a `capture.js`-style script
  (`@playwright/test`'s `chromium`, inject session via `localStorage` per
  `tutorials/portal-walkthrough/capture.js`'s `injectSupervisor()` pattern where a hub session
  needs to be simulated) rather than opening the page in CiC and using `computer` actions.
- **Record a tutorial/screencast:** follow ADR-064's five-stage pipeline exactly — do not
  hand-roll a CiC-driven recording.
- **Verify a UI fix rendered correctly:** prefer a Playwright screenshot compared against
  expectation, or extend the PWA's existing `tests/*.spec.js` (ADR-022). CiC is acceptable here
  only as a quick manual double-check *after* explicit user approval, not as the primary
  verification method.
- **Read-only guided walkthroughs for a human to watch:** use/extend the `walkthroughs/`
  framework (`WalkthroughRunner`, `walkthroughs/scripts/*.js`) — it already exists for exactly
  this purpose and is side-effect-free by design.
- **Find existing capture scripts before writing a new one:**
  `grep -rl "page.screenshot\|screenshot(" /var/www/360lm --include="*.js" --include="*.py" | grep -v node_modules`
- This decision applies **everywhere an AI session works on this codebase**, not only from the
  VPS — see the DL laptop's own memory (`feedback_dl_playwright_over_cic.md`, laptop-local, not
  part of this repo) for the equivalent laptop-side standing rule, which additionally covers
  non-PWA browser automation and Windows-desktop automation (`windows-mcp`).

## Alternatives Considered

- **Leave tool choice to session judgment, case by case.** Rejected — this is exactly what led
  to CiC being reached for by default despite an existing, cheaper, already-working pipeline;
  an explicit default closes that gap without banning CiC outright for the cases it's
  genuinely needed for.
- **Ban Claude-in-Chrome outright for PWA work.** Rejected — CiC remains the right tool for
  genuinely interactive cases (human-in-the-loop CAPTCHA per ADR-118, or when the user wants to
  watch/drive the browser themselves), so a hard ban would just force a worse workaround
  instead of an approval gate.
- **Require approval for Playwright too, symmetrically.** Rejected — Playwright automation
  against the project's own PWAs, using the project's own established toolchain, is the
  already-accepted default (ADR-022/ADR-064); requiring per-use approval for it would undo the
  point of having a standing pipeline.

## Consequences

**Positive:**
- Visual verification and capture work reuses tooling that is already built, tested, and
  documented (ADR-022, ADR-064, `walkthroughs/`), instead of re-deriving an interactive
  CiC-driven approach each time.
- Headless Playwright scripts are faster, reproducible, and don't tie up the user's live
  desktop browser session while they run.
- The explicit approval gate for CiC keeps the door open for the cases that genuinely need a
  human-observed or human-in-the-loop interactive session (CAPTCHA solving, live demos), rather
  than removing that capability.

**Negative / Trade-offs:**
- Slightly more upfront effort for a genuinely one-off visual check — writing or extending a
  small Playwright script vs. immediately clicking around in CiC. Accepted, since most
  "one-off" checks recur and end up worth scripting anyway.
- Requires the session to correctly judge "is this Playwright-feasible" before reaching for
  CiC — a wrong judgment either under-uses the existing pipeline or wastes time attempting an
  automation approach that was never going to work for a genuinely interactive flow.

**Risks and mitigations:**

| Risk | Mitigation |
|---|---|
| A session treats a target site's own bug (not an automation limitation) as "Playwright infeasible" and jumps to CiC unnecessarily | Confirm the failure mode first (as this ADR's context did for the Shiprocket NDR ticket error) before concluding Playwright can't do it |
| Approval gate becomes a rubber stamp ("CiC needs approval" asked and granted reflexively every time) | The gate's value is in prompting the *feasibility* check, not just the approval click — sessions should state why Playwright doesn't fit before asking |
| New PWA work reinvents a capture script instead of finding/extending an existing one | Implementation Notes' grep command surfaces existing scripts first |

## Related Decisions

- **ADR-022** — Playwright Is the E2E Test Framework. The baseline tooling this ADR defaults
  visual-capture work to.
- **ADR-064** — Video Tutorial Production Pipeline. The reference headless Playwright pipeline
  for screencasts specifically.
- **ADR-108** — Generic Authenticated Site Capture & Analysis Service (SiteCap). Same
  Playwright-core pattern, scoped to third-party (non-360LM) sites rather than this codebase's
  own PWAs.
- **ADR-118** — Courier Tracking Backbone + Human-in-the-Loop CAPTCHA Relay. The concrete
  precedent for when an interactive/human-driven browser session is the *correct* choice over
  a fully headless one — mirrored in this ADR's approval-gate condition 2.

## References

- `/var/www/360lm/walkthroughs/scripts/custodian_tour.js` — reusable, side-effect-free
  Playwright walkthrough/screenshot framework.
- `/var/www/360lm/tutorials/portal-walkthrough/capture.js` — plain Playwright screenshot script
  with session injection via `localStorage`.
- `/var/www/360lm/tests/custodian_tutorial_screenshots.spec.js` — Playwright recording spec
  referenced by ADR-064.
- Incident this ADR generalizes from: DL laptop Claude Code session, 2026-07-29 — RentVeh
  Vehicle Master owner-name bold fix (verified via CiC) and a Shiprocket panel NDR ticket flow
  that returned a reproducible backend error under both Playwright and CiC (CiC used there only
  after explicit user approval, correctly per this ADR's condition 1 — condition 2 was
  genuinely met since the failure was server-side, not an automation limitation).
