Install this skill

Paste this into Claude Code (VS Code panel, Adom editor, or terminal) to install:

Search the Adom Wiki for the skill "Debug" (slug: debug) at https://wiki-ufypy5dpx93o.adom.cloud/wiki/skills/debug and install it into my local ~/.claude/skills/debug/ directory. Fetch the skill_source content from the wiki page and save it as SKILL.md. Then confirm it's installed by showing the first 5 lines.
?
What is a skill? Skills are instructions that teach AI assistants like Claude Code how to perform specific tasks. The description below is loaded into the AI as context when you invoke this skill. Well-written skills make the AI significantly more effective. Like Wikipedia, anyone can improve a skill by clicking Edit AI Skill — or have your AI submit an edit on your behalf.

Description

Edit AI Skill

Debug Guide -- Iterative Visual Feedback Loop

Full reference for debugging in the Adom environment. Sections 0-9 match the operational debug SKILL (with tool installation as section 0 and shotlog setup as section 2). Sections 10+ are deep-dive reference only.


0. Auto-Install Debug Tools

Run this FIRST before any debug session. Installs missing tools silently -- skips anything already present.

# shotlog — screenshot log viewer (port 8820)
if ! command -v shotlog &>/dev/null; then
  echo "Installing shotlog..."
  gh release download v0.2.0 -R adom-inc/shotlog -p "shotlog" -D /tmp --clobber
  chmod +x /tmp/shotlog
  sudo mv /tmp/shotlog /usr/local/bin/shotlog
  echo "shotlog installed: $(shotlog --version 2>&1 || echo 'ok')"
fi

# adom-desktop — desktop bridge CLI
if ! command -v adom-desktop &>/dev/null; then
  # Check for local build first
  if [ -f ~/project/adom-desktop/cli/target/release/adom-desktop ]; then
    sudo ln -sf ~/project/adom-desktop/cli/target/release/adom-desktop /usr/local/bin/adom-desktop
  else
    echo "Installing adom-desktop..."
    gh release download -R adom-inc/adom-desktop -p "adom-desktop" -D /tmp --clobber
    chmod +x /tmp/adom-desktop
    sudo mv /tmp/adom-desktop /usr/local/bin/adom-desktop
  fi
  echo "adom-desktop installed: $(adom-desktop --version 2>&1 || echo 'ok')"
fi

After this, shotlog and adom-desktop are on PATH. Run the block once per container — subsequent calls are no-ops.


1. The Ralph Wiggum Philosophy

Four rules:

  1. Always screenshot and analyze yourself. Never say "check your viewer" or "it should work now." YOU must visually verify every iteration.
  2. Never stop until the screenshot looks correct. Loop until the visual output matches what's expected. No exceptions.
  3. Failures are data. Each broken screenshot tells you exactly what to fix next. Iteration beats perfection -- don't aim for perfect on the first try.
  4. Always log to shotlog. Every screenshot gets injected into shotlog so the user can watch the debug session unfold in real-time. The shotlog panel is the user's window into what you're doing -- without it, they're blind.

For automated iteration on well-defined tasks with clear completion criteria, see /ralph-loop.


2. Start Shotlog First

Before entering the debug loop, always set up shotlog. This opens a panel where the user can watch every screenshot you take in real-time -- it's their live feed of the debug session. Without it, the user has no visibility into what you're doing.

# 1. Ensure shotlog server is running
shotlog health || shotlog serve &

# 2. Pick a descriptive channel name based on what you're debugging
#    Examples: "nav-overflow-fix", "3d-viewer-lighting", "signup-form-validation"
CHANNEL="<descriptive-name>"

# 3. Open the shotlog viewer panel in hydrogen so the user can watch
shotlog open -c "$CHANNEL"

Do this once at the start of any debug session. The user will see a shotlog panel appear in their workspace showing an empty timeline that fills up as you work.


3. The Debug Loop

1. Edit code
2. Refresh the debug surface (webview refresh, pup reload, or restart server)
3. Interact -- send commands to exercise the change (rotate camera, click buttons, toggle states)
4. Screenshot the panel and save to file
5. Inject into shotlog (user sees it appear in real-time)
6. Read the PNG -- analyze it visually yourself
7. If broken --> identify what's wrong --> fix code --> go to 1
8. If correct --> done

