The Testing Academy · AI for QA

Prompt vs Skill vs AI Agent:
What Is Actually the Difference?

Everyone types prompts. Few testers know when a prompt should graduate into a skill file, and when a skill should graduate into a full AI agent. One table, two hand-drawn diagrams, and the same QA job, a test plan from a Jira ticket, done all four ways.

Normal prompt Vibe-coded prompt Skill file (SKILL.md) AI agent (Langflow)

The fastest way to see the difference: hold the job still and change the packaging. The job here is always the same, "turn Jira story QAJB-42 into a QA test plan". What changes is where the instructions live, who triggers them, and how much of the work the system does without you.

The ladder

1. Prompt typed in chat, every time lives in your head vibe coding = this, on repeat write it down once 2. Skill file SKILL.md + scripts, in git the agent loads it on its own give it tools + a loop 3. AI agent fetches Jira itself runs the whole job hands you the files you repeat the effort the system carries the effort
Same intelligence underneath. What changes is where the instructions live and who does the running.

The whole difference in one table

 Normal promptVibe-coded promptSkill fileAI agent
What it isOne instruction typed into chatA chat conversation you steer by feel, "make it pop"A folder with SKILL.md instructions + optional scriptsA system: model + tools + a loop that finishes the job
Where it livesYour head and the chat historyThe chat scroll, gone tomorrowVersioned in git, ~/.claude/skills/A deployed flow (Langflow, CrewAI, Claude Code)
Who triggers itYou, manually, every timeYou, again and againThe agent, when your request matches its descriptionA trigger: chat message, webhook, schedule
Repeatable?Only if you retype it exactlyNo. Nobody can reproduce it, including youYes. Same rubric, same steps, every runYes. Runs on demand, same flow every time
Can run code / tools?NoNoYes, bundled scripts (fetch Jira, export PDF)Yes, that is the point: fetch, decide, act, export
Team sharingCopy-paste into SlackA screenshot, at bestgit clone, or a .zip anyone drops inDeploy once, whole team uses it
Quality controlVaries run to runPure vibes, review skippedRubric + review gate pinned in the filePinned by the flow, plus the skills it loads
Best forA quick one-offThrowaway explorationA repeatable expert workflowAn end-to-end job with fetching + files
The example"Write test cases for login""make the plan nicer... ok ship it"test-plan-from-jiraLangflow: fetch QAJB-42, plan, export PDF

Level by level, with the real thing

works once 1

The normal prompt

You type an instruction, the model answers, done. Perfect for a one-off. The problem shows up on Tuesday, when a teammate needs the same output and types a worse version of your prompt from memory.

chat
Write a QA test plan for this story: "As a shopper I can apply a coupon
code at checkout". Include positive, negative and edge cases in a table
with steps and expected results.

Good output, zero leverage: the instruction dies with the chat tab. Nothing is versioned, nothing is triggered automatically, no script ran.

fun, unrepeatable 2

The vibe-coded prompt

"Vibe coding" is prompting by feel: you glance at the output, nudge it, glance again, and accept whatever looks right without reading the details. It is genuinely great for exploring what is possible, and genuinely dangerous the moment the output matters.

chat, 11:47 pm
you: make me a test plan for the coupon story, make it look nice
ai:  (writes a plan)
you: more edge cases. and severity colors. make the table bigger
ai:  (rewrites it)
you: the boundary ones feel wrong, redo just those, keep the vibe
ai:  (rewrites again)
you: looks good, ship it
The catch. Nobody, including you, can rerun this. There is no rubric, so "looks good" is the only quality gate, and it was applied at 11:47 pm. Vibe your way to a draft, then promote what worked into a skill so the judgment survives the chat scroll.
written once, reused forever 3

The skill file

A skill (per the agentskills.io format) is a folder with a SKILL.md: a short front-matter (name + description) and a body of instructions, plus any scripts it needs. The description is the trigger: when your request matches it, the agent loads the file and follows it, running the bundled scripts as needed. Here is the real one from our library, condensed:

test-plan-from-jira/SKILL.md (condensed)
---
name: test-plan-from-jira
description: Fetch a Jira story by key and generate a complete QA test plan
  (scope, scenarios, positive / negative / edge cases), then export it to a
  polished PDF and a filable XLSX. Use whenever the user says "create a test
  plan from <JIRA-KEY>" or asks to export a test plan to PDF / Excel.
---

