TTA · Workshop Guide
The Testing Academy · 2-Day Workshop · Fully Open-Source Stack

AI-Native Playwright Orchestration
with BrowserBash + open-source LLMs

Unguided, an LLM writes generic Playwright: wrong imports, new LoginPage(page), .btn-primary, waitForTimeout(2000), any everywhere. The fix is not a better prompt, it is an architecture the AI must work inside: an orchestrator that routes, a Constitution that forbids, skills that teach, and an open-source run layer that proves. By the closing lab the AI writes proper Playwright, orchestrates it, and runs it: BrowserBash plus a free local Ollama model, zero API keys. Two days, eleven sessions, eight labs, one runnable companion repo.

📅️ 2 days · 11 sessions 🧪 8 hands-on labs 📦 Runnable companion repo ✅ Suite passes against production 🔨 BrowserBash + Ollama: the zero-key stack
Hand-drawn end-to-end flow: a prompt routes through CLAUDE.md under RULES.md, loads only the matching lockfile-pinned skills, explores the real DOM and generates a compliant spec, then BrowserBash with a local Ollama model runs it and returns a verdict, looping back to explore until the run is green
Every session and lab on this page lives somewhere on this loop; Day 1 builds boxes 1 to 3, Day 2 builds the skills and the run layer.
The whole workshop in one line: stop re-prompting your standards into the AI every chat. Codify them once (Constitution + Skills, routed by CLAUDE.md) and a general-purpose LLM behaves like a specialist SDET that respects your architecture.
01

Day 1 · The Orchestration Pattern

From "why is AI-generated test code junk" to a working orchestrator over a real framework.

S1

The Unguided AI Problem

45 min

Teach

Open with the demo, not the theory: ask any LLM to "write a login test" for a framework it has never seen. Annotate the five defects it produces almost every time:

what the unguided AI writes
import { test } from '@playwright/test';          // wrong import: ignores custom fixtures
import { LoginPage } from '../pages/login.page';

test('login', async ({ page }) => {
  const loginPage = new LoginPage(page);          // manual instantiation: defeats DI
  await page.locator('.btn-primary').click();     // brittle CSS class selector
  await page.waitForTimeout(2000);                // hard wait: guaranteed flakiness
  const data: any = await page.evaluate(...);     // any: bypasses type safety entirely
});

Each line compiles. Each line is wrong for your framework. That distinction is the whole workshop.

Lab 0 · Reproduce the junk (15 min)

  1. Clone the companion repo and open it in your AI coding tool with CLAUDE.md temporarily renamed away.
  2. Prompt: "write a Playwright test for the TTA Bank login flow". Save the output.
  3. Count how many of the five defect classes appear. Keep the file; you will diff it against Day 1's final lab.

Every attendee has a saved "before" file with at least two of the five defect classes present.

S2

The Root Cause Is Zero Context

30 min

Teach

  • Generic knowledge. The model knows Playwright, the tool. It knows nothing about your fixtures, your import points, your selector policy: your architecture.
  • Session amnesia. Whatever rules you explained yesterday died with that chat. Every new conversation starts from zero.
  • Output inconsistency. Generic knowledge plus amnesia means every generation is a randomized coin flip against your conventions. Some days it lands. That is worse than always failing, because it teaches false trust.

The conclusion writes itself: the model does not need more intelligence, it needs your context, persistently. Give AI an architecture, not just a prompt.

S3

CLAUDE.md: The Conductor, Not the Musician

45 min

Teach

  • Always loaded. Every conversation starts by reading it. It is the one file that survives session amnesia.
  • Never writes code. Separation of concerns: the orchestrator holds no test code, no snippets to copy. It holds rules and routes.
  • Tells the AI HOW to generate. It routes: which Constitution rules apply, which skills to load, which loop to run.

Walk the real file: CLAUDE.md in the companion repo. Four blocks: Constitution, asset map, skills index, verification loop.

Context rot, the argument for modularity. The temptation is to stuff every example into the orchestrator. Overstuff it and the model starts forgetting rule number one while reading example number forty-seven. The orchestrator stays thin; depth lives in skills, loaded only when routed to.

Attendees can answer: why does CLAUDE.md contain zero test code, and what breaks if it does not stay thin?

S4

The Constitution: 5 Requirements, 5 Bans

60 min

Teach

Absolute requirements
  • Dependency Injectionfixtures from test-options.ts, never new PageObject(page)
  • Single Import Pointtest and expect come from fixtures/pom/test-options.ts only
  • Strict Selectorsladder: getByRole > getByLabel > getByPlaceholder, drops need a comment
  • Type SafetyZod schemas only, zero any in the codebase
  • Explore Before Generatenavigate to the real page or API first, never guess locators