Steps 4-5 together, every time:

# Screenshot
adom-cli hydrogen screenshot panel --panel-id <id> -o /tmp/debug-v1.png

# Immediately inject into shotlog -- the user sees this appear live
shotlog inject -c "$CHANNEL" \
  -d "v1: Initial render after adding nav dropdown component" \
  -s hydrogen /tmp/debug-v1.png

Increment filenames and prefix descriptions with the version: v1:, v2:, v3:... so the shotlog timeline tells a clear story.

Descriptions matter -- they become filenames and are what the user reads in the shotlog panel. Write what you see and why. Bad: "screenshot". Good: "v3: Nav dropdown now renders but clips at bottom edge, needs overflow fix".

Step 3 is what makes this truly autonomous. Don't just screenshot the initial render -- drive the UI. Send eval commands to your server to rotate views, click buttons, fill forms, trigger edge cases. Test multiple states before declaring victory.


4. Screen Sharing Setup

Required for hydrogen screenshots to work. One-time setup per session.

  1. Tell the user: "Click the monitor icon in the hydrogen nav bar (top right)"
  2. A browser dialog appears asking what to share:
    • "Share this tab" -- enables panel + workspace screenshot scopes (recommended for most debugging)
    • "Share entire screen" -- enables all scopes including screen
  3. Persists for the entire session -- only needs to be done once

If you get a 504 timeout on any screenshot command, the user hasn't enabled sharing yet. Tell them:

To enable screenshots, click the monitor icon in the top-right of the hydrogen nav bar, then select "Share this tab" in the browser dialog that appears. You only need to do this once per session.


5. Screenshots

Always prefer the most targeted scope. Panel capture is far more efficient than workspace or screen.

Panel capture (primary -- fastest)

# Get panel IDs first
adom-cli hydrogen workspace get
# Capture a specific panel
adom-cli hydrogen screenshot panel --panel-id <leaf-id> -o /tmp/debug-v1.png

Use for: webview panels, sandbox panels, any single panel you're debugging.

Workspace capture

adom-cli hydrogen screenshot workspace -o /tmp/ws.png

Use for: seeing full context across multiple panels, layout verification.

Screen capture

adom-cli hydrogen screenshot screen -o /tmp/screen.png

Use for: seeing the full hydrogen workspace including nav bar and surroundings.

Adom Desktop screenshots (native apps, background windows)

When debugging involves native desktop apps (KiCad, Fusion 360) or you need to capture a window that isn't in the foreground, use the Adom Desktop app via adom-desktop CLI. This is the only way to capture native desktop apps and background windows.

# Check desktop is connected
adom-desktop ping

# List all windows to find HWNDs
adom-desktop desktop_list_windows

# Screenshot a specific window (works even if in background)
adom-desktop desktop_screenshot_window \
  '{"hwnd":<hwnd>,"savePath":"project-content/screenshots/desktop-debug.png"}'

# Screenshot the entire desktop
adom-desktop desktop_screenshot_screen \
  '{"savePath":"project-content/screenshots/desktop-full.png"}'

# Bring a window to the foreground
adom-desktop browser_focus_window '{"sessionId":"debug"}'

Requires the Adom Desktop app running on the user's machine.

When to use which

What you're debuggingScreenshot method
Content in a hydrogen panel (webview, sandbox)hydrogen screenshot panel
Multiple hydrogen panels at oncehydrogen screenshot workspace
Full hydrogen workspace + nav barhydrogen screenshot screen
A pup browser session on desktopbrowser_screenshot via adom-desktop CLI
KiCad, Fusion 360, native appsdesktop_screenshot_window via adom-desktop CLI
Background window you can't seedesktop_screenshot_window (captures by HWND even in background)
Full desktop with all apps visibledesktop_screenshot_screen via adom-desktop CLI

6. Debug Surfaces

Webview panels (primary)

The main debug surface. Pattern: start HTTP server, create webview panel, refresh after changes, screenshot.