# Test Plan from Jira
1. Get the Jira key from the user (e.g. QAJB-42).
2. Fetch the story: python scripts/fetch_jira.py QAJB-42 > story.json
3. Design the plan: every acceptance criterion maps to at least one
   positive AND one negative case. Keep steps atomic and verifiable.
4. Export: python scripts/export.py plan.json -> PDF + XLSX.
5. Summarize scope + case counts back to the user.

Now the Tuesday problem is solved: the teammate types "create a test plan from QAJB-43", the agent matches the description, and the exact same workflow runs, rubric included. The prompt became infrastructure.

the whole job, end to end 4

The AI agent

An agent is not a bigger prompt. It is a system: a model wired to tools inside a loop, so it can fetch its own inputs, decide, act, and hand you finished files. In Langflow you drag this together as nodes; the same shape works in CrewAI or Claude Code. For our job the flow is four nodes:

Chat trigger"QAJB-42" Agent node system prompt = the SKILL.md above decides which tool to call Jira fetch toolREST: summary + AC story JSON back Export toolplan.json to files .pdf .xlsx Four nodes in Langflow: trigger, agent (with the skill as its brain), two tools. Drag, wire, run.
The agent's brain is the skill. The skill's trigger is your sentence. Turtles, but useful ones.

Notice what happened: the skill did not get replaced, it got embedded. The agent supplies the loop and the tools; the SKILL.md supplies the judgment (every AC gets a negative case, steps stay atomic). This is why the ladder matters: good agents are built out of good skills, which are promoted from prompts that worked.

The same job, four ways

 What you doWhat you getNext week
PromptType the whole instruction, paste the story inOne decent plan in the chatRetype it, slightly worse
Vibe-codedNudge the output five times by feelA plan that "looks good"Cannot be reproduced, even by you
SkillSay "create a test plan from QAJB-42"The same rubric-driven plan, every time, + PDF/XLSX via scriptsTeammate runs it unchanged from git
AgentSend the key to the flow (chat, webhook, or schedule)Fetched story, generated plan, exported files, no human steps betweenRuns itself; you review the output

Which one should you use?

The promotion rule. The third time you type roughly the same prompt, stop and write the SKILL.md. The third time a skill needs you to hand-feed it inputs, wire it into an agent. Each promotion costs minutes and pays forever.

n8n (pronounced "n-eight-n") is the drag-and-drop workflow tool the agent examples on the first tab talk about. This tab gets it running on Windows and Mac, via Node or Docker, then builds the simplest useful agent: a test plan generator from a Jira ticket.

1. Install n8n

Two good paths. Node is quickest to try; Docker is cleaner to keep. Either way you end at http://localhost:5678.

Path A Via Node (Windows + Mac)

Windows Install Node.js 20 LTS from nodejs.org (or winget install OpenJS.NodeJS.LTS), then in PowerShell:

PowerShell
node -v          # confirm v20+
npm install n8n -g
n8n              # starts the editor at http://localhost:5678

Mac With Homebrew:

Terminal
brew install node@20
npm install n8n -g
n8n              # same: http://localhost:5678

No global install wanted? npx n8n runs it one-off. Update later with npm update -g n8n.

Path B Via Docker (Windows + Mac)

Install Docker Desktop first (Windows needs WSL2 enabled; the installer handles it). Then the same two commands work on both systems:

Terminal / PowerShell
docker volume create n8n_data
docker run -it --rm --name n8n -p 5678:5678 \
  -v n8n_data:/home/node/.n8n docker.n8n.io/n8nio/n8n

The volume keeps your workflows across restarts. To run it in the background swap -it --rm for -d --restart unless-stopped. Update with docker pull docker.n8n.io/n8nio/n8n.

Which one? Trying it out tonight: Node. Keeping it running for the team: Docker. Same app either way, and both are free for self-hosting.

2. Build the test plan generator

Four nodes. This is the exact flow shape from the first tab's "AI agent" section, made real:

Chat Trigger"QAJB-42" Jira nodeGet an issuesummary + AC out AI Agent node model: OpenAI / Groq system prompt = the test-plan rules Replythe test plan Four nodes. Drag them onto the n8n canvas in this order and wire left to right.
The whole generator in n8n: trigger, fetch, think, reply.
  1. Add a Chat Trigger. New workflow, add node, search "Chat Trigger". This gives you a chat box where you will type the Jira key.
  2. Add the Jira node. Search "Jira Software", operation Get an issue. Create the credential: your Jira URL, email, and an API token from id.atlassian.com → API tokens. Set Issue Key to the chat input: {{ $json.chatInput }}.
  3. Add the AI Agent node. Attach a chat model (OpenAI, Groq, or OpenRouter credential), and paste the system prompt below. Feed the Jira node's output in as the user message.
  4. Wire and test. Chat Trigger → Jira → AI Agent. Open the chat, type QAJB-42, get a full test plan back. Activate the workflow to keep it always on.
