diff --git a/docs/claude/README.md b/docs/claude/README.md index da82b7383..55e4075ba 100644 --- a/docs/claude/README.md +++ b/docs/claude/README.md @@ -114,7 +114,7 @@ How to run Claude Code with a local Qwen model via Ollama for privacy, cost redu ### [Playwright MCP Browser Setup](./playwright-setup.md) -Detailed setup guide for the 7 independent Playwright browser sessions used for parallel testing, including VS Code extension configuration, CLI alternatives, and usage rules. +Setup guide for the Playwright MCP browsers: `browser-1` by default, the opt-in seven-browser pool on one shared server for parallel testing, VS Code user-scope registration, verification and usage rules. ### [Usage Tracker](../../usage-tracker/README.md) @@ -242,7 +242,7 @@ Collect requirements, study existing solutions, and identify what to build. Clau | Source | How | Commands / Tools | | ---------------------- | ------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------ | | **Tracking issues** | Sync and analyze open issues from project repos (GitHub; `tea` only for non-Conduction Forgejo repos) | `/swc-update`, `gh issue list`, `tea issues list` | -| **Other applications** | Crawl code or browse running apps to understand patterns | `/opsx-explore`, Playwright browsers (`browser-1`–`browser-7`) | +| **Other applications** | Crawl code or browse running apps to understand patterns | `/opsx-explore`, Playwright browsers (`browser-1`; `browser-2`–`browser-7` opt-in) | | **Documentation** | Read docs from other platforms, APIs, standards | `WebFetch`, `WebSearch`, `/opsx-explore` | | **Tenders** | Scrape TenderNed, classify by category, analyze requirements and ecosystem gaps | `/tender-scan`, `/tender-status`, `/tender-gap-report`, `/ecosystem-investigate`, `Read` (PDF support) | | **App store scouting** | Spot interesting apps on WordPress plugin directory, GitHub/Codeberg trending, ArtifactHub, Nextcloud app store | `WebSearch`, `WebFetch`, Playwright browsers | @@ -440,7 +440,7 @@ cp docs/claude/examples/CLAUDE.local.md.example .claude/CLAUDE.local.md ## Playwright MCP Browser Setup -The workspace uses 7 independent Playwright browser sessions for parallel testing. Copy the [example .mcp.json](./examples/.mcp.json.example) to your project root as `.mcp.json`, or see the [playwright-setup.md](./playwright-setup.md) guide for the full configuration, verification steps, CLI alternatives, and usage rules. +Every project ships **one** headless browser (`browser-1`) in its root `.mcp.json` — copy the [example .mcp.json](./examples/.mcp.json.example). The seven-browser pool for parallel agents is opt-in: one shared `@playwright/mcp` server on `localhost:8931`, referenced by URL, so a pool costs zero processes per session ([example](./examples/browser-pool-shared.json.example)). Claude Code starts every stdio server in `.mcp.json` at session start; seven per session is how WSL ran out of memory on 2026-09-08. See [playwright-setup.md](./playwright-setup.md) for the shared server, the user-scope registration for VS Code, verification steps and usage rules. **Quick summary:** @@ -491,7 +491,8 @@ This repo contains **documentation**, **global settings**, and **project templat │ ├── retrofit.md # Legacy app retrofit playbook │ └── examples/ # Project-level template files │ ├── CLAUDE.local.md.example # Template for project .claude/CLAUDE.local.md -│ └── .mcp.json.example # Template for project root .mcp.json (7 browsers) +│ ├── .mcp.json.example # Template for project root .mcp.json (browser-1 only) +│ └── browser-pool-shared.json.example # Opt-in 7-browser pool pointing at the shared server │ ├── global-settings/ # Mandatory user-level settings for ~/.claude/ │ ├── settings.json # → ~/.claude/settings.json (global read-only policy) @@ -511,7 +512,7 @@ Each Conduction project (Nextcloud apps, WordPress sites, etc.) has its own `.cl ``` / -├── .mcp.json # Playwright browser MCP servers (see docs/claude/examples/.mcp.json.example) +├── .mcp.json # Playwright browser-1 (see docs/claude/examples/.mcp.json.example); pools live in .claude/mcp/ │ └── .claude/ ├── CLAUDE.md # Workflow rules, project context @@ -647,6 +648,8 @@ npx -y @playwright/mcp@latest --headless --isolated --port 9999 # Should print: Listening on http://localhost:9999 ``` +If the pool entries (`browser-2`…) fail while `browser-1` works, the shared server is not running: `scripts/playwright-mcp-server.sh status` in the hydra checkout. + ### Claude Code doesn't see commands Ensure `.claude/` is at the workspace root and Claude Code is started from that directory. diff --git a/docs/claude/commands-tender.md b/docs/claude/commands-tender.md index 87c5a116a..fc72bb199 100644 --- a/docs/claude/commands-tender.md +++ b/docs/claude/commands-tender.md @@ -87,7 +87,7 @@ Deep-dive research into a software category — find and analyze open-source com **What it does:** 1. Loads category context and related tenders from the intelligence database -2. Uses the browser pool (browser-1 through browser-5) to scrape 5-10 competitors from multiple source types +2. Uses the browser pool (browser-1 through browser-5) to scrape 5-10 competitors from multiple source types — the pool beyond `browser-1` is opt-in and needs the shared server, see [playwright-setup.md](playwright-setup.md) 3. Creates competitor profiles in `concurrentie-analyse/{category}/{competitor-slug}/` 4. Inserts competitors and feature data into the database with provenance tracking 5. Presents a comparison table and recommendation for Nextcloud ecosystem fit diff --git a/docs/claude/examples/.mcp.json.example b/docs/claude/examples/.mcp.json.example index 3ee23759d..7e0a3ba22 100644 --- a/docs/claude/examples/.mcp.json.example +++ b/docs/claude/examples/.mcp.json.example @@ -3,30 +3,6 @@ "browser-1": { "command": "npx", "args": ["-y", "@playwright/mcp@latest", "--browser", "chromium", "--headless", "--isolated"] - }, - "browser-2": { - "command": "npx", - "args": ["-y", "@playwright/mcp@latest", "--browser", "chromium", "--headless", "--isolated"] - }, - "browser-3": { - "command": "npx", - "args": ["-y", "@playwright/mcp@latest", "--browser", "chromium", "--headless", "--isolated"] - }, - "browser-4": { - "command": "npx", - "args": ["-y", "@playwright/mcp@latest", "--browser", "chromium", "--headless", "--isolated"] - }, - "browser-5": { - "command": "npx", - "args": ["-y", "@playwright/mcp@latest", "--browser", "chromium", "--headless", "--isolated"] - }, - "browser-6": { - "command": "npx", - "args": ["-y", "@playwright/mcp@latest", "--browser", "chromium", "--isolated"] - }, - "browser-7": { - "command": "npx", - "args": ["-y", "@playwright/mcp@latest", "--browser", "chromium", "--headless", "--isolated"] } } } diff --git a/docs/claude/examples/browser-pool-shared.json.example b/docs/claude/examples/browser-pool-shared.json.example new file mode 100644 index 000000000..9d78f5480 --- /dev/null +++ b/docs/claude/examples/browser-pool-shared.json.example @@ -0,0 +1,14 @@ +{ + "mcpServers": { + "browser-1": { "type": "http", "url": "http://localhost:8931/mcp" }, + "browser-2": { "type": "http", "url": "http://localhost:8931/mcp" }, + "browser-3": { "type": "http", "url": "http://localhost:8931/mcp" }, + "browser-4": { "type": "http", "url": "http://localhost:8931/mcp" }, + "browser-5": { "type": "http", "url": "http://localhost:8931/mcp" }, + "browser-6": { + "command": "npx", + "args": ["-y", "@playwright/mcp@latest", "--browser", "chromium", "--isolated"] + }, + "browser-7": { "type": "http", "url": "http://localhost:8931/mcp" } + } +} diff --git a/docs/claude/global-claude-settings.md b/docs/claude/global-claude-settings.md index 219bf87de..8bcde4d25 100644 --- a/docs/claude/global-claude-settings.md +++ b/docs/claude/global-claude-settings.md @@ -296,8 +296,8 @@ Restart Claude Code or run `/hooks`. From then on your hooks fire alongside the `settings.json` does not read an `mcpServers` key ([Claude Code docs](https://code.claude.com/docs/en/debug-your-config#check-common-causes)), so MCP servers are **not** part of the global settings. Versions up to 2.4.0 shipped a dead `mcpServers` block with 7 Playwright browsers; it never loaded anything and was removed in 2.4.1. Configure MCP servers at one of the two supported scopes instead: -- **Project scope** — `.mcp.json` at the repository root, committed so the whole team gets the same servers. Hydra ships one with the 7-browser pool; a workspace that symlinks Hydra's `.claude/skills` symlinks its `.mcp.json` the same way. See [playwright-setup.md](playwright-setup.md). -- **User scope** — `claude mcp add --scope user …`, stored in `~/.claude.json` and loaded in every project on your machine. See [playwright-setup.md → User scope](playwright-setup.md#user-scope-all-projects-on-this-machine). +- **Project scope** — `.mcp.json` at the repository root, committed so the whole team gets the same servers. Hydra ships one with `browser-1` only; the seven-browser pool is opt-in under `.claude/mcp/` (shared server, URL entries). A workspace that symlinks Hydra's `.claude/skills` symlinks `.mcp.json` and `.claude/mcp` the same way. See [playwright-setup.md](playwright-setup.md). +- **User scope** — `claude mcp add --scope user …`, stored in `~/.claude.json` and loaded in every project on your machine. See [playwright-setup.md → The pool in VS Code](playwright-setup.md#the-pool-in-vs-code). When both define the same server name, the project-scope entry wins. diff --git a/docs/claude/playwright-setup.md b/docs/claude/playwright-setup.md index 7c6f2755c..6219d282b 100644 --- a/docs/claude/playwright-setup.md +++ b/docs/claude/playwright-setup.md @@ -1,165 +1,113 @@ # Playwright MCP Browser Setup -Each project workspace configures its own Playwright MCP browser sessions in a `.mcp.json` file at the project root. The Nextcloud workspace uses 7 browsers; other workspaces may use fewer depending on the parallelism their tests demand. An [example .mcp.json](./examples/.mcp.json.example) with the 7-browser configuration is available as a starting point. +Claude Code drives browsers through [`@playwright/mcp`](https://github.com/microsoft/playwright-mcp). Two things matter for how it is configured: -Hydra ships this file at its repository root (`hydra/.mcp.json`). A workspace that symlinks Hydra's `.claude/skills` should symlink `.mcp.json` the same way, so the browser pool always travels with the skills that depend on it: +1. **Claude Code starts every stdio MCP server in `.mcp.json` at session start**, whether or not the session ever touches a browser. A server costs ~60 MB idle (node + the `npm exec` wrapper); the browser itself is only launched on first use. +2. **A Playwright MCP server on a port serves many clients.** Every connection gets its own session and its own isolated browser context, so parallel agents stay separated exactly as they would with one process each. -```bash -ln -sfn /path/to/hydra/.mcp.json /path/to/workspace/.mcp.json -``` - -Do **not** put `mcpServers` in `~/.claude/settings.json` — Claude Code ignores that key there ([docs](https://code.claude.com/docs/en/debug-your-config#check-common-causes)). +So the setup is: one browser per project by default, and the seven-browser pool as an opt-in that points at a single shared server. -## Browser Pool (Nextcloud workspace) - -| Server | Mode | Purpose | -| ----------- | ---------- | --------------------------------- | -| `browser-1` | Headless | Main agent (default) | -| `browser-2` | Headless | Sub-agent / parallel | -| `browser-3` | Headless | Sub-agent / parallel | -| `browser-4` | Headless | Sub-agent / parallel | -| `browser-5` | Headless | Sub-agent / parallel | -| `browser-6` | **Headed** | User observation (visible window) | -| `browser-7` | Headless | Sub-agent / parallel | +> Background: on 2026-09-08 Hydra shipped the seven-browser pool as its root `.mcp.json` ([hydra#651](https://github.com/ConductionNL/hydra/pull/651)). Fourteen open Claude Code sessions then held 98 idle MCP processes and ~6 GB, and WSL ran out of memory. Hydra reverted to `browser-1` only and moved the pool to `.claude/mcp/`. -## VS Code Extension Setup +## Default: `browser-1` in `.mcp.json` -The VS Code extension loads MCP servers from `.mcp.json` in the **project root** (this file lives in each project repo, not in the `.github` documentation repo). The file defines 7 browser instances. Browsers 1–5 and 7 are headless; browser-6 is headed (omits `--headless`) so the browser window is visible when you want to watch: +Each project ships a `.mcp.json` at its root with a single headless browser. Copy the [example .mcp.json](./examples/.mcp.json.example): ```json { "mcpServers": { "browser-1": { "command": "npx", - "args": [ - "-y", - "@playwright/mcp@latest", - "--browser", - "chromium", - "--headless", - "--isolated" - ] - }, - "browser-2": { - "command": "npx", - "args": [ - "-y", - "@playwright/mcp@latest", - "--browser", - "chromium", - "--headless", - "--isolated" - ] - }, - "browser-3": { - "command": "npx", - "args": [ - "-y", - "@playwright/mcp@latest", - "--browser", - "chromium", - "--headless", - "--isolated" - ] - }, - "browser-4": { - "command": "npx", - "args": [ - "-y", - "@playwright/mcp@latest", - "--browser", - "chromium", - "--headless", - "--isolated" - ] - }, - "browser-5": { - "command": "npx", - "args": [ - "-y", - "@playwright/mcp@latest", - "--browser", - "chromium", - "--headless", - "--isolated" - ] - }, - "browser-6": { - "command": "npx", - "args": [ - "-y", - "@playwright/mcp@latest", - "--browser", - "chromium", - "--isolated" - ] - }, - "browser-7": { - "command": "npx", - "args": [ - "-y", - "@playwright/mcp@latest", - "--browser", - "chromium", - "--headless", - "--isolated" - ] + "args": ["-y", "@playwright/mcp@latest", "--browser", "chromium", "--headless", "--isolated"] } } } ``` -Project servers from `.mcp.json` need a one-time approval per workspace: accept the workspace-trust dialog when Claude Code first opens the folder, then approve the servers from `/mcp` if prompted. That is all — **no pre-approval settings are required**: +Hydra ships this file at its repository root (`hydra/.mcp.json`) and the pools under `hydra/.claude/mcp/`. A workspace that symlinks Hydra's `.claude/skills` should symlink both, so the browsers travel with the skills that depend on them: + +```bash +ln -sfn /path/to/hydra/.mcp.json /path/to/workspace/.mcp.json +ln -sfn /path/to/hydra/.claude/mcp /path/to/workspace/.claude/mcp +``` + +Do **not** put `mcpServers` in `~/.claude/settings.json` — Claude Code ignores that key there ([docs](https://code.claude.com/docs/en/debug-your-config#check-common-causes)). + +Project servers from `.mcp.json` need a one-time approval per workspace: accept the workspace-trust dialog when Claude Code first opens the folder, then approve the server from `/mcp` if prompted. No pre-approval settings are required: - `enableAllProjectMcpServers` only skips that one prompt. Hydra deliberately does not commit `.claude/settings.json` (it is machine-specific, see its `.gitignore`), and Claude Code ignores committed approvals in an untrusted folder anyway ([docs](https://code.claude.com/docs/en/mcp#project-server-approvals-and-workspace-trust)). -- An `mcp__browser-*` allow-list is not needed for parallel sub-agents (`/test-app` Full mode, `/test-counsel`): background subagents surface their permission prompts in the main session, and in auto mode the classifier evaluates their tool calls with the same rules as the main conversation ([subagent permissions](https://code.claude.com/docs/en/sub-agents)). Earlier versions of this page claimed sub-agents were "silently denied" without it; that is no longer how Claude Code behaves. +- An `mcp__browser-*` allow-list is not needed for parallel sub-agents (`/test-app` Full mode, `/test-counsel`): background subagents surface their permission prompts in the main session, and in auto mode the classifier evaluates their tool calls with the same rules as the main conversation ([subagent permissions](https://code.claude.com/docs/en/sub-agents)). Then **reload the VS Code window**: `Ctrl+Shift+P` → type `reload window` → Enter. -## Verification +## Browser pool (parallel agents) -After reload, open the MCP servers panel to verify all 7 browsers show **Connected**. You can do this two ways: +The skills that fan out — `/test-app` Full mode, `/test-counsel`, `opsx-pipeline` — assign one browser per agent: -- Type `/MCP servers` in the Claude Code chat input -- Or `Ctrl+Shift+P` → search **"MCP servers"** +| Server | Mode | Purpose | +| ----------- | ---------- | --------------------------------- | +| `browser-1` | Headless | Main agent (default) | +| `browser-2` | Headless | Sub-agent / parallel | +| `browser-3` | Headless | Sub-agent / parallel | +| `browser-4` | Headless | Sub-agent / parallel | +| `browser-5` | Headless | Sub-agent / parallel | +| `browser-6` | **Headed** | User observation (visible window) | +| `browser-7` | Headless | Sub-agent / parallel | + +The names are stable; only where the processes come from changed. Hydra keeps two pool files under `.claude/mcp/`: -![MCP servers panel showing all 7 browser instances connected](./img/mcp-servers-connected.png) +| File | Shape | When | +| ---- | ----- | ---- | +| `browser-pool-shared.json` | `browser-1`…`browser-5` and `browser-7` are `{"type": "http", "url": "http://localhost:8931/mcp"}`; `browser-6` stays a headed stdio entry | **Recommended.** Zero processes per session; needs the shared server (below) | +| `browser-pool.json` | seven stdio servers | No background process, 7 processes per session. Fallback when the shared server is not installed | -If any server shows an error, check the output panel: `Ctrl+Shift+P` → **"Output: Focus on Output"** → select **"Claude VSCode"** from the dropdown. +An [example of the shared pool](./examples/browser-pool-shared.json.example) is in this repo. Load a pool for one terminal session: -## User scope (all projects on this machine) +```bash +# the shared server must be running first — see "The shared server" below +claude --mcp-config .claude/mcp/browser-pool-shared.json +``` -To have the browser pool in every project on your machine — including repositories that do not ship a `.mcp.json` — register the servers at user scope. They are stored in `~/.claude.json` and load in every project. Run once: +### The shared server + +Hydra's `scripts/playwright-mcp-server.sh` runs one `@playwright/mcp` process on `localhost:8931` (headless, isolated). Install it once as a systemd user unit so it starts with your WSL session: ```bash -for i in 1 2 3 4 5 7; do - claude mcp add --scope user "browser-$i" -- npx -y @playwright/mcp@latest --browser chromium --headless --isolated -done -claude mcp add --scope user browser-6 -- npx -y @playwright/mcp@latest --browser chromium --isolated # headed +cd /path/to/hydra +scripts/playwright-mcp-server.sh install-service +scripts/playwright-mcp-server.sh status ``` -Verify with `claude mcp list`. A project `.mcp.json` that defines the same server name takes precedence over the user-scope entry, so both can coexist. Remove one with `claude mcp remove --scope user browser-N`. +`start` / `stop` / `restart` / `logs` / `uninstall-service` do what they say; `PLAYWRIGHT_MCP_PORT` changes the port (keep the pool file in sync). The server resolves `@playwright/mcp@latest` once, when it starts, so a long-running unit keeps that build until you `restart` it — unlike the old per-session stdio model, where every new session re-resolved `@latest`. The unit pins the `npx` it finds at install time, because a user unit does not load nvm. The headed `browser-6` is deliberately not served by it: a systemd user unit has no WSLg display, and observation browsers are rare enough that a per-session stdio entry is fine. -## CLI Alternative (terminal only) +Two concurrent MCP sessions against one server were verified to get distinct session ids and separate Chromium processes, so the isolation the numbered browsers promise still holds. -For the Claude Code CLI (`claude` terminal command, not VS Code), you can start servers as HTTP endpoints on fixed ports and reference them via URL: +### The pool in VS Code -```bash -# Start headless browsers -for port in 9221 9222 9223 9224 9225 9227; do - npx -y @playwright/mcp@latest --headless --isolated --port $port & -done +The VS Code extension cannot take `--mcp-config`. Register the URL entries at user scope instead; a URL entry costs a connection, not a process, so this is free per session. The project `.mcp.json` keeps winning for `browser-1`: -# Start headed browser -npx -y @playwright/mcp@latest --isolated --port 9226 & +```bash +for i in 2 3 4 5 7; do claude mcp add --scope user --transport http "browser-$i" http://localhost:8931/mcp; done +claude mcp add --scope user browser-6 -- npx -y @playwright/mcp@latest --browser chromium --isolated # headed, on demand ``` -> This is **not needed for VS Code** — the extension manages server processes automatically via `.mcp.json`. Only use this approach if you're running `claude` from the terminal without VS Code. +Verify with `claude mcp list`. Remove one with `claude mcp remove --scope user browser-N`. If the shared server is down, these entries show as failed in `/mcp` and nothing else breaks. + +## Verification + +After reload, open the MCP servers panel and check that `browser-1` shows **Connected** (plus whatever pool entries you registered): + +- Type `/MCP servers` in the Claude Code chat input +- Or `Ctrl+Shift+P` → search **"MCP servers"** + +![MCP servers panel showing browser instances connected](./img/mcp-servers-connected.png) + +If a server shows an error, check the output panel: `Ctrl+Shift+P` → **"Output: Focus on Output"** → select **"Claude VSCode"** from the dropdown. For the shared server, `scripts/playwright-mcp-server.sh status` and `journalctl --user -u playwright-mcp.service` are the places to look. ## Usage Rules 1. **Default**: Use `browser-1` for normal work -2. **Parallel agents**: Assign sub-agents `browser-2` through `browser-5` and `browser-7` +2. **Parallel agents**: Assign sub-agents `browser-2` through `browser-5` and `browser-7` — and check they are actually loaded first; the pool is opt-in 3. **User watching**: Switch to `browser-6` when the user wants to observe 4. **Fallback**: If a browser errors, try the next numbered browser 5. **Keep `browser-6` reserved**: Only for explicit user observation diff --git a/docs/claude/testing.md b/docs/claude/testing.md index 574f091d4..a3818059c 100644 --- a/docs/claude/testing.md +++ b/docs/claude/testing.md @@ -393,4 +393,4 @@ Ensure Docker is running and Nextcloud is accessible before testing. See [docker **Rule:** Single agent = `browser-1`. Parallel agents = each gets a distinct browser (`browser-2`, `browser-3`, `browser-4`, …) to avoid session conflicts. -For browser pool configuration, verification steps, and `.mcp.json` setup, see [playwright-setup.md](playwright-setup.md). +The default `.mcp.json` ships only `browser-1`; the parallel rows above need the opt-in pool (one shared server, URL entries). For configuration, verification and setup, see [playwright-setup.md](playwright-setup.md). diff --git a/docs/claude/workstation-setup.md b/docs/claude/workstation-setup.md index a40c05ae5..96234a807 100644 --- a/docs/claude/workstation-setup.md +++ b/docs/claude/workstation-setup.md @@ -292,6 +292,18 @@ npx playwright install chromium > If the MCP server reports a different revision is needed (e.g. after a `@playwright/mcp` update), run the install from the npx cache that the MCP server uses. You can find it at `~/.npm/_npx/` — look for the directory containing `@playwright/mcp`. +### Shared Playwright MCP server (recommended) + +Projects ship only `browser-1` in `.mcp.json`. The parallel-testing pool (`browser-2`…`browser-7`) points at one shared server, so it costs no processes per session. Install it once from the hydra checkout; it runs as a systemd user unit and starts with your WSL session: + +```bash +cd ~/hydra +scripts/playwright-mcp-server.sh install-service +for i in 2 3 4 5 7; do claude mcp add --scope user --transport http "browser-$i" http://localhost:8931/mcp; done +``` + +See [playwright-setup.md](./playwright-setup.md) for the why and the fallbacks. + ### OpenSpec CLI Used by all `/opsx-*` commands for spec-driven development: diff --git a/docs/iso/code.md b/docs/iso/code.md index 1e7681533..1feb70940 100644 --- a/docs/iso/code.md +++ b/docs/iso/code.md @@ -465,7 +465,7 @@ _What._ Determine and provide persons necessary. _What._ Determine, provide and maintain infrastructure (buildings, equipment, ICT, transport). -**Our implementation.** ICT infrastructure: GitHub (code + CI + issues + PRs), Cyso hosting (production, ISAE 3402), Docker Compose (dev/test), 7 Playwright MCP browser sessions (browser-1 … browser-7), Ollama + Qwen 3.5 for local LLM inference. Documented in CLAUDE.md and [.github/docs/claude/](../claude/). +**Our implementation.** ICT infrastructure: GitHub (code + CI + issues + PRs), Cyso hosting (production, ISAE 3402), Docker Compose (dev/test), one shared Playwright MCP server serving up to 7 named browser sessions (browser-1 … browser-7; only browser-1 is configured by default), Ollama + Qwen 3.5 for local LLM inference. Documented in CLAUDE.md and [.github/docs/claude/](../claude/). **Coverage.** _Full._