Day 1 · The Orchestration Pattern
From "why is AI-generated test code junk" to a working orchestrator over a real framework.
The Unguided AI Problem
45 minTeach
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:
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)
- Clone the companion repo and open it in your AI coding tool with
CLAUDE.mdtemporarily renamed away. - Prompt: "write a Playwright test for the TTA Bank login flow". Save the output.
- 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.
The Root Cause Is Zero Context
30 minTeach
- 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.
together: the Orchestrator
CLAUDE.md: The Conductor, Not the Musician
45 minTeach
- 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.
Attendees can answer: why does CLAUDE.md contain zero test code, and what breaks if it does not stay thin?
The Constitution: 5 Requirements, 5 Bans
60 minTeach
- 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
- 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
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)
- Pick one requirement and one ban. Find where the companion repo enforces each: DI lives in
fixtures/pom/page-object-fixture.ts, strict schemas infixtures/api/schemas/, the self-validating factory intest-data/factories/. - Break the rule on purpose (add a
waitForTimeout, loosen a schema toz.object) and articulate what failure class you just re-enabled. - 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.
The 8-Step Loop and the Asset Map
45 minTeach
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.
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)
- Point your agent at the live TTA Bank send-money page and have it snapshot the accessibility tree before writing anything.
- 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. - 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.
Live Build: The Same Prompt, With and Without
60 minTeach + demo
The routing pattern, end to end, on one prompt ("create a page object"):
Lab 3 · The before/after diff, live (35 min)
- 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". - Diff today's output against your saved "before" file. Score both against the five defect classes.
- 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.
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.
Why Real Work Needs Procedural Knowledge
45 minTeach
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.
| Layer | Persistence | Contains | Best for |
|---|---|---|---|
| Prompts | single conversation | natural language | moment-to-moment direction |
| Projects | within a workspace | documents + context | background knowledge |
| Skills | across conversations | instructions + code | procedural knowledge |
| Subagents | across sessions | full agent logic | task delegation |
| MCP | continuous connection | data access | tool connectivity |
Skill Anatomy, Progressive Disclosure, and the Trigger
60 minTeach
- Directory:
SKILL.mdis 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.
--- 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
| Description | Verdict |
|---|---|
| "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.
Determinism, Advanced Patterns, and skills-lock.json
45 minTeach
- 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.
{
"version": 1,
"skills": {
"selectors": {
"source": ".agents/skills/selectors",
"sourceType": "local",
"computedHash": "7dd5957d812f..."
}
}
}Build, Validate, and Pin Your Own Skill
90 minThe capstone lab block
Lab 4 · Author a skill (30 min)
- Pick a repetitive correction you make to AI output every week (for testers: wait strategy, test data, tagging conventions).
- 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)
- Triggering: ask three in-scope questions and three off-scope ones; the skill must fire on exactly the right three.
- Functional: run the recipe on a real task in the repo; the output must obey the Constitution.
- Baseline: compare the prompting you needed before and after; the skill should replace your repeated paragraph.
Lab 6 · Pin it (15 min)
- Add the skill folder under
.agents/skills/, register it in the CLAUDE.md skills index with a one-line routing condition. - Add its content hash to
skills-lock.json. Commit: your team now installs your expertise withgit 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.
The Open-Source Finale: BrowserBash + Ollama Run the Suite
60 minTeach
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 mcpexposesrun_objective,run_test_file, andrun_suiteto 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 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)
- 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. - 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_filefor a structured verdict instead of parsing terminal noise. - Point
browserbash importat the companion repo's specs and read the generated plain-English twins plus the honest IMPORT-REPORT of what could not be translated. - Install one skill from qaskills.sh, register it in the CLAUDE.md index, re-pin
skills-lock.json, and confirmnpm run verify:governancestays 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
| Asset | What it is | Where |
|---|---|---|
| Companion repo (ours) | Complete implementation: CLAUDE.md, 5 skills, skills-lock, DI scaffold, suite green against production | PramodDutta/AI-Native-Playwright-Orchestration |
| Ivan's workshop repo | The original scaffold (MIT); the orchestration layer is built live in his workshop, which is exactly what makes it great teaching material | idavidov13/Orchestrating-AI-Native-Testing-with-Playwright |
| App under test | TTA Bank practice app + orders API, public and stable, locator-rich by design | TTA Bank · practice hub |
| BrowserBash | Free, 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 layer | browserbash.com |
| QASkills directory | 450+ installable QA skills for the major AI coding agents, one command per skill | qaskills.sh |
| Skills craft deep-dive | Our full masterclass on building agent skills | Skills Masterclass |
| Related build | A code-review skill wired to Playwright MCP, the same explore-verify philosophy | QA 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".