git:20260714.4fd0952 to git:20261007.1acb8b5

24 added, 4 removed. Audit A to B.

---
name: Browser Debugging
description: >
Debug the running NXTG-Forge web dashboard by driving a headless Chromium via the
Playwright MCP server — read console errors, take screenshots, inspect the DOM
accessibility tree, and watch network requests. Use when the dashboard renders blank
or broken after a UI change, when the user reports browser console errors, after fixing
a React bug (infinite loop, "Maximum update depth", stale state) to confirm it's gone,
or when verifying a visual/interactive change (Command Center, Infinity Terminal) in the
real running app rather than from tests alone.
when_to_use: >
"blank page", "white screen", "console errors", "check the browser", "take a screenshot
of the UI", "does the dashboard render", "verify the React fix", "Maximum update depth",
"did my UI change work", "test Command Center", "network request failing in the browser".
allowed-tools: Bash(curl *), Bash(npm run *), mcp__playwright__browser_navigate, mcp__playwright__browser_snapshot, mcp__playwright__browser_take_screenshot, mcp__playwright__browser_console_messages, mcp__playwright__browser_network_requests, mcp__playwright__browser_click, mcp__playwright__browser_type, mcp__playwright__browser_wait_for, mcp__playwright__browser_evaluate
---
# Browser Debugging Skill
Drive a real (headless) Chromium against the running NXTG-Forge dashboard from inside
Claude Code, using the **Playwright MCP** server. No Windows Chrome dependency — Playwright
launches its own headless Chromium inside WSL.
## Where the tools come from (read first)
The Playwright MCP server is registered in **`forge-ui/.mcp.json`**, NOT in the plugin's
`.mcp.json` (which only registers governance / orchestrator / semgrep). The `browser_*` tools
only exist when a Claude Code session has `forge-ui/.mcp.json` loaded — i.e. you are working in
the `forge-ui/` repo. From the plugin repo alone they are unavailable. Config as shipped:
```jsonc
"playwright": { "command": "npx", "args": ["@playwright/mcp@latest", "--headless", "--console-level", "debug"] }
```
Two consequences of that exact config:
- `--console-level debug` → `browser_console_messages` returns **verbose debug logs**, not just
errors. Filter for `error`/`warning` yourself; do not assume a clean-looking tail means no errors.
- **No `--caps`** flag → the `pdf`, `vision` (coordinate clicks/screenshots), and `devtools`
capabilities are OFF. `browser_pdf_save` and coordinate-based interaction are not available here.
## Prerequisites
Start both servers from `forge-ui/` (one command starts Vite :5050 + Express API :5051):
```bash
cd forge-ui && npm run dev
```
- Confirm they're up before driving the browser:
+ Confirm they're up before driving the browser. Since forge-ui **v3.4.1** every `/api` route,
+ health included, needs this install's access token (forge-ui `docs/api/LOCAL-CLIENT-AUTH.md`), so
+ an unauthenticated health check gets `401` and never prints "API up":
```bash
- curl -sf http://localhost:5050 >/dev/null && echo "UI up"
- curl -sf http://localhost:5051/api/health >/dev/null && echo "API up"
+ # forge-ui health (local-client auth, forge-ui v3.4.1+)
+ curl -sf -m 5 http://localhost:5050 >/dev/null && echo "UI up"
+ FORGE_UI_API="${FORGE_UI_API:-http://127.0.0.1:5051}"
+ f="${FORGE_UI_SECRET_FILE:-${XDG_CONFIG_HOME:-$HOME/.config}/nxtg-forge/ui-secret}"
+ printf 'header = "Authorization: Bearer %s"\n' "$(cat "$f")" |
+ curl -sf -m 5 -K - -o /dev/null "$FORGE_UI_API/api/health" && echo "API up"
```
+ The token is read inside the pipe (`printf` is a builtin), so it never reaches argv or the
+ transcript. Use `127.0.0.1` for the API: `localhost` may resolve to `::1`, where it does not listen.
+ `-m 5` matters on WSL2: there a dead `localhost` port can hang instead of refusing.
+
+ **Sign-in.** Since v3.4.1 the dashboard opens on a sign-in screen. To get past it, the user runs
+ this **in their own terminal, not through Claude** (it prints the token):
+ `cd forge-ui && npx tsx src/server/auth/print-auth-url.ts` (a release archive: `node dist/server/auth/print-auth-url.js`),
+ then opens the printed link.
+
## Core tools (Playwright MCP)
| Task | Tool |
|------|------|
| Go to a URL | `browser_navigate` |
| Get the DOM as an accessibility tree (**+ element refs**) | `browser_snapshot` |
| Capture the page as an image | `browser_take_screenshot` |
| Read console output | `browser_console_messages` |
| List HTTP requests the page made | `browser_network_requests` |
| Wait for text / state (not a fixed sleep) | `browser_wait_for` |
| Click / type on a snapshot element | `browser_click` / `browser_type` (need a `ref`) |
| Run JS in page context | `browser_evaluate` |
| Multiple tabs | `browser_tabs` (single tool, `action` arg) |
The interaction model is **snapshot-first**: `browser_snapshot` returns the accessibility
tree with an element `ref` for each node; `browser_click` / `browser_type` operate on those
refs. You cannot click by raw CSS selector or by pixel coordinates in this config — snapshot,
find the element, then act on its ref.
## Workflow: dashboard is blank / has console errors
1. `browser_navigate` → `http://localhost:5050`
2. `browser_console_messages` → scan for `error` entries (React errors, failed imports,
`Maximum update depth`, uncaught exceptions). Debug-level noise is expected — grep for `error`.
3. `browser_network_requests` → any `4xx`/`5xx`? A failed `/api/*` call (API server down or
route 500) is the most common cause of a blank data panel.
4. `browser_take_screenshot` → capture the rendered state for the report.
## Workflow: verify a React fix (infinite loop / stale state)
1. `browser_navigate` → `http://localhost:5050` (fresh navigate reloads the Vite bundle — do
NOT reuse a stale open page after an HMR edit).
2. `browser_wait_for` → wait for a stable element/text instead of guessing a sleep.
3. `browser_console_messages` → confirm no `Maximum update depth exceeded` / `Warning:` reappears.
4. `browser_take_screenshot` → visual confirmation.
## Workflow: test Command Center interaction
1. `browser_navigate` → `http://localhost:5050`
2. `browser_snapshot` → locate the "Command" nav item, grab its `ref`
3. `browser_click` (that `ref`) → open Command Center
4. `browser_snapshot` again → grab the `ref` of a command button (e.g. "Forge Status")
5. `browser_click` (that `ref`)
6. `browser_console_messages` → check for errors triggered by the action
7. `browser_network_requests` → confirm the backing API call returned `2xx`
8. `browser_take_screenshot` → verify the result rendered
## Gotchas
- **The tools live in `forge-ui/.mcp.json`, not the plugin.** A session opened only in
`forge-plugin/` has no `browser_*` tools. Work from `forge-ui/`.
- **Headless Chromium is a fresh, isolated session — it is NOT the user's browser.** No user
cookies, auth, or localStorage carry over. A screenshot shows what a clean anonymous session
renders, not "what the user sees" in their logged-in Windows Chrome. If a bug depends on auth
state you must reproduce that state in the headless session first.
+ - **Since forge-ui v3.4.1 the headless session lands on the sign-in screen.** Claude has no
+ token-safe way to sign it in: navigating to the `forge_token` link puts the token into the tool
+ call. Debug the sign-in screen itself, or ask the user to check signed-in views in their own
+ browser.
- **`--console-level debug` means console output is noisy.** Absence of a visible error at the
tail of `browser_console_messages` is not proof of no error — filter the full list for `error`.
- **Snapshot before you interact.** `browser_click`/`browser_type` require a `ref` from
`browser_snapshot`. Clicking "by text" or by coordinates does not work in this config
(no `--caps vision`). Re-snapshot after any navigation or DOM change — refs go stale.
- **`browser_pdf_save` is unavailable** here (needs `--caps pdf`); so is coordinate-based
screenshotting/clicking (`--caps vision`). Don't reach for them.
- **Tab tools are consolidated.** Current Playwright MCP exposes a single `browser_tabs` tool
with an `action` argument — the old `browser_tab_list` / `browser_tab_new` / `browser_tab_select`
/ `browser_tab_close` names do not exist.
- **Re-navigate after an HMR edit.** A page left open from before your code change may still show
the old bundle; `browser_navigate` fresh to force the current build.
- **`forge-ui/.mcp.json` also pins a stale `forge` server path** (`--project .../v3`, the old
pre-rename directory). Unrelated to browser debugging, but don't be surprised the orchestrator
MCP there points at a dead path.
## Troubleshooting
If Playwright can't reach the UI:
1. `curl -sf http://localhost:5050` — Vite up?
- 2. `curl -sf http://localhost:5051/api/health` — API up?
+ 2. The authenticated health check under Prerequisites — API up? (A plain unauthenticated
+ `curl` gets `401` since forge-ui v3.4.1; that means "up, needs the token", not "down".)
3. Restart with `cd forge-ui && npm run dev` if either fails.
4. If the `browser_*` tools themselves are missing, confirm the session loaded
`forge-ui/.mcp.json` (you're in the `forge-ui/` repo), then reconnect the MCP server.