UI Debug Workflow
Debug UI changes with a repeatable evidence-first workflow. Use when validating visual regressions, reproducing frontend bugs, comparing baseline vs changed behavior, collecting screenshots/DOM/logs, or producing stakeholder-ready UI debug reports. Keywords: ui bug, visual regression, browser devtools, playwright, screenshot evidence, dom snapshot, frontend debugging.
npx skills add github:pantheon-org/tekhne --skill testing--ui-debug-workflowWhen to Use
Use this skill when frontend behavior must be verified with reproducible evidence.
When Not to Use
Do not use this workflow for backend-only issues with no UI symptom.
Core Principles
- Reproduce first, then modify code.
- Capture baseline and changed evidence under the same conditions.
- Compare artifacts before declaring a fix.
- Document commands, environment, and outcomes.
Deterministic Workflow
- Capture baseline evidence from a known branch.
- Apply fix and capture changed evidence with identical steps.
- Compare screenshots, DOM, and logs.
- If comparison shows unexpected differences: 1) verify identical conditions (URL, viewport, seed data, build mode) 2) check for flaky or animated elements 3) re-capture both runs if needed before concluding.
- Record pass/fail outcomes and unresolved risks.
- Publish a concise report with links to artifacts.
Quick Commands
Install browser runtime for automation
npx playwright install chromium
Expected result: Chromium runtime is available for automated UI checks.
Capture baseline evidence
./skills/ui-debug-workflow/scripts/capture-evidence.sh baseline http://localhost:3000 ./baseline
Expected result: screenshots, DOM snapshots, and logs saved in ./baseline.
Capture changed evidence
./skills/ui-debug-workflow/scripts/capture-evidence.sh changed http://localhost:3000 ./changed
Expected result: comparable artifacts saved in ./changed.
Compare baseline vs changed artifacts
./skills/ui-debug-workflow/scripts/compare-evidence.sh ./baseline ./changed ./comparison.md
Expected result: comparison.md with summarized differences.
Run end-to-end debug session
./skills/ui-debug-workflow/scripts/full-debug-session.sh main fix/my-branch http://localhost:3000 "npm run build" "npm start"
Expected result: complete session output with report-ready evidence.
Evaluate this skill quality
pantheon-skill-auditor evaluate ui-debug-workflow --json
Expected result: updated score and grade.
Troubleshooting
capture-evidence.sh fails to run:
- Ensure the dev server is running and reachable at the target URL before executing the script.
- Check that the expected port is not occupied by another process (
lsof -i :<port>). - Confirm Chromium is installed (
npx playwright install chromium).
compare-evidence.sh produces no output or errors:
- Verify both
./baselineand./changeddirectories exist and contain artifacts. - Re-run the capture steps if either directory is empty or incomplete.
Comparison reveals unexpected differences:
- Confirm identical conditions: same URL, viewport size, seed data, and build mode.
- Check for flaky elements (animations, timestamps, dynamic content) that may differ between runs.
- Re-capture both baseline and changed runs under verified identical conditions before drawing conclusions.
Anti-Patterns
NEVER debug without deterministic reproduction steps
WHY: Non-reproducible issues cannot be verified or closed with confidence.
BAD: “I clicked around and it looked broken once.” GOOD: “1) Open /users 2) Click Sort 3) Observe console error.”
Consequence: Fixes become guesswork and regressions reappear.
NEVER skip console and network error review
WHY: Browser errors often reveal root cause faster than code browsing.
BAD: Edit components before checking runtime errors. GOOD: Inspect console/network first, then form hypothesis.
Consequence: Time is wasted on unrelated code paths.
NEVER compare baseline and changed runs under different conditions
WHY: Different data, viewport, or build mode invalidates comparisons.
BAD: Baseline on desktop, changed on mobile viewport. GOOD: Use identical URL, viewport, seed data, and commands.
Consequence: False positives and false negatives in debug conclusions.
NEVER close a UI fix without evidence artifacts
WHY: Unlogged fixes are hard to review and hard to trust.
BAD: Merge after manual spot-check with no screenshots/logs. GOOD: Attach baseline, changed, and comparison artifacts.
Consequence: Stakeholders cannot verify what changed.
References
references/debugging-checklist.mdreferences/browser-devtools-guide.mdreferences/evidence-templates.md
Explicit NEVER/WHY/consequence triplets with side-by-side bad/good examples. Generic warnings score low — concrete failure modes with real impact (security, correctness, performance) score high.
Runtime validation via eval scenarios proving the skill actually changes agent behaviour. Requires evals/ directory, instructions.json, ≥3 scenarios, and ≥80% instruction coverage in summary.json.
Appropriate rigidity for the skill type: strict rules for critical/security skills, balanced steps for process skills, flexible options for tool skills. Mismatched calibration (too rigid or too loose) reduces score.
Expert-only knowledge the AI couldn't derive on its own — domain-specific patterns, production gotchas, decision frameworks, and non-obvious tool behaviour. High scores mean <5% redundancy with what the model already knows.
A clear philosophy plus numbered step-by-step workflows with explicit entry/exit points. Should answer: what is the core principle, how to execute it, and when (and when not) to apply it.
Rich activation keywords and trigger scenarios in the description so the skill fires when needed. Best descriptions read like an exhaustive list of the tasks and concepts that should invoke this skill.
Copy-paste-ready code, complete runnable examples, real file paths and commands. Pseudocode and abstract descriptions score low — concrete, executable content scores high.
SKILL.md as a <100-line navigation hub pointing to focused references/ files. Content should be lazy-loaded on demand, not front-loaded. Includes actionable 'When to Use' conditions per reference.
Proper frontmatter, single-task focus, activation keywords, and cross-harness portability. Skills must work across 40+ agentic harnesses without hardcoded paths or agent-specific references.
Expert-only knowledge the AI couldn't derive on its own — domain-specific patterns, production gotchas, decision frameworks, and non-obvious tool behaviour. High scores mean <5% redundancy with what the model already knows.
A clear philosophy plus numbered step-by-step workflows with explicit entry/exit points. Should answer: what is the core principle, how to execute it, and when (and when not) to apply it.
Explicit NEVER/WHY/consequence triplets with side-by-side bad/good examples. Generic warnings score low — concrete failure modes with real impact (security, correctness, performance) score high.
Proper frontmatter, single-task focus, activation keywords, and cross-harness portability. Skills must work across 40+ agentic harnesses without hardcoded paths or agent-specific references.
SKILL.md as a <100-line navigation hub pointing to focused references/ files. Content should be lazy-loaded on demand, not front-loaded. Includes actionable 'When to Use' conditions per reference.
Appropriate rigidity for the skill type: strict rules for critical/security skills, balanced steps for process skills, flexible options for tool skills. Mismatched calibration (too rigid or too loose) reduces score.
Rich activation keywords and trigger scenarios in the description so the skill fires when needed. Best descriptions read like an exhaustive list of the tasks and concepts that should invoke this skill.
Copy-paste-ready code, complete runnable examples, real file paths and commands. Pseudocode and abstract descriptions score low — concrete, executable content scores high.
No eval scenarios available.
Agent-Browser Automation
Overview
agent-browser is a CLI tool for basic browser automation using Playwright. It’s suitable for screenshots, DOM inspection, and simple interactions, but has limitations with complex user gestures.
Installation
# Install Playwright browsers
npx playwright install chromium
# Verify agent-browser is available
which agent-browser
Basic Commands
# Start a session and open URL
agent-browser --session mysession open http://localhost:3000
# Take screenshot
agent-browser --session mysession screenshot /tmp/screenshot.png
# Get DOM snapshot (accessibility tree)
agent-browser --session mysession snapshot
# Execute JavaScript
agent-browser --session mysession eval "document.title"
# Click element (use @eN from snapshot)
agent-browser --session mysession click @e42
# Close session
agent-browser --session mysession close
Example: Screenshot Capture Workflow
#!/bin/bash
# capture-evidence.sh
SESSION="ui-debug-session"
OUTPUT_DIR="./docs/changefix/changed"
URL="http://localhost:19888"
mkdir -p "$OUTPUT_DIR"
# Open the application
agent-browser --session "$SESSION" open "$URL"
sleep 3
# Capture initial state
agent-browser --session "$SESSION" screenshot "$OUTPUT_DIR/01-initial-load.png"
# Get DOM structure
agent-browser --session "$SESSION" snapshot > "$OUTPUT_DIR/01-initial-dom.txt"
# Interact with UI (example: close dialog)
agent-browser --session "$SESSION" click @e36
sleep 1
agent-browser --session "$SESSION" screenshot "$OUTPUT_DIR/02-dialog-closed.png"
# Test feature (example: select text)
agent-browser --session "$SESSION" eval "
const codeBlock = document.querySelector('pre code');
codeBlock.scrollIntoView({ block: 'center' });
const range = document.createRange();
range.selectNodeContents(codeBlock);
const sel = window.getSelection();
sel.removeAllRanges();
sel.addRange(range);
'Selected ' + sel.toString().length + ' characters';
"
sleep 1
agent-browser --session "$SESSION" screenshot "$OUTPUT_DIR/03-text-selected.png"
# Inspect final state
agent-browser --session "$SESSION" eval "
{
annotations: document.querySelectorAll('mark[data-bind-id]').length,
codeBlocks: document.querySelectorAll('pre code').length
}
" > "$OUTPUT_DIR/04-final-state.json"
# Close session
agent-browser --session "$SESSION" close
echo "✅ Evidence captured to $OUTPUT_DIR"
Capabilities
What agent-browser CAN do
✅ Navigate and load pages - Open URLs, follow links
✅ Capture visual state - Take screenshots at any point
✅ Inspect DOM - Get accessibility tree snapshots
✅ Execute JavaScript - Run custom code in the page context
✅ Click elements - Interact with buttons, links (via accessibility tree IDs)
✅ Read text content - Extract text from the page
What agent-browser CANNOT reliably do
❌ Complex user gestures - Text selection, drag-and-drop, multi-touch
❌ Trigger UI toolbars - Programmatic selections don’t always trigger contextual UI
❌ Hover states - Mouseover effects may not work as expected
❌ Keyboard input sequences - Complex keyboard interactions
❌ Real-time interactions - Animations, transitions may not complete
When agent-browser is Insufficient
Signs you need full Playwright:
- Text selection doesn’t trigger UI - Annotation toolbars, context menus don’t appear
- Drag-and-drop required - Moving elements or selecting ranges
- Complex event sequences - Multiple steps that depend on timing
- Visual validation - Pixel-perfect comparison of before/after
See: playwright-testing.md for full Playwright examples.
Related Documentation
- Playwright Testing - Full browser automation
- Evidence Collection - Screenshot best practices
- Troubleshooting - Common issues
Troubleshooting: agent-browser Not Found
Cause
agent-browser is not installed or not in PATH.
Solutions
Option 1: Install agent-browser
# Check if available via npm
npm search agent-browser
# If available:
npm install -g agent-browser
Option 2: Use Playwright Directly
Code Generation
# Generate test code interactively
npx playwright codegen http://localhost:3000
Write Test Script
See Playwright Testing for examples.
Option 3: Use the Automation Scripts
The skill provides ready-to-use scripts:
# Full workflow
~/.config/opencode/skills/ui-debug-workflow/scripts/full-debug-session.sh \
main fix/feature http://localhost:3000 "npm run build" "npm start"
Verification
# Check agent-browser
which agent-browser
# Check Playwright
npx playwright --version
# List installed browsers
npx playwright list-chromium
Related Documentation
- Agent-browser
- Playwright Testing
- Scripts - Ready-to-use automation scripts
Troubleshooting: Blank Screenshots
Cause
Page not fully loaded before screenshot is captured.
Solution: Add Wait Conditions
agent-browser
# Wait for specific element
agent-browser --session test eval "
await new Promise(resolve => {
const check = () => {
if (document.querySelector('pre code')) resolve();
else setTimeout(check, 100);
};
check();
});
"
# Then take screenshot
agent-browser --session test screenshot output.png
Playwright
// Wait for network to be idle
await page.waitForLoadState('networkidle');
// Wait for specific element
await page.waitForSelector('pre code');
// Wait for multiple elements
await Promise.all([
page.waitForSelector('pre code'),
page.waitForSelector('button'),
]);
// Then take screenshot
await page.screenshot({ path: "output.png" });
Alternative: Manual Sleep
# Simple but not ideal
sleep 3
agent-browser --session test screenshot output.png
Related Issues
Browser DevTools Guide
Use this guide to gather high-signal browser evidence.
Console
- Filter by Errors first, then Warnings.
- Expand stack traces and copy file:line references.
- Record recurring messages and timestamps.
Network
- Check failed requests (4xx/5xx) and payload mismatches.
- Validate request order for race-condition symptoms.
- Save HAR or request/response summaries when needed.
Elements and Styles
- Inspect computed styles for layout regressions.
- Compare class list before/after interaction.
- Check visibility/overflow/z-index for hidden UI.
Performance (Optional)
- Capture short profile for UI stutter/jank.
- Correlate long tasks with interaction lag.
Comprehensive Debug Report Template
Use this template for complete debug reports with evidence.
Template
# Debug Report: [Issue Title]
**Date**: YYYY-MM-DD
**Reporter**: [Your name]
**Status**: ✅ Fixed | ⚠️ In Progress | ❌ Not Fixed
---
## Executive Summary
[2-3 sentences summarizing the issue, fix, and result]
## Issue Description
**Original Problem**:
- Description of the bug or missing feature
- Steps to reproduce
- Expected vs actual behavior
**Impact**:
- User-facing impact
- Technical impact
- Severity: Critical | High | Medium | Low
## Changes Implemented
**Files Modified**:
```bash
M packages/ui/components/Viewer.tsx
M packages/ui/utils/highlighter.ts
```
**Key Changes**:
1. **File:line** - Description of change
2. **File:line** - Description of change
**Git Diff**: [See Appendix A]
## Test Environment
| Component | Version/Details |
|-----------|-----------------|
| **OS** | macOS 14.2 |
| **Browser** | Chromium 120.0.6099.129 |
| **Node/Bun** | Bun 1.0.23 |
| **Build Tool** | Vite 5.0.10 |
| **Server** | localhost:19888 |
| **Test Date** | YYYY-MM-DD HH:MM UTC |
## Test Methodology
### Baseline Capture
1. Checked out `main` branch (commit `abc123`)
2. Built application: `bun run build:hook`
3. Started server on port 19888
4. Captured screenshots and DOM snapshots
5. Documented observed behaviors and bugs
### Changed State Testing
1. Checked out `fix/feature` branch (commit `def456`)
2. Rebuilt application with changes
3. Restarted server on same port
4. Repeated identical test steps
5. Compared results with baseline
### Test Cases
- ✅ **TC-01**: Test description
- ✅ **TC-02**: Test description
- ⚠️ **TC-03**: Test description (partial)
- ❌ **TC-04**: Test description (failed)
## Evidence
### Test Case 01: Description
**Before (Baseline)**:

