LazyCodex's general QA guidance prefers the bundled Codex Browser for unauthenticated browser work, but the debugging skill says Playwright CLI is mandatory. That conflict routes browser debugging to a separate Playwright session even when Codex Browser and its tab.playwright surface are available.
Live runtime discovery independently confirmed that both Chrome and Codex In-app Browser were available and advertised CDP capability, so tool absence was not the cause.
The patch makes the routing precedence explicit in the skill-picker-consumed frontmatter and all debugging QA references: Codex Browser plus tab.playwright first, standalone Playwright only when Browser is unavailable, lacks the required action, or an isolated browser process is specifically required. The regression test verifies only the minimal routing-bearing frontmatter tokens without snapshotting body prose.
diff --git a/plugins/omo/skills/debugging/SKILL.md b/plugins/omo/skills/debugging/SKILL.md
index 3baf0b4..6f1b5de 100644
--- a/plugins/omo/skills/debugging/SKILL.md
+++ b/plugins/omo/skills/debugging/SKILL.md
@@ -1,6 +1,6 @@
---
name: debugging
-description: "MUST USE for any real runtime debugging across ANY language or binary — crashes, silent failures, wrong responses, stuck processes, memory leaks, async misbehavior, unexplained timing, reverse engineering. Runs a hypothesis-driven loop: form ≥3 hypotheses, investigate in parallel, after 2 failed rounds spawn Oracles from orthogonal angles, confirm root cause, lock with a failing test, fix minimally, QA by actually USING the system, scrub artifacts. The actual HOW lives in `references/` — READ THEM. Triggers: 'debug this', 'why is X not working', 'hanging', 'attach a debugger', 'reverse engineer', 'pwndbg', 'gdb', 'lldb', 'node inspect', 'tsx debug', 'pdb', 'dlv', 'delve', 'rust-gdb', 'set a breakpoint', 'context window exploded', 'why is the response empty', 'why is this happening', 'trace this bug', 'reproduce and fix', 'silent failure', 'HTTP 200 but empty', 'why did it stop', 'inspect the binary', 'playwright', 'flaky test', 'fails intermittently', 'passes in isolation', 'only fails in CI'."
+description: "MUST USE for any real runtime debugging across ANY language or binary — crashes, silent failures, wrong responses, stuck processes, memory leaks, async misbehavior, unexplained timing, reverse engineering. Runs a hypothesis-driven loop: form ≥3 hypotheses, investigate in parallel, after 2 failed rounds spawn Oracles from orthogonal angles, confirm root cause, lock with a failing test, fix minimally, QA by actually USING the system, scrub artifacts. For browser UI bugs in Codex, route through browser:control-in-app-browser and its tab.playwright surface first; use standalone Playwright fallback only when that surface is unavailable or lacks the required capability. The actual HOW lives in `references/` — READ THEM. Triggers: 'debug this', 'why is X not working', 'hanging', 'attach a debugger', 'reverse engineer', 'pwndbg', 'gdb', 'lldb', 'node inspect', 'tsx debug', 'pdb', 'dlv', 'delve', 'rust-gdb', 'set a breakpoint', 'context window exploded', 'why is the response empty', 'why is this happening', 'trace this bug', 'reproduce and fix', 'silent failure', 'HTTP 200 but empty', 'why did it stop', 'inspect the binary', 'playwright', 'flaky test', 'fails intermittently', 'passes in isolation', 'only fails in CI'."
---
# Debugging
@@ -49,12 +49,12 @@ These are not "optional extras". They are the correct tool in their domain, and
| Tool | Use when | Reference |
|---|---|---|
-| **Playwright CLI** | Any browser-served web UI bug. Any flow that requires clicking/typing/navigating. Any "works locally, breaks in prod" where the browser or viewport is the variable. **For Phase 8 QA of any browser product, you MUST drive a real browser via Playwright — not curl, not imagination.** | 📖 **[references/tools/playwright-cli.md](references/tools/playwright-cli.md)** |
+| **Browser Playwright / Playwright CLI** | Any browser-served web UI bug. Any flow that requires clicking/typing/navigating. Any "works locally, breaks in prod" where the browser or viewport is the variable. **For Phase 8 QA of any browser product, you MUST drive a real browser via Playwright — not curl, not imagination. In Codex, use `browser:control-in-app-browser` and its `tab.playwright` API first; use standalone Playwright only when Browser is unavailable or lacks the required action.** | 📖 **[references/tools/playwright-cli.md](references/tools/playwright-cli.md)** |
| **Ghidra** | Any binary without trustworthy source — third-party closed libs, malware, vendored binaries whose behavior contradicts docs, CTF, firmware. **Use Ghidra's decompiler before `strings`/`objdump` guessing. It turns machine code into readable C.** | 📖 **[references/tools/ghidra.md](references/tools/ghidra.md)** |
| **pwndbg** | Any native binary debugging session. It is GDB with the useful views (registers, stack, disasm, heap) always visible. **If you'd reach for plain `gdb`, reach for `pwndbg` instead — it is strictly a superset.** | 📖 **[references/tools/pwndbg.md](references/tools/pwndbg.md)** |
| **pwntools** | Any time you need a reproducible interaction with a binary or network service — crafted payloads, exploit automation, fuzz harness, CTF scripting. | 📖 **[references/tools/pwntools.md](references/tools/pwntools.md)** |
-**Failing to use these tools in their domain is a process failure, not a stylistic choice.** If the bug is in a browser and you did Phase 8 without Playwright, you are doing it wrong. If the bug is in a stripped binary and you read hex with `xxd`, you are doing it wrong. The references tell you how. Read them.
+**Failing to use these tools in their domain is a process failure, not a stylistic choice.** If the bug is in a browser and you did Phase 8 without a real Playwright-driven browser surface, you are doing it wrong. Codex Browser's `tab.playwright` satisfies this requirement; starting a separate Playwright session when Browser can perform the scenario does not. If the bug is in a stripped binary and you read hex with `xxd`, you are doing it wrong. The references tell you how. Read them.
---
diff --git a/plugins/omo/skills/debugging/references/methodology/02-investigate.md b/plugins/omo/skills/debugging/references/methodology/02-investigate.md
index 5a01c30..7796233 100644
--- a/plugins/omo/skills/debugging/references/methodology/02-investigate.md
+++ b/plugins/omo/skills/debugging/references/methodology/02-investigate.md
@@ -65,7 +65,7 @@ When the `team_*` tools are present, create a **debug-squad** team and split inv
{
"kind": "category",
"category": "deep",
- "prompt": "You are the Reproduction Engineer. Your job: build the smallest reliable repro — a curl command, a vitest/pytest/go test, a tmux script, a Playwright script for browser bugs, a pwntools script for binary targets. It must reproduce on first try and be copy-pasteable by the Lead. Document exact input, expected output, observed output. Save repro artifacts under /tmp/ and tell the Lead to journal them. If the bug is browser-based you MUST use Playwright CLI — do not simulate with curl."
+ "prompt": "You are the Reproduction Engineer. Your job: build the smallest reliable repro — a curl command, a vitest/pytest/go test, a tmux script, a Playwright script for browser bugs, a pwntools script for binary targets. It must reproduce on first try and be copy-pasteable by the Lead. Document exact input, expected output, observed output. Save repro artifacts under /tmp/ and tell the Lead to journal them. If the bug is browser-based you MUST use a real Playwright-driven browser — in Codex prefer browser:control-in-app-browser and tab.playwright, falling back to standalone Playwright only when needed; do not simulate with curl."
},
{
"kind": "category",
diff --git a/plugins/omo/skills/debugging/references/methodology/08-qa.md b/plugins/omo/skills/debugging/references/methodology/08-qa.md
index d900e40..016a02f 100644
--- a/plugins/omo/skills/debugging/references/methodology/08-qa.md
+++ b/plugins/omo/skills/debugging/references/methodology/08-qa.md
@@ -14,7 +14,7 @@ Pick the row that matches the product. Do what it says. Do not substitute.
|---|---|
| **CLI tool** | Open `tmux`, run the actual command end-to-end, capture output. Paste the session transcript into the journal. Include exit code, stdout, stderr, side-effect check (files created/modified). |
| **HTTP API** | Start the real server, hit endpoints with `curl` or `httpie`, inspect response status + body + headers. Hit the specific endpoint that reproduced the bug. If there's auth, use real auth. |
-| **Browser-served web app** | **Drive a real browser via Playwright CLI.** See [tools/playwright-cli.md](../tools/playwright-cli.md). Navigate the exact page/flow that reproduced the bug. Capture screenshot + DOM + network evidence. **Do not substitute with curl** — browsers have state (cookies, localStorage, service workers, client-side JS, viewport-dependent CSS) that curl does not have. |
+| **Browser-served web app** | **Drive a real browser via Playwright.** In Codex, use `browser:control-in-app-browser` and its `tab.playwright` API first; use standalone Playwright only when Browser is unavailable or lacks the required action. See [tools/playwright-cli.md](../tools/playwright-cli.md). Navigate the exact page/flow that reproduced the bug. Capture screenshot + DOM + network evidence. **Do not substitute with curl** — browsers have state (cookies, localStorage, service workers, client-side JS, viewport-dependent CSS) that curl does not have. |
| **Agent / LLM pipeline** | Run the same user prompt that originally failed. Capture the full turn — tool calls, messages, usage counters. **Confirm non-zero usage** (zero usage = still failing silently, see silent-failure check below). |
| **Background worker / job queue** | Trigger the job through the normal entry point (API call, cron tick, message publish), tail the worker logs, observe completion state in the queue or DB. Don't just call the worker function directly — the trigger path matters. |
| **MCP server** | Invoke the tool via its actual client (Claude Desktop, Cursor, etc. if available) or `mcp-cli`, not just the HTTP probe endpoint. The MCP handshake itself is sometimes where bugs live. |
diff --git a/plugins/omo/skills/debugging/references/tools/playwright-cli.md b/plugins/omo/skills/debugging/references/tools/playwright-cli.md
index 7d63671..0e8e7c4 100644
--- a/plugins/omo/skills/debugging/references/tools/playwright-cli.md
+++ b/plugins/omo/skills/debugging/references/tools/playwright-cli.md
@@ -2,9 +2,9 @@
**https://playwright.dev/ · https://github.com/microsoft/playwright**
-For any browser-served web UI bug, this is the correct tool. Not curl. Not imagination. Not a headless HTTP library. A real browser with a real rendering engine, real JS execution, real cookies, real service workers, real viewport.
+For any browser-served web UI bug, a Playwright-driven real browser is the correct surface. Not curl. Not imagination. Not a headless HTTP library. Use a real rendering engine with real JS execution, cookies, service workers, and viewport behavior.
-**In Phase 8 Manual QA for browser products, using Playwright is not optional.** Curl cannot catch: CSS that breaks at specific viewport widths, hydration mismatches, client-side router bugs, cookie/session interactions, service-worker caching, JS-triggered navigations. All of those are common bug classes. Drive a browser.
+**In Phase 8 Manual QA for browser products, using Playwright is not optional.** In Codex, read `browser:control-in-app-browser` first and use its `tab.playwright` API when available. That is the primary Playwright surface and keeps the in-app browser context. Use standalone Playwright CLI only when Browser is unavailable, lacks the required action, or the scenario specifically needs an isolated browser process. Curl cannot catch CSS that breaks at specific viewport widths, hydration mismatches, client-side router bugs, cookie/session interactions, service-worker caching, or JS-triggered navigations.
> Note: `microsoft/playwright-cli` is the legacy repo; the current tooling lives in `@playwright/test` (npm) and `playwright` (pip), which include the `playwright` CLI. Use those — the legacy `playwright-cli` package is deprecated.
diff --git a/plugins/omo/test/debugging-browser-routing.test.mjs b/plugins/omo/test/debugging-browser-routing.test.mjs
new file mode 100644
index 0000000..9123f7f
--- /dev/null
+++ b/plugins/omo/test/debugging-browser-routing.test.mjs
@@ -0,0 +1,22 @@
+import assert from "node:assert/strict";
+import { readFile } from "node:fs/promises";
+import { dirname, join } from "node:path";
+import test from "node:test";
+import { fileURLToPath } from "node:url";
+
+const pluginRoot = dirname(dirname(fileURLToPath(import.meta.url)));
+
+test("#given the debugging skill #when its routing description is parsed #then Codex Browser precedes standalone Playwright", async () => {
+ // given
+ const skill = await readFile(join(pluginRoot, "skills", "debugging", "SKILL.md"), "utf8");
+ const frontmatter = skill.match(/^---\n(?<body>[\s\S]*?)\n---\n/);
+ assert(frontmatter?.groups?.body, "debugging skill must have YAML frontmatter");
+
+ // when
+ const description = frontmatter.groups.body.match(/^description:\s*"(?<value>.*)"$/m)?.groups?.value;
+ assert(description, "debugging skill frontmatter must expose a description");
+
+ // then
+ assert.match(description, /browser:control-in-app-browser/);
+ assert.match(description, /standalone Playwright fallback/);
+});
Problem Situation
LazyCodex's general QA guidance prefers the bundled Codex Browser for unauthenticated browser work, but the debugging skill says Playwright CLI is mandatory. That conflict routes browser debugging to a separate Playwright session even when Codex Browser and its
tab.playwrightsurface are available.Reproduction Logs
Command:
Before the fix:
Live runtime discovery independently confirmed that both Chrome and Codex In-app Browser were available and advertised CDP capability, so tool absence was not the cause.
Root Cause
plugins/omo/skills/debugging/SKILL.mdand its Phase 8 references described Playwright CLI as the only acceptable real-browser QA path. They did not recognize the bundled Browser plugin'stab.playwrightAPI as a Playwright-driven real-browser surface. This narrower rule overrode the Browser-first guidance already present inreview-workandvisual-qa.Verified Fix
The patch makes the routing precedence explicit in the skill-picker-consumed frontmatter and all debugging QA references: Codex Browser plus
tab.playwrightfirst, standalone Playwright only when Browser is unavailable, lacks the required action, or an isolated browser process is specifically required. The regression test verifies only the minimal routing-bearing frontmatter tokens without snapshotting body prose.Verification
browser:control-in-app-browserwas absent from the routing description.1 passed, 0 failed).4 passed, 0 failed).npm pack --dry-run --jsonsucceeded.32failures before and after), caused by absent shared monorepo packages and unrelated marketplace-update fixtures.codex execwith Browser and Chrome available selectedBrowser/IAB (tab.playwright)as primary andPlaywright CLIas standalone fallback.This fix was debugged, implemented, and verified with LazyCodex.
Tag: lazycodex-generated