Create a webview panel pointing at your server:

  • Panel type: adom/a1b2c3d4-0031-4000-a000-000000000031
  • Use workspace API to create via split or add-tab
  • Pass initialState: { "url": "https://<service-url>.adom.cloud/" }

Control:

# Navigate to a URL
adom-cli hydrogen webview navigate --panel-id <id> <url>

# Refresh after code changes
adom-cli hydrogen webview refresh --panel-id <id>

# Hide address bar for clean screenshots
adom-cli hydrogen webview set-header --panel-id <id> true

Pup browser sessions (desktop browser)

For when you need a real Chrome window on the user's desktop (full DevTools, console access, no iframe restrictions).

# Open a browser window
adom-desktop browser_open_window \
  '{"sessionId":"debug","profile":"debug","url":"http://localhost:3000"}'

# After code changes: reload + alert (flash taskbar)
adom-desktop browser_reload '{"sessionId":"debug"}'
adom-desktop browser_alert_window '{"sessionId":"debug"}'

# Screenshot
adom-desktop browser_screenshot '{"sessionId":"debug"}'

# Check JS errors
adom-desktop browser_errors '{"sessionId":"debug"}'

# Evaluate JS in the page
adom-desktop browser_eval \
  '{"sessionId":"debug","expr":"document.title"}'

Rules: Always pass sessionId. Always browser_reload + browser_alert_window after changes.

Sandbox panels

For isolated JS execution. Screenshot with element-capture (requires screen sharing like webview panels).


7. Serving Content with Remote Control

Your mini web server should expose a command/eval endpoint so Claude can interact with the page programmatically. This is what makes the debug loop truly autonomous -- Claude can edit code, reload, simulate user actions, screenshot, and verify without human intervention.

Build your server with an /eval endpoint:

// Express server with eval endpoint
const express = require('express');
const { WebSocketServer } = require('ws');
const app = express();
app.use(express.json());
app.use(express.static('./dist'));

const server = app.listen(3000);
const wss = new WebSocketServer({ server });

app.post('/eval', (req, res) => {
  // Broadcast the JS expression to all connected clients
  wss.clients.forEach(ws => ws.send(JSON.stringify({ type: 'eval', expr: req.body.expr })));
  res.json({ ok: true });
});

Client-side handler (in your HTML):

const ws = new WebSocket(`ws://${location.host}`);
ws. => {
  const msg = JSON.parse(e.data);
  if (msg.type === 'eval') {
    try { eval(msg.expr); } catch(err) { console.error(err); }
  }
};

Then Claude can drive the UI via curl:

# Rotate the 3D camera
curl -s http://127.0.0.1:3000/eval -X POST -H "Content-Type: application/json" \
  -d '{"expr":"viewer.camera.position.set(5, 3, 5); viewer.camera.lookAt(0,0,0);"}'

# Click a toolbar button
curl -s http://127.0.0.1:3000/eval -X POST -H "Content-Type: application/json" \
  -d '{"expr":"document.querySelector(\"#toggle-wireframe\").click()"}'

# Simulate a mouse click at coordinates
curl -s http://127.0.0.1:3000/eval -X POST -H "Content-Type: application/json" \
  -d '{"expr":"document.elementFromPoint(400,300)?.click()"}'

# Toggle a setting
curl -s http://127.0.0.1:3000/eval -X POST -H "Content-Type: application/json" \
  -d '{"expr":"toggleNightMode()"}'

This is especially powerful for 3D viewers, interactive dashboards, multi-state UIs, and anything where you need to test more than just the initial render.

For pup sessions, use browser_eval instead of curl:

adom-desktop browser_eval \
  '{"sessionId":"debug","expr":"document.querySelector(\"#btn\").click()"}'

Quick-start for simple static servers (no eval needed):

python3 -m http.server 3000 --directory ./dist &
# or
npx serve -p 3000 ./dist &

8. Decision Tree

