Operating the Walnut web UI (localhost:3456) via Playwright MCP tools — understanding the UI layout, chatting with main agent, interacting with sessions, monitoring status changes. Use when asked to test, verify, or interact with the Walnut web console as a human user would.
Installs into .claude/skills of the current project.
Are you the author of Walnut Console Ops?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/evanzhang008-walnut-console-ops)
---
name: walnut-console-ops
description: Operating the Walnut web UI (localhost:3456) via Playwright MCP tools — understanding the UI layout, chatting with main agent, interacting with sessions, monitoring status changes. Use when asked to test, verify, or interact with the Walnut web console as a human user would.
requires:
bins: []
---
# Walnut Web Console — How It Works
This skill explains the Walnut web UI so you can operate it confidently via Playwright MCP tools, acting as a human user.
**Which server.** Looking at the real console on :3456 is fine. Anything that creates or changes data to test a flow (a new task, a session, a sent message, a cron job) runs on a throwaway server instead: `npm run dev:ephemeral`, then open the printed port. Test data made on :3456 stays on the user's board. The ephemeral server's sessions and tools talk only to it (see "Live verification" in the repo AGENTS.md).
## Core Mental Models for SPA Console Testing
These principles are universal — they apply to any single-page app, not just Walnut.
### 1. Stay on one page — panels, not pages
SPAs show everything through panels that open/close on the same page. Don't navigate away to find something. The session panel opens inline when you click a session link. (The dedicated `/sessions` page was removed — `/sessions?id=…` links reroute to the home session columns.)
### 2. Disambiguate inputs by placeholder
Multiple input boxes coexist on screen. They look identical in the DOM. **Placeholder text** is the reliable discriminator. Check it before typing.
### 3. Same state, multiple renderings — verify all
One piece of state (e.g., "session mode") appears in 3+ places, each with different update paths (REST vs WebSocket vs React state). Check all of them. Bugs often show as one updating while another stays stale.
### 4. Act → Wait → Verify
Every action triggers async work. Pattern: **Act** → **Wait** (5-20s) → **Screenshot** (visual truth) → **Snapshot** (clickable refs). Skip the wait = see stale state.
### 5. Screenshot is ground truth
Screenshot shows what the user sees. DOM snapshot shows logical tree. When they disagree, trust the screenshot.
### 6. Refs are ephemeral
Playwright refs (`ref=e1234`) are tied to a specific DOM state. Any async change invalidates them. Always re-snapshot before interacting.
### 7. Build ≠ Deploy
Frontend changes (vite build) take effect on page refresh. Backend changes (npm run build) need server restart. Common mistake: build the fix, test, see no change — server is still running old code.
### 8. Trace WebSocket events through 4 stages
When the UI doesn't update: (1) Did backend emit the event? → (2) Did WebSocket forward it? → (3) Did frontend listen for it? → (4) Did component re-render? The bug is always at one stage.
## Understanding Session-Based Apps
These patterns apply to any app where you chat with an AI agent that spawns background sessions.
### Two conversation channels
There's a **main agent** (orchestrator) and **individual sessions** (workers). They have separate inputs. Typing in the wrong one sends your message to the wrong place. The main agent can create sessions, but once a session exists, you interact with it directly through its own input.
### Session lifecycle and status
Sessions go through states: created → running → stopped. They also have a **work status** (in_progress, awaiting_human, agent_complete) and a **mode** (bypass, plan, etc.). These update in real-time via WebSocket. Status displays in multiple UI locations, each updated by different code paths.
### Opening a session inline
When the agent creates a session and shows a link, click it to open a **session panel inline** on the same page. Don't navigate to a separate sessions page. The session panel has its own chat history, input box, and status header — all distinct from the main chat.
## Walnut Home Page Layout (`/`)
```
┌────────┬─────────────────┬─────────────────┬──────────────────┐
│Sidebar │ Main Chat │ SessionPanel │ TodoPanel │
│ │ │ │ ┌─────────────┐ │
│ Home │ Chat history │ Session header │ │ Task list │ │
│ Tasks │ (main agent) │ (status, mode) │ │ (grouped by │ │
│ Sess. │ │ │ │ cat → proj)│ │
│ Search │ │ Session chat │ ├─────────────┤ │
│ Memory │ │ (You ↔ Claude) │ │ TodoDetail │ │
│ ... │ │ │ │ Sessions │ │
│ │ ┌─────────────┐ │ │ │ Summary │ │
│ │ │ Chat input │ │ Session input │ │ Triage │ │
│ │ └─────────────┘ │ │ └─────────────┘ │
│ │ Task context │ │ Quick add │
└────────┴─────────────────┴─────────────────┴──────────────────┘
```
SessionPanel only appears when a session is open. Without it, Main Chat takes more space.
### 1. Main Chat
Where you talk to the **main agent**. It can create tasks, start sessions, query data, run tools. When it starts a session, it responds with a clickable session link:
> ✅ Session started: [Session Title](/sessions?id=xxx)
Input placeholder: `"Type a message... (/ for commands)"` or `"Ask about '<task name>'..."`
### 2. TodoPanel (right side, top)
Task list grouped by Category → Project. Each task row shows phase icon, title, **SessionPill** (e.g., `plan · Awaiting Human / Stopped`), priority, star. Clicking a task opens TodoDetailPanel below.
### 3. TodoDetailPanel (right side, bottom — when task selected)
Shows detail for the selected task:
- **Sessions list**: mode badge (**Plan** or none for bypass), work status, timestamp, clickable `↗`
- **Summary (AI)**: triage-generated summary
- **Triage history**: `View Triage History (N) →`
Clicking a session in this list also opens SessionPanel.
### 4. SessionPanel (middle — when session open)
Session chat with the Claude Code CLI process. Contains:
- **Header**: session title + process status (Running/Stopped) + work status (Awaiting Human, In Progress)
- **Task link**: `📋 Task Name ↗`
- **My Messages**: collapsible list of messages you've sent
- **Chat history**: full conversation with tool calls, thinking blocks, token counts
- **Input**: `"Send a message to this session... (/ for commands)"`
The session input sends messages **directly to the Claude Code CLI process** — not through the main agent.
### How Panels Connect
- Click **task** in TodoPanel → TodoDetailPanel opens, task context set in main chat
- Click **session link** in main chat → SessionPanel opens inline (stays on `/`)
- Click **session** in TodoDetailPanel → SessionPanel opens
- Close SessionPanel (`×`) → returns to layout without it
## Input Boxes
| Input | Placeholder | Purpose |
|---|---|---|
| Main chat | `"Type a message..."` or `"Ask about '...'"` | Talk to main agent |
| Session chat | `"Send a message to this session..."` | Talk directly to Claude Code CLI |
| Task search | `"Search tasks... ⌘K"` | Filter tasks in TodoPanel |
| Quick add | `"Quick add task..."` | Create task directly (no agent) |
## Common Actions
### Create a Task + Session
Talk to the main agent: `"Create task 'My Task' in Projects/Walnut, start a bypass session with prompt 'Just say HI'"`
Session modes: **bypass** (execute immediately), **plan** (investigate → write plan → ExitPlanMode)
### Interact with a Session
1. Find session link in chat or TodoDetailPanel
2. Click it → SessionPanel opens inline
3. Click session input (`"Send a message to this session..."`)
4. Type → Enter
5. Wait → screenshot → snapshot to verify
### Monitor Status Changes
After an action, check **three places**:
- **SessionPanel header**: process + work status
- **TodoPanel SessionPill**: mode prefix (`plan` / `session`) + status
- **TodoDetailPanel sessions list**: mode badge + work status label
## Routes
| Route | What it is |
|---|---|
| `/` | Home — main chat + SessionPanel + TodoPanel (most common) |
| `/tasks` | Full task board |
| `/tasks/:id` | Task detail page (standalone, different from TodoDetailPanel) |
| `/search` | Full-text search |
Most of the time, `/` gives you everything you need.
## Interact Like a Human
**Prefer**:
- `browser_take_screenshot` — see what a real user sees, catch visual bugs
- `browser_snapshot` — accessibility tree with clickable refs
- `browser_click` — click buttons, links, tabs like a user would
- `browser_type` + `browser_press_key` — type into inputs and press Enter
**Use sparingly**:
- `browser_evaluate` / `browser_run_code` — useful for waits, but don't bypass the UI
- Direct DOM manipulation — defeats the purpose of testing
Principle: if a human would click a button, you click that button.
## Debugging Tips
- **`browser_snapshot`** — most useful tool, gives refs for clicking
- **`browser_take_screenshot`** — visual truth when DOM looks fine but something's wrong
- **Refs change** after DOM updates — re-snapshot before interacting
- **`browser_run_code`** — `await page.waitForTimeout(5000)` for delays
- **`browser_network_requests`** — check API calls and WebSocket frames
- **`browser_console_messages`** — check for JS errors
- **Server logs** (`/tmp/walnut/walnut-YYYY-MM-DD.log`) — when UI doesn't update, check if backend sent the event
- **WebSocket events**: `session:status-changed` carries mode/work_status/process_status; `task:updated` carries task data; `session:started`/`session:ended` for lifecycle