**Observations**:
- What you saw
- Issues identified
**After (With Fix)**:

**Observations**:
- What changed
- Issues resolved
**Result**: ✅ **PASS** - Description of outcome
---
[Repeat for each test case]
---
## Results Summary
| Test Case | Description | Baseline | Changed | Status |
|-----------|-------------|----------|---------|--------|
| TC-01 | Description | ✅ Pass | ✅ Pass | ✅ No regression |
| TC-02 | Description | ❌ Fail | ✅ Pass | ✅ **FIXED** |
| TC-03 | Description | ✅ Pass | ✅ Pass | ✅ No regression |
**Overall Result**: ✅ **FIXED** - Summary
## Known Limitations
### Limitation 1
**Issue**: Description
**Impact**: What it affects
**Mitigation**: How you worked around it
## Recommendations
### Immediate Actions
1. ✅ Action item with status
2. ⚠️ Action item with warning
3. 📝 Action item for documentation
### Future Improvements
1. Description of improvement
2. Description of improvement
## Appendix
### Appendix A: Git Diff
```diff
[Include git diff or link to file]
```
### Appendix B: Build Output
```
[Build log excerpt]
```
### Appendix C: Browser Console Logs
```
[Console output]
```
### Appendix D: Test Artifacts
**Location**: `./docs/changefix/YYYY-MM-DD-issue-name/`
**Contents**:
```
baseline/
01-initial-load.png
dom-snapshot.html
changed/
01-initial-load.png
dom-snapshot.html
changes.diff
build-output.log
```
---
**Report Generated**: YYYY-MM-DD HH:MM UTC
**Tool**: OpenCode UI Debug Workflow Skill
Simplified Template
For quick reports:
# Debug Report: [Issue]
## Summary
- **Issue**: Brief description
- **Fix**: What was changed
- **Result**: ✅ Fixed | ⚠️ Partial | ❌ Not Fixed
## Evidence
### Before