Need visual debug feedback?
  |
  +-- Content in a hydrogen panel? (webview, sandbox)
  |     +-- Screen sharing enabled?
  |     |     +-- YES --> screenshot panel --panel-id <id>  (fastest)
  |     |     +-- NO  --> Tell user to click monitor icon to enable sharing
  |     |
  |     +-- After changes: webview refresh, then interact, then screenshot
  |
  +-- Content in a desktop browser window? (pup session)
  |     +-- browser_reload --> browser_eval (interact) --> browser_screenshot
  |
  +-- Content in a native desktop app? (KiCad, Fusion 360)
  |     +-- Adom Desktop connected? (ping)
  |     |     +-- YES --> desktop_list_windows --> desktop_screenshot_window
  |     |     +-- NO  --> Guide user to install/connect Adom Desktop
  |     |
  |     +-- Need window in foreground? --> browser_focus_window first
  |
  +-- Need to see multiple panels or full layout?
  |     +-- screenshot workspace
  |
  +-- Need to see the user's full desktop?
        +-- desktop_screenshot_screen (Adom Desktop, captures everything)
        +-- or screenshot screen (hydrogen, limited to browser tab)

9. Troubleshooting

SymptomCauseFix
504 timeout on screenshotScreen sharing not enabledClick monitor icon in nav bar, share tab
Panel ID not foundWrong ID or panel closedadom-cli hydrogen workspace get to list current IDs
Webview shows blankServer not running or wrong portcurl http://127.0.0.1:PORT/ to check
Screenshot shows stale contentPage not refreshedadom-cli hydrogen webview refresh --panel-id <id>
Pup can't connectDesktop Conduit not runningadom-desktop ping
desktop_screenshot failsAdom Desktop not connectedGuide user through desktop app setup
Can't find window HWNDWindow closed or title changeddesktop_list_windows to refresh
Need to see background windowWindow behind other appsdesktop_screenshot_window captures by HWND even in background
Eval endpoint not respondingServer missing /eval route or WS not connectedCheck server code has /eval POST handler and client has WS listener

10. Canvas/WebGL Screenshot Wiring (Legacy AV)

This section applies only to content displayed in the Adom Viewer (AV) panel. Webview panels use hydrogen's element-capture API and need no wiring.

AV uses a cooperative capture protocol via postMessage. When av_capture is called, the parent document sends a mgmt_capture_request message to the content iframe. The iframe must respond with mgmt_canvas_capture containing the rendered image data.

Canvas-based AV widgets

If your AV widget renders to <canvas> (WebGL, 2D canvas, charts), add this handler:

window.addEventListener('message', (e) => {
  let msg;
  try { msg = typeof e.data === 'string' ? JSON.parse(e.data) : e.data; } catch { return; }
  if (msg?.type === 'mgmt_capture_request') {
    const canvas = document.querySelector('canvas');
    if (canvas) {
      // Force a render frame if using requestAnimationFrame
      parent.postMessage({
        type: 'mgmt_canvas_capture',
        _reqId: msg._reqId,
        data: canvas.toDataURL('image/png')
      }, '*');
    }
  }
});

Sub-iframes in AV widgets

If your AV widget embeds sub-iframes, each must implement its own capture handler. The parent forwards mgmt_capture_request down and relays mgmt_canvas_capture responses back up:

// Parent widget: forward capture requests to sub-iframe
window.addEventListener('message', (e) => {
  let msg;
  try { msg = typeof e.data === 'string' ? JSON.parse(e.data) : e.data; } catch { return; }
  if (msg?.type === 'mgmt_capture_request') {
    document.getElementById('my-sub-iframe')?.contentWindow?.postMessage(msg, '*');
  }
  // Relay sub-iframe responses up to AV parent
  if (msg?.type === 'mgmt_canvas_capture') {
    parent.postMessage(msg, '*');
  }
});

Note: Do not use html2canvas for screenshot wiring. It is deprecated -- it doesn't work with WebGL, 3D content, or cross-iframe scenarios.


11. Puppeteer Deep Dive

For complex debugging that needs full browser control, console visibility, and network inspection beyond what browser_eval and browser_errors provide.

Full Puppeteer Script Template

const puppeteer = require('puppeteer');
const path = require('path');

