{
  "schema_version": 1,
  "type": "skill",
  "slug": "debug",
  "title": "Debug",
  "brief": "Full reference guide for iterative visual debugging in Adom.",
  "version": "1.0.0",
  "tags": [],
  "license": "MIT",
  "source_path": "SKILL.md",
  "readme": "# Debug Guide -- Iterative Visual Feedback Loop\n\nFull 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.\n\n---\n\n## 0. Auto-Install Debug Tools\n\n**Run this FIRST before any debug session.** Installs missing tools silently -- skips anything already present.\n\n```bash\n# shotlog — screenshot log viewer (port 8820)\nif ! command -v shotlog &>/dev/null; then\n  echo \"Installing shotlog...\"\n  gh release download v0.2.0 -R adom-inc/shotlog -p \"shotlog\" -D /tmp --clobber\n  chmod +x /tmp/shotlog\n  sudo mv /tmp/shotlog /usr/local/bin/shotlog\n  echo \"shotlog installed: $(shotlog --version 2>&1 || echo 'ok')\"\nfi\n\n# adom-desktop — desktop bridge CLI\nif ! command -v adom-desktop &>/dev/null; then\n  # Check for local build first\n  if [ -f ~/project/adom-desktop/cli/target/release/adom-desktop ]; then\n    sudo ln -sf ~/project/adom-desktop/cli/target/release/adom-desktop /usr/local/bin/adom-desktop\n  else\n    echo \"Installing adom-desktop...\"\n    gh release download -R adom-inc/adom-desktop -p \"adom-desktop\" -D /tmp --clobber\n    chmod +x /tmp/adom-desktop\n    sudo mv /tmp/adom-desktop /usr/local/bin/adom-desktop\n  fi\n  echo \"adom-desktop installed: $(adom-desktop --version 2>&1 || echo 'ok')\"\nfi\n```\n\nAfter this, `shotlog` and `adom-desktop` are on PATH. Run the block once per container — subsequent calls are no-ops.\n\n---\n\n## 1. The Ralph Wiggum Philosophy\n\nFour rules:\n\n1. **Always screenshot and analyze yourself.** Never say \"check your viewer\" or \"it should work now.\" YOU must visually verify every iteration.\n2. **Never stop until the screenshot looks correct.** Loop until the visual output matches what's expected. No exceptions.\n3. **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.\n4. **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.\n\nFor automated iteration on well-defined tasks with clear completion criteria, see `/ralph-loop`.\n\n---\n\n## 2. Start Shotlog First\n\n**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.\n\n```bash\n# 1. Ensure shotlog server is running\nshotlog health || shotlog serve &\n\n# 2. Pick a descriptive channel name based on what you're debugging\n#    Examples: \"nav-overflow-fix\", \"3d-viewer-lighting\", \"signup-form-validation\"\nCHANNEL=\"<descriptive-name>\"\n\n# 3. Open the shotlog viewer panel in hydrogen so the user can watch\nshotlog open -c \"$CHANNEL\"\n```\n\nDo 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.\n\n---\n\n## 3. The Debug Loop\n\n```\n1. Edit code\n2. Refresh the debug surface (webview refresh, pup reload, or restart server)\n3. Interact -- send commands to exercise the change (rotate camera, click buttons, toggle states)\n4. Screenshot the panel and save to file\n5. Inject into shotlog (user sees it appear in real-time)\n6. Read the PNG -- analyze it visually yourself\n7. If broken --> identify what's wrong --> fix code --> go to 1\n8. If correct --> done\n```\n\n**Steps 4-5 together, every time:**\n```bash\n# Screenshot\nadom-cli hydrogen screenshot panel --panel-id <id> -o /tmp/debug-v1.png\n\n# Immediately inject into shotlog -- the user sees this appear live\nshotlog inject -c \"$CHANNEL\" \\\n  -d \"v1: Initial render after adding nav dropdown component\" \\\n  -s hydrogen /tmp/debug-v1.png\n```\n\nIncrement filenames and prefix descriptions with the version: `v1:`, `v2:`, `v3:`... so the shotlog timeline tells a clear story.\n\n**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\".\n\nStep 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.\n\n---\n\n## 4. Screen Sharing Setup\n\n**Required for hydrogen screenshots to work.** One-time setup per session.\n\n1. Tell the user: \"Click the **monitor icon** in the hydrogen nav bar (top right)\"\n2. A browser dialog appears asking what to share:\n   - **\"Share this tab\"** -- enables `panel` + `workspace` screenshot scopes (recommended for most debugging)\n   - **\"Share entire screen\"** -- enables all scopes including `screen`\n3. Persists for the entire session -- only needs to be done once\n\n**If you get a 504 timeout on any screenshot command**, the user hasn't enabled sharing yet. Tell them:\n\n> 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.\n\n---\n\n## 5. Screenshots\n\nAlways prefer the most targeted scope. Panel capture is far more efficient than workspace or screen.\n\n### Panel capture (primary -- fastest)\n\n```bash\n# Get panel IDs first\nadom-cli hydrogen workspace get\n# Capture a specific panel\nadom-cli hydrogen screenshot panel --panel-id <leaf-id> -o /tmp/debug-v1.png\n```\n\nUse for: webview panels, sandbox panels, any single panel you're debugging.\n\n### Workspace capture\n\n```bash\nadom-cli hydrogen screenshot workspace -o /tmp/ws.png\n```\n\nUse for: seeing full context across multiple panels, layout verification.\n\n### Screen capture\n\n```bash\nadom-cli hydrogen screenshot screen -o /tmp/screen.png\n```\n\nUse for: seeing the full hydrogen workspace including nav bar and surroundings.\n\n### Adom Desktop screenshots (native apps, background windows)\n\nWhen 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.\n\n```bash\n# Check desktop is connected\nadom-desktop ping\n\n# List all windows to find HWNDs\nadom-desktop desktop_list_windows\n\n# Screenshot a specific window (works even if in background)\nadom-desktop desktop_screenshot_window \\\n  '{\"hwnd\":<hwnd>,\"savePath\":\"project-content/screenshots/desktop-debug.png\"}'\n\n# Screenshot the entire desktop\nadom-desktop desktop_screenshot_screen \\\n  '{\"savePath\":\"project-content/screenshots/desktop-full.png\"}'\n\n# Bring a window to the foreground\nadom-desktop browser_focus_window '{\"sessionId\":\"debug\"}'\n```\n\nRequires the Adom Desktop app running on the user's machine.\n\n### When to use which\n\n| What you're debugging | Screenshot method |\n|---|---|\n| Content in a hydrogen panel (webview, sandbox) | `hydrogen screenshot panel` |\n| Multiple hydrogen panels at once | `hydrogen screenshot workspace` |\n| Full hydrogen workspace + nav bar | `hydrogen screenshot screen` |\n| A pup browser session on desktop | `browser_screenshot` via adom-desktop CLI |\n| KiCad, Fusion 360, native apps | `desktop_screenshot_window` via adom-desktop CLI |\n| Background window you can't see | `desktop_screenshot_window` (captures by HWND even in background) |\n| Full desktop with all apps visible | `desktop_screenshot_screen` via adom-desktop CLI |\n\n---\n\n## 6. Debug Surfaces\n\n### Webview panels (primary)\n\nThe main debug surface. Pattern: start HTTP server, create webview panel, refresh after changes, screenshot.\n\n**Create a webview panel pointing at your server:**\n- Panel type: `adom/a1b2c3d4-0031-4000-a000-000000000031`\n- Use workspace API to create via split or add-tab\n- Pass `initialState: { \"url\": \"https://<service-url>.adom.cloud/\" }`\n\n**Control:**\n```bash\n# Navigate to a URL\nadom-cli hydrogen webview navigate --panel-id <id> <url>\n\n# Refresh after code changes\nadom-cli hydrogen webview refresh --panel-id <id>\n\n# Hide address bar for clean screenshots\nadom-cli hydrogen webview set-header --panel-id <id> true\n```\n\n### Pup browser sessions (desktop browser)\n\nFor when you need a real Chrome window on the user's desktop (full DevTools, console access, no iframe restrictions).\n\n```bash\n# Open a browser window\nadom-desktop browser_open_window \\\n  '{\"sessionId\":\"debug\",\"profile\":\"debug\",\"url\":\"http://localhost:3000\"}'\n\n# After code changes: reload + alert (flash taskbar)\nadom-desktop browser_reload '{\"sessionId\":\"debug\"}'\nadom-desktop browser_alert_window '{\"sessionId\":\"debug\"}'\n\n# Screenshot\nadom-desktop browser_screenshot '{\"sessionId\":\"debug\"}'\n\n# Check JS errors\nadom-desktop browser_errors '{\"sessionId\":\"debug\"}'\n\n# Evaluate JS in the page\nadom-desktop browser_eval \\\n  '{\"sessionId\":\"debug\",\"expr\":\"document.title\"}'\n```\n\n**Rules:** Always pass `sessionId`. Always `browser_reload` + `browser_alert_window` after changes.\n\n### Sandbox panels\n\nFor isolated JS execution. Screenshot with element-capture (requires screen sharing like webview panels).\n\n---\n\n## 7. Serving Content with Remote Control\n\nYour 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.\n\n**Build your server with an `/eval` endpoint:**\n\n```javascript\n// Express server with eval endpoint\nconst express = require('express');\nconst { WebSocketServer } = require('ws');\nconst app = express();\napp.use(express.json());\napp.use(express.static('./dist'));\n\nconst server = app.listen(3000);\nconst wss = new WebSocketServer({ server });\n\napp.post('/eval', (req, res) => {\n  // Broadcast the JS expression to all connected clients\n  wss.clients.forEach(ws => ws.send(JSON.stringify({ type: 'eval', expr: req.body.expr })));\n  res.json({ ok: true });\n});\n```\n\n**Client-side handler (in your HTML):**\n\n```javascript\nconst ws = new WebSocket(`ws://${location.host}`);\nws.onmessage = (e) => {\n  const msg = JSON.parse(e.data);\n  if (msg.type === 'eval') {\n    try { eval(msg.expr); } catch(err) { console.error(err); }\n  }\n};\n```\n\n**Then Claude can drive the UI via curl:**\n\n```bash\n# Rotate the 3D camera\ncurl -s http://127.0.0.1:3000/eval -X POST -H \"Content-Type: application/json\" \\\n  -d '{\"expr\":\"viewer.camera.position.set(5, 3, 5); viewer.camera.lookAt(0,0,0);\"}'\n\n# Click a toolbar button\ncurl -s http://127.0.0.1:3000/eval -X POST -H \"Content-Type: application/json\" \\\n  -d '{\"expr\":\"document.querySelector(\\\"#toggle-wireframe\\\").click()\"}'\n\n# Simulate a mouse click at coordinates\ncurl -s http://127.0.0.1:3000/eval -X POST -H \"Content-Type: application/json\" \\\n  -d '{\"expr\":\"document.elementFromPoint(400,300)?.click()\"}'\n\n# Toggle a setting\ncurl -s http://127.0.0.1:3000/eval -X POST -H \"Content-Type: application/json\" \\\n  -d '{\"expr\":\"toggleNightMode()\"}'\n```\n\nThis is especially powerful for 3D viewers, interactive dashboards, multi-state UIs, and anything where you need to test more than just the initial render.\n\n**For pup sessions**, use `browser_eval` instead of curl:\n```bash\nadom-desktop browser_eval \\\n  '{\"sessionId\":\"debug\",\"expr\":\"document.querySelector(\\\"#btn\\\").click()\"}'\n```\n\n**Quick-start for simple static servers** (no eval needed):\n```bash\npython3 -m http.server 3000 --directory ./dist &\n# or\nnpx serve -p 3000 ./dist &\n```\n\n---\n\n## 8. Decision Tree\n\n```\nNeed visual debug feedback?\n  |\n  +-- Content in a hydrogen panel? (webview, sandbox)\n  |     +-- Screen sharing enabled?\n  |     |     +-- YES --> screenshot panel --panel-id <id>  (fastest)\n  |     |     +-- NO  --> Tell user to click monitor icon to enable sharing\n  |     |\n  |     +-- After changes: webview refresh, then interact, then screenshot\n  |\n  +-- Content in a desktop browser window? (pup session)\n  |     +-- browser_reload --> browser_eval (interact) --> browser_screenshot\n  |\n  +-- Content in a native desktop app? (KiCad, Fusion 360)\n  |     +-- Adom Desktop connected? (ping)\n  |     |     +-- YES --> desktop_list_windows --> desktop_screenshot_window\n  |     |     +-- NO  --> Guide user to install/connect Adom Desktop\n  |     |\n  |     +-- Need window in foreground? --> browser_focus_window first\n  |\n  +-- Need to see multiple panels or full layout?\n  |     +-- screenshot workspace\n  |\n  +-- Need to see the user's full desktop?\n        +-- desktop_screenshot_screen (Adom Desktop, captures everything)\n        +-- or screenshot screen (hydrogen, limited to browser tab)\n```\n\n---\n\n## 9. Troubleshooting\n\n\n| Symptom | Cause | Fix |\n|---------|-------|-----|\n| 504 timeout on screenshot | Screen sharing not enabled | Click monitor icon in nav bar, share tab |\n| Panel ID not found | Wrong ID or panel closed | `adom-cli hydrogen workspace get` to list current IDs |\n| Webview shows blank | Server not running or wrong port | `curl http://127.0.0.1:PORT/` to check |\n| Screenshot shows stale content | Page not refreshed | `adom-cli hydrogen webview refresh --panel-id <id>` |\n| Pup can't connect | Desktop Conduit not running | `adom-desktop ping` |\n| desktop_screenshot fails | Adom Desktop not connected | Guide user through desktop app setup |\n| Can't find window HWND | Window closed or title changed | `desktop_list_windows` to refresh |\n| Need to see background window | Window behind other apps | `desktop_screenshot_window` captures by HWND even in background |\n| Eval endpoint not responding | Server missing /eval route or WS not connected | Check server code has /eval POST handler and client has WS listener |\n\n---\n\n## 10. Canvas/WebGL Screenshot Wiring (Legacy AV)\n\n**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.\n\nAV 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.\n\n### Canvas-based AV widgets\n\nIf your AV widget renders to `<canvas>` (WebGL, 2D canvas, charts), add this handler:\n\n```javascript\nwindow.addEventListener('message', (e) => {\n  let msg;\n  try { msg = typeof e.data === 'string' ? JSON.parse(e.data) : e.data; } catch { return; }\n  if (msg?.type === 'mgmt_capture_request') {\n    const canvas = document.querySelector('canvas');\n    if (canvas) {\n      // Force a render frame if using requestAnimationFrame\n      parent.postMessage({\n        type: 'mgmt_canvas_capture',\n        _reqId: msg._reqId,\n        data: canvas.toDataURL('image/png')\n      }, '*');\n    }\n  }\n});\n```\n\n### Sub-iframes in AV widgets\n\nIf 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:\n\n```javascript\n// Parent widget: forward capture requests to sub-iframe\nwindow.addEventListener('message', (e) => {\n  let msg;\n  try { msg = typeof e.data === 'string' ? JSON.parse(e.data) : e.data; } catch { return; }\n  if (msg?.type === 'mgmt_capture_request') {\n    document.getElementById('my-sub-iframe')?.contentWindow?.postMessage(msg, '*');\n  }\n  // Relay sub-iframe responses up to AV parent\n  if (msg?.type === 'mgmt_canvas_capture') {\n    parent.postMessage(msg, '*');\n  }\n});\n```\n\n**Note:** Do not use html2canvas for screenshot wiring. It is deprecated -- it doesn't work with WebGL, 3D content, or cross-iframe scenarios.\n\n---\n\n## 11. Puppeteer Deep Dive\n\nFor complex debugging that needs full browser control, console visibility, and network inspection beyond what `browser_eval` and `browser_errors` provide.\n\n### Full Puppeteer Script Template\n\n```javascript\nconst puppeteer = require('puppeteer');\nconst path = require('path');\n\nconst SESSION_ID = `pup-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;\nconst SESSION_DIR = path.join(__dirname, 'sessions', SESSION_ID);\n\n(async () => {\n  const results = { sessionId: SESSION_ID, console: [], errors: [], screenshots: [], data: null };\n  let browser;\n\n  try {\n    browser = await puppeteer.launch({\n      headless: false,\n      defaultViewport: { width: 1280, height: 900 },\n      args: ['--no-sandbox', `--user-data-dir=${SESSION_DIR}`]\n    });\n\n    const page = await browser.newPage();\n\n    // Capture ALL console output\n    page.on('console', msg => {\n      results.console.push({ type: msg.type(), text: msg.text(), ts: new Date().toISOString() });\n    });\n\n    // Capture uncaught page errors\n    page.on('pageerror', err => {\n      results.errors.push({ message: err.message, ts: new Date().toISOString() });\n    });\n\n    await page.goto('http://localhost:3000', { waitUntil: 'networkidle2', timeout: 30000 });\n    await new Promise(r => setTimeout(r, 2000)); // Wait for async rendering\n\n    const screenshot = await page.screenshot({ encoding: 'base64', fullPage: false });\n    results.screenshots.push({ name: 'debug-shot', base64: screenshot });\n\n    results.data = await page.evaluate(() => ({\n      title: document.title,\n      url: window.location.href,\n      bodyText: document.body?.innerText?.slice(0, 500) || '',\n    }));\n\n  } catch (err) {\n    results.errors.push({ message: err.message, stack: err.stack, ts: new Date().toISOString() });\n  } finally {\n    if (browser) await browser.close();\n  }\n\n  console.log('__PUPPETEER_RESULTS__');\n  console.log(JSON.stringify(results, null, 2));\n})();\n```\n\n### Console Error Analysis\n\nThe biggest advantage of Puppeteer over hydrogen screenshots is JS console visibility:\n\n1. Check `errors[]` first -- uncaught exceptions crash widgets silently\n2. Check `console[]` for `type: 'error'` entries -- failed fetches, CORS blocks, missing resources\n3. Common widget killers:\n   - `ReferenceError: X is not defined` -- missing library or typo\n   - `TypeError: Cannot read properties of null` -- DOM element not found (wrong selector or script before DOM ready)\n   - `CORS error` / `blocked by CSP` -- widget needs a real origin\n   - `404 on resource` -- wrong path for a dependency\n\n### Keep Browser Open for Inspection\n\nFor interactive debugging (WebGL, CesiumJS, 3D), keep the browser open so the user can interact:\n\n```javascript\n// DON'T close browser in finally block\n} finally {\n  console.log('Browser open for inspection. Close manually when done.');\n}\n// Keep node alive\nawait new Promise(r => setTimeout(r, 300000)); // 5 min\n```\n\n---\n\n## 12. Screen Sharing Architecture\n\nHydrogen screenshots use the browser's **Screen Capture API** (`getDisplayMedia`). When the user clicks the monitor icon and grants permission:\n\n- **\"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.\n\n- **\"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.\n\n**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.\n\n---\n\n## 13. Image Sizing\n\nAll 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.\n\n| Source | Where resize happens |\n|--------|---------------------|\n| Hydrogen screenshots | Server-side in the hydrogen API |\n| Pup browser_screenshot | adom-desktop CLI, defaults to `maxWidth: 1568` |\n| Adom Desktop screenshots | MCP server via `sharp` |\n\nFor manual resizing (e.g., large images from other sources):\n```bash\nshotlog resize large-image.png              # Resize in-place to max 1400px\nshotlog resize large-image.png -o small.png # Resize to new file\n```",
  "author": {
    "id": "695820315b5f1e4db2fcf602",
    "name": "Kyle Bergstedt",
    "email": "[email protected]"
  },
  "visibility": {
    "public": true
  },
  "hero": null,
  "sample_prompts": [],
  "discovery_triggers": [],
  "discovery_pitch": null,
  "metadata": {},
  "created_at": "2026-05-28T05:29:58.110Z",
  "updated_at": "2026-05-28T05:29:58.110Z",
  "sub_skills": [],
  "parent_app": null
}