# Mode 1, Component visual probe: probe classes + triage

`<plugin-root>` below is `$CLAUDE_PLUGIN_ROOT` in Claude Code; the plugin's installed directory
in Codex.

Detection layers, deepest last:

1. `npm run dogfood:visual-probe`, baseline per-page bar: no 4xx/5xx, no
   console JS errors, non-zero body rect, ≥1 upgraded custom-element host.
   Artifacts land in `qa/findings/visual-probe-<DATE>/`.
2. Bundled deep analyzer, `node "<plugin-root>/skills/demo-audit/scripts/analyze.mjs"`.
   Resolves the target checkout from `$ADIA_REPO_ROOT`, else cwd, always run
   from (or point it at) the monorepo checkout, never the plugin install dir.
   Flags: `--filter <slug>` · `--port N` (default 5173) · `--out PATH`
   (default `qa/findings/dogfooding-YYYY-MM-DD.md`) · `--quiet`.
   Exit 1 iff any critical finding.

Dev server first: `npm run dev` (foreground in a separate terminal, or
background with a "ready in" wait on the log, never leave one running after
the sweep).

## The 8 deep-probe classes

| # | Probe | Bug class + non-obvious diagnosis |
|---|---|---|
| 1 | Zero-area | Element collapsed: parent `display:none`, toolbar overflow spilled it, layout glitch. Diagnosis varies, never auto-fix. |
| 2 | Transparent fill | `[data-swatch]` / variant pill / button / chart indicator with computed `rgba(0,0,0,0)`, an unresolved fallback token (the chart-legend `--chart-N` class). |
| 3 | Empty control | `input-ui` / `search-ui` whose `connected()` should have stamped internals but didn't. |
| 4 | Synonym-attr / synonym-slot drift | Markers from `.claude/docs/conventions/attribute-api-migration.md` (`avatar-ui[name]`, `grid-ui[cols]`, `card-ui [slot=meta]`, `stepper-ui[current]`, `stepper-item-ui[state]`). |
| 5 | Alert flex-row | `alert-ui` with multiple bare `<text-ui>` children, needs `<col-ui slot="content">` wrap. |
| 6 | Missing component CSS | A `*-ui` tag rendered with no stylesheet matching `/components/{prefix}/{prefix}.css` loaded. Component CSS ships via `<link>`, separate from the JS module graph, a JS-only import registers the element but leaves it unstyled. Catches the swap-to-primitive-but-forget-the-link class. |
| 7 | Unstyled popover | Open `[popover]:popover-open` with transparent background + zero padding, same class as #6, seen from the runtime side. |
| 8 | Console | Every `console.error` + `console.warn` during load + 800ms settling. |

## Known false positives (do NOT extend `COLORED_SELECTORS` around these)

- `tag-ui` without a variant, bg is correctly `--a-bg-muted`, not transparent.
- `button-ui[variant=ghost]`, transparent by design.
- `chart-legend-ui[shape=dashed]` `[data-swatch]`, intentionally transparent
  bg + colored `border-top`; the probe already handles it.
- On a confirmed false positive, add the component/selector to the skip-list, never remove the entry from `COLORED_SELECTORS` (that drops coverage on the
  canonical case).

## Environment false positives (rule these out before filing)

- A worktree without its own `node_modules` silently breaks vite
  `import.meta.glob('/node_modules/…')`, icon/glob loaders return empty.
  `npm install` in the worktree before starting vite.
- A freshly-spun worktree vite serves icon `?raw` SVGs as `image/svg+xml`
  (module-script console errors) that the warm main server doesn't, count
  console errors against the warm server.
- Vite caches `import.meta.glob` at transform time; `npm install` after vite
  started won't refresh it, restart vite.
- Several vite servers run concurrently on adjacent ports. Confirm which
  checkout a port serves before investigating: `lsof -p <vite-pid> | grep cwd`.
- The `?chunks` dev overlay prepends a `<span data-chunk-marker>` into every
  `[data-chunk]` element, `:first-child`/`:nth-child` breakage under `?chunks`
  is the overlay, not the component.

## Silent-failure classes worth a manual probe pass

- A hardcoded `open` attribute on a `showModal` overlay (drawer-ui / modal-ui)
  bricks the whole page, only a live `elementFromPoint`/click probe catches it.
- Components silently accept made-up attributes as no-ops (`text-ui muted`,
  `empty-state-ui title=`: the real prop is `heading`). A demo that "renders
  but looks wrong" often used an attribute that doesn't exist; check the
  component yaml.

## Expanding the probe set

- New component with internal stamping logic → add to `STAMP_CONTRACTS` in
  `scripts/analyze.mjs`.
- New synonym-attribute drift class in the attribute-api-migration convention →
  add to `DRIFT_MARKERS`.
- `scripts/dev/playground-dogfood.mjs` (repo-local) carries the same probe set
  pointed at the tasks playground (outside the sitemap), mirror probe
  additions there when they apply.