const SESSION_ID = `pup-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
const SESSION_DIR = path.join(__dirname, 'sessions', SESSION_ID);

(async () => {
  const results = { sessionId: SESSION_ID, console: [], errors: [], screenshots: [], data: null };
  let browser;

  try {
    browser = await puppeteer.launch({
      headless: false,
      defaultViewport: { width: 1280, height: 900 },
      args: ['--no-sandbox', `--user-data-dir=${SESSION_DIR}`]
    });

    const page = await browser.newPage();

    // Capture ALL console output
    page.on('console', msg => {
      results.console.push({ type: msg.type(), text: msg.text(), ts: new Date().toISOString() });
    });

    // Capture uncaught page errors
    page.on('pageerror', err => {
      results.errors.push({ message: err.message, ts: new Date().toISOString() });
    });

    await page.goto('http://localhost:3000', { waitUntil: 'networkidle2', timeout: 30000 });
    await new Promise(r => setTimeout(r, 2000)); // Wait for async rendering

    const screenshot = await page.screenshot({ encoding: 'base64', fullPage: false });
    results.screenshots.push({ name: 'debug-shot', base64: screenshot });

    results.data = await page.evaluate(() => ({
      title: document.title,
      url: window.location.href,
      bodyText: document.body?.innerText?.slice(0, 500) || '',
    }));

  } catch (err) {
    results.errors.push({ message: err.message, stack: err.stack, ts: new Date().toISOString() });
  } finally {
    if (browser) await browser.close();
  }

  console.log('__PUPPETEER_RESULTS__');
  console.log(JSON.stringify(results, null, 2));
})();

Console Error Analysis

The biggest advantage of Puppeteer over hydrogen screenshots is JS console visibility:

  1. Check errors[] first -- uncaught exceptions crash widgets silently
  2. Check console[] for type: 'error' entries -- failed fetches, CORS blocks, missing resources
  3. Common widget killers:
    • ReferenceError: X is not defined -- missing library or typo
    • TypeError: Cannot read properties of null -- DOM element not found (wrong selector or script before DOM ready)
    • CORS error / blocked by CSP -- widget needs a real origin
    • 404 on resource -- wrong path for a dependency

Keep Browser Open for Inspection

For interactive debugging (WebGL, CesiumJS, 3D), keep the browser open so the user can interact:

// DON'T close browser in finally block
} finally {
  console.log('Browser open for inspection. Close manually when done.');
}
// Keep node alive
await new Promise(r => setTimeout(r, 300000)); // 5 min

12. Screen Sharing Architecture

Hydrogen screenshots use the browser's Screen Capture API (getDisplayMedia). When the user clicks the monitor icon and grants permission:

  • "Share this tab" grants access to capture the hydrogen editor tab. The element-capture method can then isolate individual panels by their bounding rect. This is why panel capture is so efficient -- it captures only the pixels for that panel, not the entire viewport.

  • "Share entire screen" grants access to the full display. The screen scope captures everything -- taskbar, other windows, desktop. More data to transfer and process, which is why it's slower.

Why element-capture is better than html2canvas: Element-capture reads actual rendered pixels from the compositor. It captures everything exactly as the user sees it: WebGL, CSS filters, backdrop-blur, canvas content, nested iframes, video elements. html2canvas attempts to re-render DOM elements to a canvas, which fails for anything beyond basic HTML/CSS.


13. Image Sizing

All screenshot methods auto-resize images to <=1568px on the longest edge before returning them. This is Claude's ideal image size -- larger images provide zero quality benefit.

SourceWhere resize happens
Hydrogen screenshotsServer-side in the hydrogen API
Pup browser_screenshotadom-desktop CLI, defaults to maxWidth: 1568
Adom Desktop screenshotsMCP server via sharp

For manual resizing (e.g., large images from other sources):

shotlog resize large-image.png              # Resize in-place to max 1400px
shotlog resize large-image.png -o small.png # Resize to new file

Sub-Skills
?
What are Sub-Skills?

Sub-skills are community-contributed AI skill extensions for this component. They teach AI assistants about specific tools, configurators, or workflows.

Examples:

  • A manufacturer’s configuration tool for a motor controller
  • A community-written design guide for an amplifier circuit
  • An automated test/validation script for a sensor module

How to add one: Click Add Sub-Skill, provide the URL to your skill and a brief description. Submissions are reviewed by the Adom team before going live.

No sub-skills yet. Be the first to contribute one!

0 revisions · Updated 2026-04-16 10:56:12