AI Agent node - system prompt (paste as-is)
You are a senior QA engineer. You will receive a Jira story (summary,
description, acceptance criteria). Produce a complete test plan:
- Scope and assumptions (what is and is not tested)
- Test scenarios grouped by feature area
- For each case: id, title, type (positive/negative/edge/boundary),
  priority P1-P3, preconditions, numbered steps, expected result
- Every acceptance criterion gets at least one positive AND one
  negative case. Steps atomic, expected results verifiable.
- If the story is thin, list your assumptions at the top.
Return the plan as clean markdown with one table per feature area.
Secrets rule. The Jira token and the model API key live in n8n credentials, never in the prompt or the workflow JSON you export and share.

From here the upgrades are one node each: send the plan to Confluence or email, trigger from a Jira webhook when a story hits READY instead of chat, or export XLSX with the spreadsheet node. The skill version of this generator is the portable twin of this flow.

LangFlow is the visual builder for LangChain-style agents: components on a canvas, wires between them, a Playground to chat with the result. This tab installs it on Windows and Mac, via pip/uv or Docker, then rebuilds the same Jira test plan generator so you can compare it with the n8n version side by side.

1. Install LangFlow

Path A Normal install with Python (Windows + Mac)

You need Python 3.10 or newer. The uv installer is the fast, clean way:

Terminal / PowerShell
python --version              # 3.10+ (Windows: from python.org, Mac: brew install python)
pip install uv                # fast installer, once
uv pip install langflow       # or plain: pip install langflow
uv run langflow run           # or plain: langflow run

It prints a local URL, open http://127.0.0.1:7860. On Mac there is also a one-click LangFlow Desktop app if you want zero terminal.

Path B Via Docker (Windows + Mac)

With Docker Desktop installed (same note as the n8n tab: Windows wants WSL2), one command:

Terminal / PowerShell
docker run -it --rm -p 7860:7860 langflowai/langflow:latest

Open http://localhost:7860. To keep flows across restarts, mount a volume:

persistent variant
docker volume create langflow_data
docker run -d --restart unless-stopped -p 7860:7860 \
  -v langflow_data:/app/langflow langflowai/langflow:latest