- Observed bug: Description
### After

- Bug fixed: Description
## Changes
- File:line - Description
## Test Results
- ✅ TC-01: Description
- ✅ TC-02: Description
## Next Steps
1. Action item
2. Action item
Related Documentation
- Evidence Collection - How to gather evidence
- Git Context - Including version info
Debugging Checklist
Use this checklist to keep UI debugging runs reproducible.
Pre-Run
- Confirm baseline branch and changed branch names.
- Confirm URL, viewport, and test account.
- Confirm build/start commands and environment variables.
Baseline Capture
- Start app from baseline branch.
- Capture screenshots at key states.
- Save DOM snapshots for affected components.
- Save console and server logs.
Changed Capture
- Switch to changed branch.
- Repeat identical interaction steps.
- Capture the same artifact set.
Comparison
- Diff screenshots side-by-side.
- Compare DOM and console output.
- Record exact pass/fail criteria.
DOM Snapshots
Types of Snapshots
1. Accessibility Tree (agent-browser)
Quick, lightweight view of interactive elements:
agent-browser --session test snapshot > dom-snapshot-accessible.txt
Use for:
- Finding element IDs for clicking (@e42, @e36, etc.)
- Understanding page structure
- Identifying interactive elements
2. Full HTML
Complete DOM with all attributes:
# Via agent-browser
agent-browser --session test eval "document.documentElement.outerHTML" > full-dom.html
# Via Playwright
const html = await page.content();
fs.writeFileSync('full-dom.html', html);
Use for:
- Inspecting classes and data attributes
- Comparing DOM structure before/after
- Debugging styling issues
When to Capture
- ✅ Before and after each major interaction
- ✅ When debugging DOM structure issues
- ✅ To compare element attributes (classes, data attributes)
- ✅ After errors or unexpected behavior
Example: Comparing DOM States
# Baseline
agent-browser --session baseline snapshot > baseline-dom.txt
# Changed
agent-browser --session changed snapshot > changed-dom.txt
# Compare
diff baseline-dom.txt changed-dom.txt
Extracting Specific Data
# Count elements
agent-browser --session test eval "
{
codeBlocks: document.querySelectorAll('pre code').length,
annotations: document.querySelectorAll('mark[data-bind-id]').length,
buttons: document.querySelectorAll('button').length
}
" > element-counts.json
# Check for specific classes
agent-browser --session test eval "
Array.from(document.querySelectorAll('pre code')).map((el, i) => ({
index: i,
hasHighlighting: el.querySelectorAll('span.hljs-keyword').length > 0,
language: el.className.match(/language-(\w+)/)?.[1]
}))
" > code-block-status.json
Related Documentation
- Screenshots - Visual evidence
- Logs - Build and runtime logs
Evidence Templates
Use these templates to standardize debug output.
Session Header
# UI Debug Session
- Baseline branch:
- Changed branch:
- URL:
- Viewport:
- Build command:
- Start command:
- Timestamp:
Comparison Summary
## Comparison Summary
| Check | Baseline | Changed | Status |
| --- | --- | --- | --- |
| Initial render | Pass | Pass | ✅ |
| Target bug reproduction | Reproducible | Not reproducible | ✅ |
| Console errors | 2 errors | 0 errors | ✅ |
Findings Block
## Findings
- Root cause:
- Fix implemented:
- Residual risks:
- Follow-up tasks:
Git Context
Essential Information
Always include in debug reports:
Current Branch
git branch --show-current
Latest Commit
git log -1 --oneline
# or more detailed:
git log -1 --format="%H%n%an <%ae>%n%ad%n%s"
Changes
# Full diff between baseline and changed
git diff main...HEAD > changes.diff
# Just file names
git diff --name-status main...HEAD
# Statistics
git diff --stat main...HEAD
Including in Reports
Baseline Section
## Baseline
**Branch**: `main`
**Commit**: `abc1234 Fix syntax highlighting`
**Date**: 2026-02-04 14:30 UTC
Changed Section
## Changed
**Branch**: `fix/code-annotations`
**Commit**: `def5678 Use plain text wrapper for annotations`
**Date**: 2026-02-05 10:15 UTC
**Files Modified**:
- M packages/ui/components/Viewer.tsx
- M packages/ui/utils/highlighter.ts
Full Diff
Include in appendix:
## Appendix A: Git Diff
See [changes.diff](./changes.diff) for complete diff.
**Summary**:
- 2 files changed
- 45 insertions(+)
- 12 deletions(-)
Automated Capture
#!/bin/bash
# capture-git-context.sh
OUTPUT_DIR="${1:-.}"
mkdir -p "$OUTPUT_DIR/git"
# Branch and commit
git branch --show-current > "$OUTPUT_DIR/git/branch.txt"
git log -1 --oneline > "$OUTPUT_DIR/git/commit.txt"
git log -1 --format="%H%n%an <%ae>%n%ad%n%s%n%n%b" > "$OUTPUT_DIR/git/commit-full.txt"
# Changes
git diff main...HEAD > "$OUTPUT_DIR/git/changes.diff"
git diff --name-status main...HEAD > "$OUTPUT_DIR/git/changed-files.txt"
git diff --stat main...HEAD > "$OUTPUT_DIR/git/stats.txt"
echo "✅ Git context saved to $OUTPUT_DIR/git/"
Related Documentation
- Logs - Build and runtime logs
- Report Structure - Using git info in reports
UI Debug Workflow Skill - Complete Summary
Overview
Created a comprehensive OpenCode skill for systematic UI debugging based on the Plannotator code block annotation testing session.
Skill Location: ~/.config/opencode/skills/ui-debug-workflow/
What Was Created
1. Core Documentation (31+ KB total)
SKILL.md (27 KB) - Complete workflow documentation including:
- 4-phase debugging workflow (baseline → changed → compare → report)
- Browser automation guide (agent-browser + Playwright)
- Evidence collection best practices
- Comprehensive report template with example
- Troubleshooting guide
- Advanced techniques (visual diffs, CI/CD integration)
README.md (4 KB) - Quick start guide:
- Installation instructions
- Usage examples
- Troubleshooting
- Links to full documentation
INSTALL-SUMMARY.md (6 KB) - This summary document
2. Automation Scripts (3 executable bash scripts)
capture-evidence.sh:
- Opens application in agent-browser
- Captures 8 screenshots at key interaction points
- Saves DOM snapshots (accessibility tree + full HTML)
- Analyzes code blocks, syntax highlighting, annotations
- Outputs structured JSON data files
compare-evidence.sh:
- Compares baseline and changed evidence directories
- Diffs JSON data with jq
- Creates side-by-side screenshot comparison
- Generates visual diffs with ImageMagick
- Outputs markdown comparison report
full-debug-session.sh:
- Complete end-to-end automation
- Switches branches, builds both versions
- Runs evidence capture for baseline and changed
- Generates comparison report
- Creates debug report template
- Outputs:
./docs/change-fix/YYYY-MM-DD-HHMMSS/
3. Example Test Suite
ui-debug-test.spec.ts (9 KB):
- Playwright test suite for code block annotations
- 8 test cases covering:
- Page load with syntax highlighting
- Text selection in code blocks
- Annotation creation (auto and manual)
- Annotation removal
- Syntax highlighting restoration
- Multiple annotations
- Comprehensive report generation
- Demonstrates real mouse interactions vs programmatic
- Shows how to capture visual evidence in tests
Key Learnings Documented
Agent-browser Capabilities
✅ Adequate for:
- Page navigation and screenshots
- DOM inspection (accessibility tree)
- JavaScript execution
- Basic element clicking
- Visual state capture
❌ Insufficient for:
- Complex user gestures (text selection, drag-and-drop)
- Triggering contextual UI (toolbars appearing on selection)
- Real mouse interaction testing
- Interactive behavior validation
When to Use Each Tool
| Task | Tool | Reason |
|---|---|---|
| Screenshots | agent-browser | Fast, simple, adequate |
| DOM inspection | agent-browser | Good enough for structure analysis |
| Complex interactions | Playwright | Real browser events, full control |
| Visual regression | Playwright | Screenshot comparison built-in |
| CI/CD testing | Playwright | Better automation, reporting |
Workflow Overview
Phase 1: Baseline Capture (before changes)
git checkout main
npm run build
npm start
./scripts/capture-evidence.sh baseline-session http://localhost:3000 ./baseline
Output: Screenshots, DOM snapshots, structured data (JSON)
Phase 2: Changed State Testing (after changes)
git checkout fix/my-feature
npm run build
npm start
./scripts/capture-evidence.sh changed-session http://localhost:3000 ./changed
Output: Same evidence structure for comparison
Phase 3: Comparison
./scripts/compare-evidence.sh ./baseline ./changed ./comparison.md
Output: Side-by-side comparison report with diffs
Phase 4: Report Generation
Fill out the comprehensive debug report template:
- Summary with status (✅/⚠️/❌)
- Test environment details
- Git changes
- Evidence (screenshots, logs, diffs)
- Test results table
- Findings and recommendations
Usage Examples
Quick Start: Full Automated Session
cd ~/.config/opencode/skills/ui-debug-workflow
./scripts/full-debug-session.sh \
main \
fix/code-block-annotations \
"http://localhost:19888" \
"bun run build:hook" \
"bun run apps/hook/server/index.ts plan"
Result: Complete debug report in ./docs/change-fix/YYYY-MM-DD-HHMMSS/
In OpenCode Chat
User: Follow the ui-debug-workflow to validate this UI fix
Agent: I'll use the ui-debug-workflow skill to:
1. Capture baseline evidence from main branch
2. Capture changed evidence from your fix branch
3. Compare visual and functional differences
4. Generate comprehensive report with screenshots
[Agent runs scripts and generates report]
Manual Step-by-Step
# 1. Baseline
git checkout main && npm run build
./scripts/capture-evidence.sh baseline http://localhost:3000 ./baseline
# 2. Changed
git checkout fix/feature && npm run build
./scripts/capture-evidence.sh changed http://localhost:3000 ./changed
# 3. Compare
./scripts/compare-evidence.sh ./baseline ./changed ./report.md
Evidence Artifacts Generated
Each capture session creates:
| File | Purpose |
|---|---|
01-initial-load.png |
Page on first load |
02-dialog-closed.png |
After dismissing dialogs |
03-code-blocks-highlighted.png |
Code blocks outlined |
04-text-selected.png |
Text selection state |
05-annotations-check.png |
Annotation verification |
06-syntax-highlighting.json |
Structured highlighting data |
07-final-state.png |
Final screenshot |
07-full-html.html |
Complete DOM snapshot |
*-dom.txt |
Accessibility tree snapshots |
| Various JSON files | Structured test data |
Report Template Structure
The skill provides a comprehensive debug report template:
# Debug Report: [Issue Title]
## Summary
- Issue description
- Fix applied
- Result status
## Changes Made
- Git diff
- Files modified
## Test Environment
- Browser, OS, tools, versions
## Evidence
### Before (Baseline)
- Screenshots
- Observations
- Bugs identified
### After (With Fix)
- Screenshots
- Observations
- Bugs resolved
## Test Results
- Test case checklist with ✅/⚠️/❌
## Findings
- Issues fixed
- Regressions (if any)
- Outstanding issues
## Recommendations
- Next steps
- Improvements
## Appendix
- Logs, diffs, artifacts
Real-World Application
This skill was created based on testing the Plannotator code block annotation fix:
Original Issue:
surroundContents()broke syntax highlighting in code blocks- Annotations failed with DOM exceptions
- Removing annotations didn’t restore syntax highlighting
Testing Process:
- Built baseline (main branch with bugs)
- Built changed (fix branch with corrections)
- Captured visual evidence from both
- Discovered agent-browser limitations for interactive testing
- Documented full workflow for future use
Result: Complete, repeatable debugging workflow with visual proof
Prerequisites
Required
# Playwright browsers
npx playwright install chromium
# agent-browser (if using that approach)
# Usually available as CLI tool
Optional (for enhanced features)
# ImageMagick (visual diffs)
brew install imagemagick # macOS
apt-get install imagemagick # Linux
# jq (JSON diffing)
brew install jq # macOS
apt-get install jq # Linux
File Structure
~/.config/opencode/skills/ui-debug-workflow/
├── SKILL.md (27 KB) # Complete documentation
├── README.md (4 KB) # Quick start
├── INSTALL-SUMMARY.md (6 KB) # This file
├── scripts/
│ ├── capture-evidence.sh # Single evidence capture
│ ├── compare-evidence.sh # Baseline vs changed
│ └── full-debug-session.sh # End-to-end automation
├── examples/
│ └── ui-debug-test.spec.ts # Playwright test template
└── templates/ # (for future additions)
Total Size: ~50 KB documentation + scripts
Benefits
✅ Systematic: Step-by-step workflow, not ad-hoc testing
✅ Evidence-based: Visual proof with screenshots and logs
✅ Comprehensive: Complete before/after documentation
✅ Repeatable: Scripts automate the entire process
✅ Shareable: Reports suitable for stakeholders
✅ Educational: Documents what works and what doesn’t
Next Steps
To Use Immediately
- Install Playwright:
npx playwright install chromium - Run full session on your project
- Review evidence in
./docs/change-fix/ - Fill out report template
To Extend the Skill
- Add more test scenarios to
examples/ - Create project-specific templates in
templates/ - Add integrations (Cypress, Selenium, etc.)
- Contribute CI/CD workflow examples
Skill Metadata
name: ui-debug-workflow
description: Complete workflow for debugging UI changes with visual testing
license: MIT
compatibility: opencode
applies_to: ui, frontend, react, web-development, testing, debugging
methodology: visual-testing, evidence-based-debugging
tools: playwright, browser-automation, screenshots, git
Documentation
- Main docs:
~/.config/opencode/skills/ui-debug-workflow/SKILL.md - Quick start:
~/.config/opencode/skills/ui-debug-workflow/README.md - OpenCode skills: https://opencode.ai/docs/skills
Created: 2026-02-05
Session Duration: ~50 minutes
Based On: Plannotator code block annotation debugging session
Status: ✅ Complete and ready to use
Quick Reference
Run Full Session
~/.config/opencode/skills/ui-debug-workflow/scripts/full-debug-session.sh \
baseline-branch changed-branch url build-cmd start-cmd
Capture Evidence Only
~/.config/opencode/skills/ui-debug-workflow/scripts/capture-evidence.sh \
session-name url output-dir
Compare Evidence
~/.config/opencode/skills/ui-debug-workflow/scripts/compare-evidence.sh \
baseline-dir changed-dir output-report
Run Playwright Tests
npx playwright test examples/ui-debug-test.spec.ts --headed
The skill is now installed and ready to use for systematic UI debugging with visual evidence!
Logs
Types of Logs
Build Logs
Capture complete build output:
npm run build 2>&1 | tee build-output.log
# or
bun run build 2>&1 | tee build-output.log
What to look for:
- Build errors or warnings
- Bundle sizes
- Build time
- Optimization notes
Server Logs
Capture server startup and runtime logs:
npm start 2>&1 | tee server-output.log
What to look for:
- Server port
- Initialization errors
- Runtime warnings
- Request logs
Test Execution Logs
./test-script.sh 2>&1 | tee test-execution.log
Browser Console Logs
Via Playwright
import { test } from "@playwright/test";
import fs from "fs";
test("capture console logs", async ({ page }) => {
// Capture all console messages
page.on("console", (msg) => {
fs.appendFileSync("console.log", `[${msg.type()}] ${msg.text()}\n`);
});
// Capture errors
page.on("pageerror", (error) => {
fs.appendFileSync("console.log", `[ERROR] ${error.message}\n${error.stack}\n`);
});
await page.goto("http://localhost:3000");
// ... run tests
});
Via agent-browser
# Check for console errors
agent-browser --session test eval "
console.log('Test checkpoint 1');
// Your test code
'Checkpoint reached';
"
Git Context
Always include git information in reports:
# Current branch and commit
git branch --show-current > git-branch.txt
git log -1 --oneline > git-commit.txt
# Changes made
git diff main...HEAD > changes.diff
# File list
git diff --name-status main...HEAD > changed-files.txt
Log Organization
Suggested directory structure:
./docs/change-fix/
└── 2026-02-05-feature-name/
├── baseline/
│ ├── build-output.log
│ ├── server-output.log
│ └── console.log
├── changed/
│ ├── build-output.log
│ ├── server-output.log
│ └── console.log
└── git/
├── changes.diff
├── changed-files.txt
└── commit-info.txt
Related Documentation
- Screenshots - Visual evidence
- DOM Snapshots - Page structure
- Git Context - Version control info
Playwright Testing
Overview
Full Playwright scripts provide complete control over browser interactions, including real mouse events, keyboard input, and complex gestures that agent-browser cannot handle.
When to Use Playwright
Use full Playwright when:
- Text selection must trigger contextual UI (toolbars, menus)
- Drag-and-drop interactions are required
- Complex event sequences with timing dependencies
- Visual regression testing (screenshot comparison)
- Comprehensive test coverage needed
Basic Example
// test-ui-changes.spec.ts
import { test, expect } from "@playwright/test";
test("code block annotation works", async ({ page }) => {
await page.goto("http://localhost:19888");
// Wait for page load
await page.waitForSelector("pre code");
// Take baseline screenshot
await page.screenshot({ path: "baseline.png", fullPage: true });
// Select text in code block (real mouse interaction)
const codeBlock = page.locator("pre code").first();
await codeBlock.scrollIntoViewIfNeeded();
// Triple-click to select all text in code block
await codeBlock.click({ clickCount: 3 });
// Wait for toolbar to appear
await page.waitForSelector('[role="toolbar"]', { timeout: 2000 });
// Click "Redline" button
await page.getByRole("button", { name: /redline/i }).click();
// Verify annotation was created
const annotations = await page.locator("mark[data-bind-id]").count();
expect(annotations).toBeGreaterThan(0);
// Take screenshot of annotated state
await page.screenshot({ path: "annotated.png", fullPage: true });
// Test removal
await page.locator("mark[data-bind-id]").first().click({ button: "right" });
await page.getByText("Remove").click();
// Verify annotation removed and syntax highlighting restored
const remainingAnnotations = await page.locator("mark[data-bind-id]").count();
expect(remainingAnnotations).toBe(0);
// Verify code block still has syntax highlighting
const highlightedElements = await codeBlock.locator("span.hljs-*").count();
expect(highlightedElements).toBeGreaterThan(0);
// Final screenshot
await page.screenshot({ path: "removed.png", fullPage: true });
});
Run the Test
# Headed mode (see the browser)
npx playwright test test-ui-changes.spec.ts --headed
# Debug mode (step through)
npx playwright test test-ui-changes.spec.ts --debug
# Generate HTML report
npx playwright test test-ui-changes.spec.ts --reporter=html
Interactive Debugging
// debug-session.ts
import { chromium } from "playwright";
(async () => {
const browser = await chromium.launch({ headless: false, slowMo: 500 });
const page = await browser.newPage();
// Enable console logging
page.on("console", (msg) => console.log(`[BROWSER] ${msg.text()}`));
await page.goto("http://localhost:19888");
// Pause for manual inspection
await page.pause();
// Interactive REPL
await page.evaluate(() => {
debugger; // Breaks in DevTools
});
await browser.close();
})();
Run with: npx playwright test debug-session.ts --debug
Capturing Browser Logs
page.on("console", (msg) => {
fs.appendFileSync("console.log", `[${msg.type()}] ${msg.text()}\n`);
});
page.on("pageerror", (error) => {
fs.appendFileSync("console.log", `[ERROR] ${error.message}\n`);
});
Visual Regression Testing
test("visual regression", async ({ page }) => {
await page.goto("http://localhost:3000");
// Compare screenshot with baseline
await expect(page).toHaveScreenshot("homepage.png", {
maxDiffPixels: 100 // Allow minor differences
});
});
CI/CD Integration
# .github/workflows/visual-regression.yml
name: Visual Regression Tests
on: [pull_request]
jobs:
visual-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: oven-sh/setup-bun@v1
- name: Install dependencies
run: bun install
- name: Install Playwright
run: npx playwright install chromium
- name: Build baseline (main)
run: |
git checkout main
bun run build
# Save baseline screenshots
- name: Build changed (PR branch)
run: |
git checkout ${{ github.head_ref }}
bun run build
# Save changed screenshots
- name: Compare screenshots
run: ./scripts/compare-visual-evidence.sh
- name: Upload artifacts
uses: actions/upload-artifact@v3
with:
name: visual-test-results
path: docs/changefix/
Key Differences from agent-browser
| Feature | agent-browser | Playwright |
|---|---|---|
| Screenshot | ✅ Fast | ✅ Full control |
| DOM inspection | ✅ Accessibility tree | ✅ Full DOM access |
| Text selection | ❌ Doesn’t trigger UI | ✅ Real mouse events |
| Drag-and-drop | ❌ Not supported | ✅ Full support |
| Keyboard input | ❌ Limited | ✅ Complete |
| Event timing | ❌ Manual waits | ✅ Smart waiting |
| Visual regression | ❌ Manual comparison | ✅ Built-in |
Related Documentation
- Agent-browser - Basic automation
- Evidence Collection - Screenshot and log best practices
- Example Tests - Complete test suite
Troubleshooting: Programmatic Selection Doesn’t Trigger UI
Cause
UI frameworks may ignore programmatic JavaScript events and only respond to real user interactions.
Problem
# This doesn't trigger annotation toolbar:
agent-browser --session test eval "
const el = document.querySelector('pre code');
const range = document.createRange();
range.selectNodeContents(el);
window.getSelection().removeAllRanges();
window.getSelection().addRange(range);
"
Solution: Use Full Playwright
Real Mouse Interactions
import { test } from "@playwright/test";
test("select text with real mouse", async ({ page }) => {
await page.goto("http://localhost:3000");
const codeBlock = page.locator("pre code").first();
// Triple-click to select all (real mouse event)
await codeBlock.click({ clickCount: 3 });
// Wait for toolbar to appear
await page.waitForSelector('[role="toolbar"]', { timeout: 2000 });
});
Alternative: Trigger Events Manually
Sometimes you can dispatch events:
await page.evaluate(() => {
const el = document.querySelector('pre code');
const range = document.createRange();
range.selectNodeContents(el);
window.getSelection().removeAllRanges();
window.getSelection().addRange(range);
// Dispatch events
el.dispatchEvent(new MouseEvent('mouseup', { bubbles: true }));
document.dispatchEvent(new Event('selectionchange'));
});
But this doesn’t always work. Real mouse interactions are more reliable.
When agent-browser is Insufficient
Signs you need full Playwright:
- Text selection doesn’t trigger UI toolbars
- Drag-and-drop required
- Complex event sequences
- Hover effects needed
Solution: See Playwright Testing
Related Documentation
Screenshots
Naming Convention
Use descriptive, sequential naming:
[sequence]-[stage]-[description].png
Examples:
01-baseline-initial-load.png
02-baseline-text-selected.png
03-changed-initial-load.png
04-changed-text-selected.png
05-changed-annotation-applied.png
What to Capture
- ✅ Initial page load
- ✅ Each step of the interaction flow
- ✅ Error states (if any)
- ✅ Final state after all interactions
- ✅ Full page AND focused element screenshots
Best Practices
Full Page vs Focused
# Full page screenshot
agent-browser --session test screenshot full-page.png
# Or with Playwright
await page.screenshot({ path: "full.png", fullPage: true });
# Focused element
const element = page.locator("pre code").first();
await element.screenshot({ path: "focused.png" });
Timing
Always wait for page to be fully ready:
# agent-browser: manual wait
sleep 3
# Playwright: smart waiting
await page.waitForLoadState('networkidle');
await page.waitForSelector('pre code');
Visual Comparison
Using ImageMagick
# Install
brew install imagemagick # macOS
apt-get install imagemagick # Linux
# Compare two screenshots
compare baseline.png changed.png diff.png
# Highlight differences in red
compare -fuzz 5% -highlight-color red baseline.png changed.png diff.png
Playwright Visual Regression
test("visual regression", async ({ page }) => {
await page.goto("http://localhost:3000");
// Compare with baseline
await expect(page).toHaveScreenshot("homepage.png", {
maxDiffPixels: 100 // Allow minor differences
});
});
Troubleshooting
See blank-screenshots-troubleshooting.md for common issues.