e2e-verification
The e2e-verification skill drives the running Chorus dev server in a real browser using Playwright to manually verify frontend changes. Use it when acceptance criteria require human confirmation that a user sees specific UI behavior: log in with local credentials stored in .env, navigate to the target page, and capture snapshots or screenshots to validate the actual rendering and state.
git clone --depth 1 https://github.com/Chorus-AIDLC/Chorus /tmp/e2e-verification && cp -r /tmp/e2e-verification/.claude/skills/e2e-verification ~/.claude/skills/e2e-verificationSKILL.md
# E2E Verification (Playwright + local login)
## Overview
Drive the **running** Chorus dev server in a real browser via the Playwright MCP to verify UI changes end-to-end. Core loop: **log in with local credentials → navigate → snapshot to read state / act → screenshot for the human**.
This is manual acceptance, not the automated Vitest suite (`pnpm test`). Use it when an AC says "the user sees X" and only a real browser can confirm it.
## Prerequisites
- **Dev server healthy** on port **8637**. Always check first:
```bash
curl -s -o /dev/null -w "%{http_code}" http://localhost:8637/login # expect 200
```
If it's not `200` (connection refused, 500, or anything else), the server isn't running or is broken — start it with `pnpm dev:local` and re-check until it returns `200` before driving the browser. (Plain `pnpm dev` works too, but `pnpm dev:local` brings up the full local stack.)
- **Playwright MCP tools available.** They are deferred — load them first:
`ToolSearch("select:mcp__playwright__browser_navigate,mcp__playwright__browser_snapshot,mcp__playwright__browser_take_screenshot,mcp__playwright__browser_click,mcp__playwright__browser_fill_form")`
## Step 1 — Find credentials
Local dev uses the **default-auth** path (plain email + password on `/login`). Read it from the `.env` file at the repo root:
```bash
grep -E "DEFAULT_USER|DEFAULT_PASSWORD" .env
# DEFAULT_USER="admin@chorus.local"
# DEFAULT_PASSWORD="chorus"
```
`DEFAULT_USER` logs in as a regular workspace user (e.g. `admin@chorus.local`). This is NOT the same as `SUPER_ADMIN_EMAIL` (the `/login/admin` panel) or the Cognito OIDC flow described in the older `.claude/skills/oidc-login.md` — for normal page verification you want default-auth.
## Step 2 — Log in (verified flow)
When default-auth is enabled, `/login` shows the email+password form **directly** (no SSO redirect):
1. `browser_navigate` → `http://localhost:8637/login`
2. `browser_snapshot` → grab the `ref` of the Email box, Password box, and "Sign In" button.
3. `browser_fill_form` with both fields:
```
[{ name: "Email", target: "<email-ref>", type: "textbox", value: "admin@chorus.local" },
{ name: "Password", target: "<password-ref>", type: "textbox", value: "chorus" }]
```
4. `browser_click` the "Sign In" button.
5. **Success = URL becomes `/projects`.** The tool result echoes `Page URL` — confirm it changed. If it stays on `/login` with an error banner, the credentials or the server's default-auth config are wrong.
Already authenticated from a prior session? The browser keeps the session, so you can skip straight to Step 3. To test the login itself, sign out first: on any dashboard page click the **"Sign out"** button (bottom-left of the sidebar), which returns you to `/login`. (If `/login` already shows the email+password form, you're logged out — there's no "Sign out" button to find; just proceed with Step 2.)
## Step 3 — Navigate & observe
- `browser_navigate` to the target, e.g. a project dashboard:
`http://localhost:8637/projects/<project-uuid>/dashboard`
- **`browser_snapshot` is the workhorse** — it returns an accessibility tree with stable `ref` ids. Use it to read page state and to get the `ref`/`target` you pass to `browser_click`, `browser_fill_form`, etc. Prefer it over screenshots for *acting*.
- Toggle UI and re-snapshot to compare states (e.g. the dashboard's Ideas/Stats and Flat/Lineage segmented controls each re-render the tree).
- If a navigate returns a near-empty tree, the page is still hydrating — call `browser_snapshot` again.
## Step 4 — Screenshot for the human
Use `browser_take_screenshot` when the human needs to *see* the result (visual layout, spacing, color).
**Always prefix the filename with `.playwright-mcp/`** — that directory is gitignored. A bare `filename: "foo.png"` saves to the **repo root** and pollutes `git status`.
```
browser_take_screenshot({ type: "png", filename: ".playwright-mcp/dashboard-lineage.png" })
```
Then `Read` it to view it inline. The screenshot tool result echoes a **repo-relative** path; `Read` needs an **absolute** one, so read it as `<repo-root>/.playwright-mcp/<name>.png` (prefix the path you passed with the absolute repo root). To capture a single element instead of the viewport, pass its `target` ref (from a snapshot). The MCP also auto-saves snapshot `.yml` and console `.log` files under `.playwright-mcp/`.
## Quick reference
| Need | Tool |
|------|------|
| Go to a URL | `browser_navigate` |
| Read page state / get element refs | `browser_snapshot` |
| Click / type / fill | `browser_click`, `browser_type`, `browser_fill_form` |
| Visual capture for the human | `browser_take_screenshot` → `.playwright-mcp/<name>.png` |
| Check console errors | `browser_console_messages` |
## Common mistakes
| Mistake | Fix |
|---------|-----|
| Calling `browser_*` before loading schemas | `ToolSearch("select:mcp__playwright__browser_...")` first; bare calls fail with InputValidationError. |
| Screenshot with bare filename | Prefix `.playwright-mcp/` or it lands in repo root (not gitignored). |
| Following the old `oidc-login.md` (Cognito) for local dev | Local dev = default-auth (`.env` `DEFAULT_USER`/`DEFAULT_PASSWORD`) → `/api/auth/default-login` → `/projects`. |
| Acting on coordinates from a screenshot | Screenshots aren't actionable; get `ref`s from `browser_snapshot`. |
| "Login failed" but credentials look right | Confirm dev server is up on 8637 and `Page URL` actually moved off `/login`. |
| `find`-ing for a saved screenshot across the FS | It's already at the `.playwright-mcp/<name>.png` you passed; just `Read` it. |Write release blog posts for Chorus — problem-first narrative, bilingual (zh/en), following the project's editorial style.
Optional divergent-then-convergent dialogue for fuzzy ideas. Invoked from the idea skill as a prelude to structured elaboration; produces one ElaborationRound of decision-point Q&A and returns control. Never writes files, never posts comments, never resolves elaboration.
Optional divergent-then-convergent dialogue for fuzzy ideas. Invoked from the idea skill as a prelude to structured elaboration; produces one ElaborationRound of decision-point Q&A and returns control. Never writes files, never posts comments, never resolves elaboration.
Chorus AI Agent collaboration platform — overview, common tools, setup, and routing to stage-specific skills.
Read-only Chorus code-review gateway — the final ship-time review of an Idea''s aggregate code change (the whole feature across all its tasks, not one task). Fetches the Idea, its approved proposals, documents, and tasks via MCP, reviews the aggregate implementation, and posts a structured VERDICT comment on the Idea. Invoke by mounting this skill into a default sub-agent via spawn_agent(agent_type="default", items=[{ type: "skill", path: "chorus:chorus-code-reviewer", ... }, { type: "text", text: "Review the code for idea <uuid>. Round: N." }]).
Read-only Chorus proposal reviewer. Fetches a proposal via MCP, audits PRD/task drafts against the originating Idea, and posts a structured VERDICT comment. Invoke by mounting this skill into a default sub-agent via spawn_agent(agent_type="default", items=[{ type: "skill", path: "chorus:chorus-proposal-reviewer", ... }, { type: "text", text: "Review proposal <uuid>. Max review rounds: 3." }]).
Read-only Chorus task reviewer. Fetches a task plus its acceptance criteria plus originating proposal documents via MCP, independently verifies the implementation, and posts a structured VERDICT comment. Invoke by mounting this skill into a default sub-agent via spawn_agent(agent_type="default", items=[{ type: "skill", path: "chorus:chorus-task-reviewer", ... }, { type: "text", text: "Review task <uuid>. Max review rounds: 3." }]).
Final ship-time review of an Idea's aggregate code change — the whole feature across all its tasks, not one task. Read the integrated code, check cross-task integration / architecture / security / regression / coverage, run tests. Invoke after the last task of an idea-rooted proposal is verified; ends with a VERDICT comment on the Idea.