Hard guardrails
  • No XPathcoupled to DOM structure, banned entirely
  • No Hard Waitspage.waitForTimeout() strictly forbidden
  • No Loose Schemasz.object() banned; z.strictObject() catches unmapped API changes
  • No Guessed Explorationsnapshot the live DOM (playwright-cli or MCP), never invent it from memory
  • No Hardcoded Datainline strings banned; Faker factories that self-validate

The payoff, line by line

Without orchestration
With the Constitution
import { test } from '@playwright/test'
import { test } from 'fixtures/pom/test-options'
const loginPage = new LoginPage(page)
test('...', ({ loginPage }) => { ... })
page.locator('.btn-primary')
page.getByRole('button', { name: 'Login' })
await page.waitForTimeout(2000)
await expect(locator).toBeVisible()
const data: any = ...
const data = UserSchema.parse(...)
Where the law lives in the companion repo: the Constitution is a standalone RULES.md, the single normative source with explicit precedence; CLAUDE.md is a thin adapter that references it (asset map, skills index, loop only), and npm run verify:governance fails the build if an adapter restates the law or a skill hash stops matching skills-lock.json.

Lab 1 · Prove a rule with code (25 min)

  1. Pick one requirement and one ban. Find where the companion repo enforces each: DI lives in fixtures/pom/page-object-fixture.ts, strict schemas in fixtures/api/schemas/, the self-validating factory in test-data/factories/.
  2. Break the rule on purpose (add a waitForTimeout, loosen a schema to z.object) and articulate what failure class you just re-enabled.
  3. Revert. The framework is the proof of the Constitution: every rule already exists as working code.

Each attendee can map all ten rules to a concrete file in the repo.

S5

The 8-Step Loop and the Asset Map

45 min

Teach

step 8 loops back to step 2 until the run is green

Two anchors carry the whole loop: explore before generate (step 2) and run before report (step 8). Everything between them is bookkeeping.

The asset map

Without a map, the AI invents paths. The map's {area} placeholders are the clever part: they force the model to run ls and discover the real folder names instead of hallucinating them.

CLAUDE.md, the asset map
pages/{area}/[name].page.ts
test-data/factories/{area}/[name].factory.ts
fixtures/api/schemas/{area}/[name]Schema.ts
# {area} is discovered with ls, never guessed (currently: app)

Lab 2 · Explore before generate (25 min)

  1. Point your agent at the live TTA Bank send-money page and have it snapshot the accessibility tree before writing anything.
  2. Ask for the locator of the confirm button. Compare against the shipped page object: the snapshot yields getByRole('button', { name: 'Confirm and send' }), not a CSS class.
  3. Ask the agent to place a new page object file. Watch it run ls pages/ first because the map told it to.

The agent produced a role-based locator from a real snapshot and a correct path from a real ls.

S6

Live Build: The Same Prompt, With and Without

60 min

Teach + demo

The routing pattern, end to end, on one prompt ("create a page object"):

Ingestprompt hits CLAUDE.md; Constitution + skills index load
Routeindex sends the AI to page-objects + selectors skills
Executeskills trigger the explore loop; the AI snapshots the browser
Generatecode follows strict DI rules
Verifythe AI runs the tests before returning output