pip or Docker? If Python 3.10+ already lives on your machine, pip/uv is lighter. If your Python situation is a crime scene (every tester's laptop eventually), Docker sidesteps it entirely.

2. Build the test plan generator

Five components, wired left to right. Start from a blank flow (or the "Basic Prompting" template and add the API Request):

Chat Input"QAJB-42" API RequestGET /rest/api/3/issue/KEY Prompt the test-plan template with {ticket} filled in ModelOpenAI / Groq /any provider ChatOutput Five components on the LangFlow canvas. Test it in the Playground, then hit the API endpoint it exposes.
The same generator in LangFlow: input + fetch feed the prompt, the model answers, chat shows the plan.
  1. Chat Input. Drag it in; this is where you will type the Jira key.
  2. API Request. Method GET, URL https://YOURSITE.atlassian.net/rest/api/3/issue/<KEY>. Add a header Authorization: Basic <base64 of email:api_token> (make the token at id.atlassian.com). Wire Chat Input into the URL's key so the typed key is fetched.
  3. Prompt. Paste the template below. It has two variables: {ticket} (wire the API Request output here) and the instructions baked in.
  4. Model. Drop in an OpenAI (or Groq / any provider) component, paste your API key into its field, wire Prompt → Model.
  5. Chat Output. Wire Model → Chat Output. Open the Playground, type QAJB-42, read your test plan. Every LangFlow flow also exposes an API endpoint, so this generator is callable from CI later.
Prompt component - template (paste as-is)
You are a senior QA engineer. Below is a Jira story fetched as JSON.

{ticket}

Produce a complete test plan from it:
- Scope and assumptions (what is and is not tested)
- Test scenarios grouped by feature area
- For each case: id, title, type (positive/negative/edge/boundary),
  priority P1-P3, preconditions, numbered steps, expected result
- Every acceptance criterion gets at least one positive AND one
  negative case. Steps atomic, expected results verifiable.
Return clean markdown with one table per feature area.
Secrets rule. The Jira token and model key go into the component credential fields, never hard-coded into a flow you export or screenshot.

Same job, three shapes now: the skill (portable, lives in git), the n8n flow (always-on automation), and this LangFlow flow (visual, API-exposed). That is the ladder from the first tab, walked end to end.

The first tab warned that vibe coding is unrepeatable. Here is the honest fix: keep the vibe loop, but start it from a written spec prompt. Below is a complete one. Paste it into DeepSeek, Gemini Flash, Groq's gpt-oss-120b, or Claude, and you get a working local Jira Chat app: a chatbot that creates real Jira tickets from plain English, with Groq as the brain.

1. What you are about to build

Browser chat UI "create a jira ticket for login broken, high" + Settings drawer POST /api/chat server.js (Express) localhost:3000 keys held in memory only never sent to the browser Node 20 fetch, no axios tool call? Groq llama-3.3-70b / gpt-oss-120b create_jira_ticket(args) POST /rest/api/3/issue Jira Cloud Basic auth email:token ADF description "Created QAJB-123" + clickable /browse link back to the chat
One file. The browser only ever talks to your local server; the server talks to Groq and Jira.

The shape matters more than the code: the browser never touches Jira or Groq directly. A one-file Express server serves the chat UI, holds your keys in memory, and does both API calls. That single decision kills the two classic failures of vibe-coded API apps: CORS errors and API keys leaking into client-side JavaScript.

2. The vibe loop, done right

  1. Copy the prompt below into any strong code model (DeepSeek, Gemini Flash, openai/gpt-oss-120b on Groq, Claude). It outputs one server.js.
  2. Run it: npm init -y && npm i express && node server.js, open http://localhost:3000.
  3. Fill Settings: Jira site URL, email, an API token from id.atlassian.com, your project key, and a free key from console.groq.com.
  4. Run the acceptance test baked into the prompt: one message that must create a real Bug, one that must not create anything. If either fails, paste the error back into the same chat: that is the vibe part.
  5. Now vibe freely. "Make the bubbles green." "Add a dropdown for issue type." "Show my last 10 tickets." Each nudge is safe because the acceptance test tells you when you broke the core.
Why this prompt one-shots on most models. It pins exactly the things weak models get wrong: the CORS-proxy architecture, the ADF description format Jira v3 requires, the tool schema, the 400-retry without priority, and a concrete acceptance test. Vague vibes in, broken app out; pinned constraints in, working app out. That is the whole lesson of this page in one artifact.

3. The prompt (copy everything)

paste into DeepSeek / Gemini Flash / gpt-oss-120b / Claude
Build a lightweight local "Jira Chat" app in ONE file: server.js (Node 20+,
only dependency: express). No database, no build step, no framework.

WHAT IT IS
A chatbot web app. I type things like "create a jira ticket for the login
button being broken on mobile, high priority bug" and it creates a REAL
ticket in my Jira Cloud site, then replies in the chat with the ticket key
and a clickable link. Anything that is not a ticket request gets a normal
short chat reply.

ARCHITECTURE (do not deviate)
- server.js starts an Express server on http://localhost:3000
- GET /  serves the full chat UI as one inline HTML string (no separate
  files): a header, a scrollable message list, a text input + Send button,
  and a Settings drawer with five fields:
    1. Jira site URL   (https://YOURSITE.atlassian.net)
    2. Jira email
    3. Jira API token
    4. Jira project key (e.g. QAJB)
    5. Groq API key
  Clean modern styling, dark background, rounded message bubbles, my
  messages right-aligned, bot messages left-aligned, typing indicator.
- The browser NEVER calls Jira or Groq directly (CORS + secret leakage).
  It only calls these local endpoints:
    POST /api/settings  -> stores the five values in a server-side
                           variable (memory only; never written to disk,
                           never echoed back, never logged)
    POST /api/chat      -> { message } -> does the AI + Jira work below
- Node 20 global fetch for all outbound calls. No axios.

THE AI STEP (Groq)
- Call https://api.groq.com/openai/v1/chat/completions with the stored
  Groq key. Default model "llama-3.3-70b-versatile"; put the model name in
  one constant so I can switch it to "openai/gpt-oss-120b".
- Use OpenAI-style tool calling. Declare ONE tool:
    create_jira_ticket(summary: string, description: string,
                       issue_type: "Bug"|"Task"|"Story",
                       priority: "Highest"|"High"|"Medium"|"Low")
- System prompt: "You are a QA assistant inside a local Jira chat app.
  When the user asks to create/raise/file/log a ticket, bug, task, or
  story, call create_jira_ticket with a crisp summary (max 90 chars), a
  useful 2-4 sentence description expanding what they said, the right
  issue_type, and priority (default Medium unless stated). For anything
  else, answer briefly in plain text. Never invent details the user did
  not imply."
- If the response contains a tool call: parse its JSON arguments, create
  the ticket (below), then reply in chat with:
  "Created <KEY>: <summary> - <siteUrl>/browse/<KEY>" (render as a link).
- If no tool call: just show the model's text reply.

THE JIRA STEP
- POST {siteUrl}/rest/api/3/issue
- Header Authorization: "Basic " + base64(email + ":" + apiToken),
  Content-Type application/json.
- Body: fields = { project: {key}, summary, issuetype: {name: issue_type},
  priority: {name: priority}, description in ADF format EXACTLY:
  { "type":"doc","version":1,"content":[{"type":"paragraph",
    "content":[{"type":"text","text": description }]}] }
  (a plain string description fails on API v3).
- On success return the "key" from the response.

ERROR HANDLING (show all of these as red bot bubbles in chat, never crash)
- Settings not saved yet -> "Open Settings and save your Jira + Groq keys."
- Jira 401/403 -> "Jira rejected the credentials: check email + API token."
- Jira 400 -> show Jira's error messages field (usually a bad project key
  or a priority not available on the project; if priority causes the 400,
  retry once WITHOUT the priority field).
- Groq non-200 -> "AI error" + status + body snippet.

SECURITY RULES
- Secrets live only in the server-side variable for this run.
- Never put keys in client-side JS, localStorage, URLs, or console.log.
- Add a one-line comment where a .env upgrade would go.

ACCEPTANCE TEST (make sure this exact flow works)
1. npm init -y && npm i express && node server.js
2. Open http://localhost:3000, save settings.
3. Type: "create a jira ticket for checkout coupon getting lost on double
   click, high priority bug" -> a real Bug appears in the project, chat
   shows "Created QAJB-123: ..." with a working link.
4. Type: "what can you do" -> plain chat reply, no ticket created.

OUTPUT: the complete server.js in one code block, then the three run
commands. Nothing else.
Secrets rule. The Jira token and Groq key go into the app's Settings drawer at runtime, held in server memory only. Never commit them, never paste them into the AI chat, never put them in client-side code. The prompt enforces this in the generated app too.

Where this sits on the ladder: the prompt is level 2 done professionally. Promote it to a skill when your team reuses it, or wire the same Groq + Jira calls into n8n / LangFlow when you want it running without a laptop.

A skill is one file. SKILL.md is a block of YAML frontmatter the agent reads to decide when to load the skill, followed by a Markdown body it follows once loaded. That is the whole format. Below is every part, what each one is for, and a complete skill you can copy.

Two halves: discovery and execution

The split is the point. The frontmatter is tiny and always sits in the agent's index, so it can match your request to the right skill without loading anything heavy. The body is the real instruction set, pulled into context only when the skill actually fires. Write the frontmatter for a matcher; write the body for a worker.

The description is the whole ballgame. It is the one line the agent reads to decide whether to load your skill at all. Write it in the third person, say what the skill does and the exact phrases a user would type ("analyze this ticket", "is this story ready to test"). A vague description means your skill never triggers; an over-broad one means it triggers on everything.

The frontmatter: how the agent finds it

Fenced at the very top of the file between --- lines, written in YAML.

FieldRequiredWhat it does
nameYesKebab-case identifier, usually matching the skill's folder. How the skill is referenced and invoked.
descriptionYesThe trigger. A third-person summary of what the skill does and when to use it, including the phrases that should fire it. This is what the agent matches on.
licenseOptionalAn SPDX id such as MIT, so others know how they may reuse it.
metadataOptionalA free-form map for cataloguing: author, version, and domain tags (here, stlc-phase). Used for filtering and provenance, never for triggering.

The body: what the agent does

Plain Markdown under the frontmatter. Order it so the agent reads its remit first, then how to act, then what to hand back, then what it must never do.

SectionWhat goes in it
Title + informationA # Heading and one paragraph stating the skill's job and its boundary: what it produces, and what it deliberately does not do.
When to useThe concrete situations that should trigger it, in list form. Mirrors the description so a human and the matcher agree.
WorkflowThe numbered steps the agent follows. Anything consequential ends in a mandatory human review gate.
Output shapeA template of the exact result format, so every run looks the same and is easy to diff and trust.
Guardrails + safetyThe hard rules: never fabricate, stay in scope, keep findings traceable, require human sign-off on anything that matters.
When NOT to useThe situations where the skill should stay silent. This is what stops it over-triggering onto jobs it was not built for.
Don'tsExplicit prohibitions, stated flatly, so the agent has no room to improvise past its remit.
Guardrails are not decoration. The line that separates a useful skill from a dangerous one is usually a guardrail: "a missing item is a finding, not a blank to fill." Without it, a requirement-analysis skill will happily invent the very acceptance criteria it was supposed to flag as missing.

A complete skill, annotated

Here is a real, runnable skill, jira-requirement-analyzer, with every part above in place: frontmatter for discovery, then a body of remit, when-to-use, workflow, output shape, and guardrails.

---
name: jira-requirement-analyzer
description: >-
  Read a JIRA ticket and judge whether it is actually ready to test. Use when a
  tester says "analyze this ticket", "is this story ready to test", "find gaps in
  JIRA-123", or pastes a user story / acceptance criteria and wants it pressure-tested.
  Fetches the ticket, scores it against a readiness checklist, and returns a
  gaps / ambiguities / risks report plus clarifying questions to send back to the author.
license: MIT
metadata:
  author: TheTestingAcademy
  stlc-phase: Requirement Analysis
  version: 1.0.0
---

# JIRA Requirement Analyzer

You decide whether a story is **testable yet**, and if not, you say exactly what is
missing. Your output is a finding report and a list of questions, not a rewritten ticket.

## When to use
- Someone hands you a JIRA key or story text and asks "is this ready to test?"
- A grooming / refinement session needs the ambiguities surfaced before estimation.
- Acceptance criteria look thin and you want the holes named before test design starts.

## Workflow
1. **Fetch the ticket.** If a JIRA key is given, pull it via an available JIRA MCP tool
   or REST call. If neither is reachable, ask the user to paste the ticket body,
   never invent ticket content. Capture summary, description, ACs, components,
   linked issues, attachments, and fix version.
2. **Score readiness.** Walk each requirement and mark ✅ clear / ⚠️ ambiguous / ❌ missing.
   Check: testable acceptance criteria, defined error/empty/boundary states, roles &
   permissions, non-functional needs (perf, security, a11y, i18n), data & dependencies.
3. **Classify findings** into three buckets: Gaps (missing info), Ambiguities (two valid
   readings), Risks (what could ship broken). Rate each by impact.
4. **Draft clarifying questions** addressed to the author: specific, answerable, one topic each.
5. **HUMAN REVIEW GATE (mandatory).** Present the report as a draft. State what you
   assumed and could not confirm. Ask the tester to confirm or edit before these
   questions go to the author; do not proceed to scenario design until confirmed.

## Output shape

```
## Requirement Analysis - <JIRA-KEY>: <title>
Readiness verdict: READY / NOT READY (n blockers)
Gaps            [ ❌ ... ]
Ambiguities     [ ⚠️ ... two readings each ]
Risks           [ impact-rated ]
Questions for the author (numbered, one topic each)
--- HUMAN REVIEW GATE ---
Assumptions / What I could not confirm / "Confirm before I send these to the author"
```

## Guardrails
- Never fabricate an acceptance criterion, field, or requirement: a missing item is a
  finding, not a blank to fill.
- Do not rewrite the ticket for the author; surface the gap and ask the question.
- A "READY" verdict is advisory; the author and QA lead own that call.
- Keep every finding traceable to a specific line or absence in the ticket.
Read it top to bottom. The frontmatter tells the agent when to reach for this skill; the body tells it exactly how to behave once it does, right down to refusing to invent a ticket and stopping at a human review gate. That is all a skill is: a matcher plus a disciplined worker, in one file.

Want to build one end to end? The test-plan skill guide walks you through authoring one live, and the 36-skill STLC suite is a shelf of these you can lift. Where this sits on the ladder: a skill is the reusable middle rung between a one-off prompt and a full AI agent.