# Architecture

A condensed extract of the design constraints. For implementation depth, see [`CLAUDE.md`](https://github.com/Hyperyond/Hover/blob/main/CLAUDE.md) at the repo root.

## The flow

The full path of one command:

```
page UI (widget Shadow DOM)
  ↕ WebSocket (127.0.0.1)
@hover-dev/core (Node service)
  ↕ spawn child
local CLI agent (claude -p / codex exec)
  ↕ MCP
Playwright MCP server
  ↕ CDP
isolated debug Chrome
  ↕ DOM
user's dev page
```

Step events flow back the same path in reverse.

## Boundary constraints

These are load-bearing — several are non-obvious:

- The **agent** never launches its own Chromium. It connects to whatever debug Chrome is on `chromeDebugPort` via `connectOverCDP` and picks the existing context/page whose URL matches the dev-server origin.
- The **service** is allowed to spawn one specific Chrome: the isolated debug Chrome under `<tmpdir>/hover-chrome`. This happens either at dev-server startup (when `autoLaunchChrome: true`) or on widget demand. It is *not* the user's primary Chrome profile.
- **Sandboxing is per-agent.** Hard-sandbox agents get an explicit `--allowedTools mcp__playwright --disallowedTools …` lockdown. Soft-sandbox agents get OS-level constraints (`--sandbox read-only`) plus a strict `developer_instructions` system prompt; a determined hallucinating agent could still try a built-in shell call, hence the ⚠ badge in the widget.
- The injected UI lives in a **Shadow DOM** marked with `data-hover="true"` so Playwright tests can skip it. Tailwind's default scan does not work inside Shadow DOM — inline styles or CSS-in-JS only.
- The local Node service binds to **`127.0.0.1` only**. Plugins are no-op in production builds.
- Generated Playwright code prefers `page.getByRole / getByText` over CSS / XPath selectors.
- Cookies / localStorage never transit the Node service; auth state stays inside the browser and is handled by Playwright in-process.

## Why isolated debug Chrome, not the user's normal browser?

Hover deliberately does not attach to the user's primary Chrome profile. Doing so would require the user to relaunch their everyday browser with `--remote-debugging-port` and would expose every tab, cookie, and extension to whatever the agent does. The trade-off is honest: the user has to log into the app once inside the debug Chrome, but the profile dir at `<tmpdir>/hover-chrome` persists across runs.

## Why is filesystem access disallowed on the agent?

The agent only needs the Playwright MCP server. Allowing `Bash`, `Edit`, `Write`, `Read`, etc. dramatically widens the blast radius if the prompt is hijacked or if the agent hallucinates a destructive action. The single write path (`__vibe_tests__/<slug>.spec.ts`) is granted by the Node service, not by the agent's tool list.