Lab 3 · The before/after diff, live (35 min)

  1. Restore CLAUDE.md (from Lab 0's rename). Same tool, same prompt as Lab 0: "write a Playwright test for the TTA Bank login flow".
  2. Diff today's output against your saved "before" file. Score both against the five defect classes.
  3. Finish with the repo's own proof: npm test. The suite registers a fresh user against the live app, transfers money, validates the API contract, and passes.

Day 1 exit: a diff showing the same model producing compliant code, and a green run as evidence. The metaphor to close on: orchestrator = conductor, Constitution = key signature, skills = musicians.

02

Day 2 · The Agent Skills Playbook

From "what is a skill" to shipping your own, then closing the loop with a fully open-source run layer.

S7

Why Real Work Needs Procedural Knowledge

45 min

Teach

Without procedure: repeat the prompt, get inconsistent output, start over, waste effort. With a skill: define the workflow once, parameterize the input, version-control the folder, get the same expert behavior every time.

LayerPersistenceContainsBest for
Promptssingle conversationnatural languagemoment-to-moment direction
Projectswithin a workspacedocuments + contextbackground knowledge
Skillsacross conversationsinstructions + codeprocedural knowledge
Subagentsacross sessionsfull agent logictask delegation
MCPcontinuous connectiondata accesstool connectivity
The kitchen and the recipe. MCP is connectivity: it tells the model what it CAN do (reach GitHub, Notion, a browser). Skills are knowledge: they tell it how it SHOULD do it (your sequences, your standards). A kitchen full of appliances still needs recipes.
S8

Skill Anatomy, Progressive Disclosure, and the Trigger

60 min

Teach

  • Directory: SKILL.md is required (frontmatter + instructions); optional /scripts/ for deterministic executables, /references/ for depth loaded on demand, /assets/ for templates.
  • Progressive disclosure (the deck's guidance figures): level 1 is the frontmatter, roughly a hundred tokens, always loaded; level 2 is the body, kept under about five thousand tokens, loaded on trigger; level 3 is linked files, loaded only when a step needs them.
  • The trigger is the most critical component. A description must answer two questions: what does this do, and when exactly should it fire.
selectors/SKILL.mdIllustration
---
name: selectors
description: >
  Chooses stable Playwright locators using the
  strict ladder. Use when writing any new
  locator, fixing a flaky one, or reviewing
  selector quality in a page object.
---

The trigger. Kebab-case name; description states capability AND exact firing conditions.

## Context
This framework bans CSS classes and XPath.
The ladder exists because role locators
survive redesigns; class names do not.

The context. What the skill accomplishes and why it exists.

## Recipe
1. Snapshot the real element first.
2. Try getByRole(role, { name }).
3. Fall through the ladder with a comment
   for every drop.
4. Prove stability: run the spec twice.

The recipe. Numbered, executable procedural knowledge, not passive documentation.

Description craft

DescriptionVerdict
"Helps with projects"Bad: no capability, no trigger
"Creates sophisticated multi-page documentation systems"Bad: capability but zero trigger phrases
"Manages Linear project workflows. Use when the user mentions 'sprint', 'Linear tasks', or asks to 'create tickets'"Good: capability plus exact firing conditions

Attendees can rewrite a vague description into a trigger-rich one and say which disclosure level each part of a skill lives at.

S9

Determinism, Advanced Patterns, and skills-lock.json

45 min

Teach

  • Structuring for determinism: explicit numbered phases; concrete "user says X, actions, result" examples; designed error handling ("if connection refused, verify the API key"); bullets over prose, depth pushed to /references/.
  • Five advanced patterns: sequential orchestration (onboard, then payment, then email); multi-MCP coordination (Figma to Drive to Linear); iterative refinement (draft, validate, loop, finalize); context-aware routing (decision trees by input); domain-specific intelligence (business rules like compliance checks before payment).
  • Validation, three axes: triggering tests (fires on target phrases, stays dormant off-scope), functional tests (the calls succeed, edge cases handled), performance baselines (less manual prompting, lower total tokens).

Skills as dependencies

The companion repo pins every skill in a lockfile, exactly like package-lock.json for code. Rarely discussed anywhere, and one of the sharpest ideas in the source material: knowledge as a versioned, hash-verified dependency.

skills-lock.json (companion repo, trimmed)
{
  "version": 1,
  "skills": {
    "selectors": {
      "source": ".agents/skills/selectors",
      "sourceType": "local",
      "computedHash": "7dd5957d812f..."
    }
  }
}
Deploy once, run everywhere: the same skill folder uploads to the Claude apps, drops into a skills directory for Claude Code, or is managed programmatically through the API's skills endpoints for pipelines at scale.
S10

Build, Validate, and Pin Your Own Skill

90 min

The capstone lab block

Lab 4 · Author a skill (30 min)

  1. Pick a repetitive correction you make to AI output every week (for testers: wait strategy, test data, tagging conventions).
  2. Write SKILL.md: trigger-rich frontmatter, context, numbered recipe, one worked example, one error-handling clause. Model it on the five skills in the companion repo's .agents/skills/.

Lab 5 · Validate on three axes (25 min)

  1. Triggering: ask three in-scope questions and three off-scope ones; the skill must fire on exactly the right three.
  2. Functional: run the recipe on a real task in the repo; the output must obey the Constitution.
  3. Baseline: compare the prompting you needed before and after; the skill should replace your repeated paragraph.

Lab 6 · Pin it (15 min)

  1. Add the skill folder under .agents/skills/, register it in the CLAUDE.md skills index with a one-line routing condition.
  2. Add its content hash to skills-lock.json. Commit: your team now installs your expertise with git pull.

Workshop exit: every attendee leaves with one working, validated, pinned skill and the full loop internalized: identify the repetitive workflow, codify it, connect tools, execute consistently.

S11

The Open-Source Finale: BrowserBash + Ollama Run the Suite

60 min

Teach

Everything so far taught the AI to write compliant Playwright. The finale closes the loop with a stack that is open source top to bottom: the AI also orchestrates and runs what it wrote, with zero API keys.

  • BrowserBash (Apache-2.0): one plain-English sentence in, a real browser run out, and a machine-readable verdict back (status, extracted values, deterministic assertion results, cost). Its Verify steps compile to real Playwright checks with no model in the loop.
  • Three swappable layers, free by default: provider (your local Chrome), engine (Stagehand, MIT), LLM (local Ollama first; any OpenAI-compatible server works). The default path costs nothing and phones no one.
  • MCP built in: browserbash mcp exposes run_objective, run_test_file, and run_suite to any MCP host. Step 8 of the loop ("run and fix") stops being a guess and becomes a tool call with exit codes your pipeline already understands.
  • qaskills.sh: a directory of 450+ ready-made QA skills installable into the same .agents/skills/ layout with one command, then governed exactly like the skills you wrote in S10: registered in the index, pinned in the lockfile.
the zero-key stack
# the run layer
npm install -g browserbash-cli
ollama pull qwen3

# plug it into the orchestrator as MCP verification tools
claude mcp add browserbash -- browserbash mcp

# install a ready-made skill from the directory
npx @qaskills/cli add playwright-e2e

Lab 7 · Write, orchestrate, run (35 min)

  1. Install the stack above, then prove the run layer alone: browserbash run "Open https://app.thetestingacademy.com/playwright/tta-bank/ and store the page heading as 'h1'" --headless.
  2. Add the MCP line and re-run Day 1's Lab 3 prompt. The orchestrator now generates the spec per RULES.md, and verifies it by calling run_test_file for a structured verdict instead of parsing terminal noise.
  3. Point browserbash import at the companion repo's specs and read the generated plain-English twins plus the honest IMPORT-REPORT of what could not be translated.
  4. Install one skill from qaskills.sh, register it in the CLAUDE.md index, re-pin skills-lock.json, and confirm npm run verify:governance stays green.

Workshop finale: the same AI that wrote the test ran it through an open-source runner on a local model and returned a verdict, and every skill in the repo is pinned and governed. Write, orchestrate, run: all three, no API key in sight.

Materials and facilitator notes

AssetWhat it isWhere
Companion repo (ours)Complete implementation: CLAUDE.md, 5 skills, skills-lock, DI scaffold, suite green against productionPramodDutta/AI-Native-Playwright-Orchestration
Ivan's workshop repoThe original scaffold (MIT); the orchestration layer is built live in his workshop, which is exactly what makes it great teaching materialidavidov13/Orchestrating-AI-Native-Testing-with-Playwright
App under testTTA Bank practice app + orders API, public and stable, locator-rich by designTTA Bank · practice hub
BrowserBashFree, open-source (Apache-2.0) plain-English browser automation CLI with a built-in MCP server and deterministic Playwright Verify checks; the workshop's run layerbrowserbash.com
QASkills directory450+ installable QA skills for the major AI coding agents, one command per skillqaskills.sh
Skills craft deep-diveOur full masterclass on building agent skillsSkills Masterclass
Related buildA code-review skill wired to Playwright MCP, the same explore-verify philosophyQA Code-Review Skill
Prerequisites checklist (send before Day 1)

Node 20+, git, an AI coding tool that reads CLAUDE.md and skill folders (Claude Code recommended), one npm install && npx playwright install chromium run completed, and the companion repo cloned. For the S11 finale: npm install -g browserbash-cli and a pulled Ollama model (ollama pull qwen3), both free. Windows attendees: if PowerShell blocks npm scripts, the usual fix is Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser (on managed machines, check policy first or use cmd.exe).

Timing and energy notes

Day 1 is demo-heavy: keep Lab 0's "junk file" visible on a second screen all day, it is the emotional anchor. S4 runs long when the bans spark debate; cap discussion at two rules and point the rest to the repo. Day 2's S10 is the payoff: protect the full 90 minutes. Close both days by re-running npm test: ending on a green suite lands the run-before-report habit better than any slide.

Claims worth being careful with

The token figures in S8 (about a hundred for frontmatter, under about five thousand for the body) are the source deck's guidance numbers, not measured constants. The "13 skills" figure from the source is a plan: seven are named, and our companion repo ships five fully written ones instead. Phrase the lockfile idea as "rarely discussed", not "first ever".