Test Case Analysis
Execute a TMS test case against the live app, observe what actually
happens, and emit an Automation-Friendly Spec (AFS) a downstream
engineer can implement without re-exploring.
Core philosophy: a written test case is a hypothesis. The app is
the only source of truth. This skill never trusts the case as
authored — it runs it step by step, captures stable selectors,
flags defects, and only then produces a spec.
Absolute boundaries
- No automation code. No
.spec.ts, no test_*.py, no step
definitions. The output is a markdown AFS file. Automation is
implemented downstream — your agent knows which role / workflow
picks the AFS up.
- No automating un-automatable cases. Physical device, visual
judgment that can't be asserted, flows that genuinely can't be
scripted — mark the AFS
un-automatable and stop.
- No skipping exploration. Even if the TMS case looks complete,
execute it. The case describes intent; only execution reveals truth.
Analyst slot contract
This skill IS the analyst slot in the test-automation pipeline. When
dispatched — by an orchestrator like test-automation-lead, or
standalone for "analyse SCRUM-T101" — role, context, parameters, and
return shape are fixed here so dispatch prompts don't have to inline
them.
Role. Execute one TMS test case end-to-end against the live app,
capture stable selectors, classify the finding, emit an AFS. No
automation code (see § Absolute boundaries).
Session context — read once at session start. Typically
auto-imported via @-blocks in your agent's AGENT.md; if your
agent doesn't auto-import, read them now:
.agents/profile.md — project systems, base URL, credentials
matrix, sample users, bug filing target
.agents/workflow.md — branch/PR rules, EPIC pattern
.agents/testing.md — framework, locator strategy, TMS case-gate
exclusion list
.agents/memory/<your-agent>/project_briefing.md — accumulated
project gotchas from prior sessions
.agents/architecture.md — the surfaces you'll touch (also
referenced in Phase 2)
Missing context → flag the gap; don't fabricate defaults.
Per-case parameters (caller provides at dispatch time):
- TMS case ID (e.g.
SCRUM-T101)
- User set — a key into
.agents/profile.md § Roles & sample users
(e.g. ${TEST_USER} / ${TRIAL_USER})
- Base URL — usually from
.agents/profile.md, but caller may
override
- EPIC parent key — for defect filing under
story-subtask style
Return contract:
- Status — one of
ready-for-automation / already-covered /
extend-existing / blocked / defect-found /
out-of-scope-by-author / un-automatable. Full semantics in
Phase 0 (out-of-scope) and § 5 Classify findings (the rest).
- AFS path —
test-specs/<feature>/l<pri>_<slug>_<tms-id>.md
for fresh-implementation, lcovered_* for already-covered,
lextend_* for extend-existing. Omitted for un-automatable and
out-of-scope-by-author (no AFS emitted).
- Filed bug IDs — if
defect-found, the tracker IDs created
per § 5's bug-filing routing.
Phase 0 — Case-gate (preflight, runs BEFORE Phase 1)
Before fetching the case body, probe its TMS author metadata. Skip cases the author has marked as not actionable — there's no analyst value in executing them, and downstream the implementer / orchestrator will reject them.
What to probe (project-defined in .agents/testing.md § TMS case-gate; if absent, default to fetching all and flag the gap):
| Metadata field |
Typical exclusions |
Why |
| Status |
Out of Scope, Untested, Draft, Deprecated |
Author has signalled the case isn't currently a target — don't burn cycles |
| Folder / parent membership |
Mismatch vs requested folder |
Catches raw-key-ASC iteration drift across folders (e.g. KEY-NNN is in folder A, KEY-NNN+1 jumped to folder B) — drift recurs when iterating by key |
| Version / last-modified |
Stale per the project's freshness threshold |
Stale cases often contradict the live product (case-text drift) — see test-automation-workflow § Reverse-masking guard |
How to probe. Probe the single-case status field directly via your adapter (get_field_value / fetch_case(id, fields=[status]) / equivalent). Don't query-set — JQL-style status in (...) queries on TMS custom fields are unreliable across adapters; verify the field on each case directly.
Outcomes:
- All probes clear → continue to Phase 1.
- Status excluded → don't fetch the body; return
out-of-scope-by-author with the field value as evidence; close the case in the tracker (or mark per project convention).
- Folder/membership mismatch → don't dispatch; return to the orchestrator with the discrepancy. Iteration drift is an orchestrator-side routing issue, not an analyst-side execution issue.
- TMS unreachable for the probe → fall back to fetching the body (Phase 1 will surface it); flag the gap for scout to fill in
.agents/testing.md.
The six-phase loop (one case at a time, runs AFTER Phase 0)
1. Fetch the case → TMS adapter (pluggable; see test-automation.yaml)
2. Read app context → .agents/architecture.md + previous AFS files
3. Execute → browser-driving capability (your agent's wired MCP), step-by-step
4. Capture selectors → stable, accessible, fallback-ready
5. Classify findings → ready / already-covered / extend-existing / blocked / defect-found / un-automatable
6. Emit AFS → test-specs/<feature>/l<pri>_<slug>_<tms-id>.md
1. Fetch the case
Use the adapter declared in .agents/test-automation.yaml. If
transport: mcp and the MCP server is online, prefer MCP tool calls
(mcp__<server>__<tool> / <server>/<tool> depending on host) —
no secrets travel through the agent's context. Otherwise use HTTP
with the configured auth_env. If no adapter is configured, read
the markdown case from test-specs/. If the TMS is unreachable,
open the case in the browser and copy it by hand — do not block on a
flaky TMS.
Extract: name, priority, preconditions, steps, expected, cleanup,
linked story, attachments.
2. Read app context
.agents/architecture.md — know the surfaces you'll touch
- Previous AFS files in
test-specs/<feature>/ — match their shape
- Existing page objects — selector notes should align with what exists
3. Execute
Three browser tools sit at different layers; pick by what's wired and
what challenge you're solving. Full triage:
../test-automation-workflow/references/browser-tools.md.
In short:
- Default —
playwright-testing
(Playwright MCP). Prefer its accessibility-snapshot tool for accessible-name
discovery — it yields both the ref you need to click and the
role-name pair you'll assert on.
- MCP server not wired —
playwright-cli
drives the same browser surface from the shell (codegen,
--trace, multi-tab, storage, request mocking).
- Visual / CDP / a11y —
browser-verify
for computed styles, real CDP input events, storage/cookies, or axe
audits.
Soft guidance, not a hard rule: switching tools mid-case is fine when
the first one isn't producing useful evidence — note which tool
produced which observation in the AFS so the next reader can follow.
For each step:
- Perform the real action. Never synthesize a click via
page.evaluate — the app may react differently.
- Screenshot. Always.
- Check console messages. Even when the UI looks fine. Silent
JS errors are the worst bugs.
- Check network. Note which requests fire and which payloads matter.
- Observe actual vs expected. Record both if they differ.
4. Capture selectors
Priority order — document in the AFS for every interactive element:
data-testid / data-test — stable, intentional
- ARIA role + accessible name —
getByRole('button', { name: 'Apply' })
- Accessible label —
getByLabel('Email')
- Text content —
getByText('Sign in') (fragile to i18n)
- CSS selector — last resort; prefer one anchored to a stable attribute
Always give a fallback. Apps change. A single selector per
element is a single point of failure.
5. Classify findings
Status per case (goes in the AFS metadata block):
- ready-for-automation — case executed end-to-end, selectors
captured, no blockers
- already-covered — Rule-6 behavioural-equivalence dedup against
an existing merged spec. The observable this case asserts is
already proven by another spec on file. No own implementation
needed. Emit a traceability AFS at
test-specs/<feature>/lcovered_<slug>_<tms-id>.md containing the
dedup proof: covering spec at file:line + a one-paragraph
behavioural-equivalence argument (why the existing assertion
satisfies this case's expected observable). Link the original
TMS case to the covering one in the tracker so the audit trail
resolves both ways. The lcovered_ filename prefix is the
contract — downstream audits grep for it to enumerate
Rule-6-dedup coverage distinct from fresh-implementation coverage.
- extend-existing — Rule-6 partial-overlap. An existing merged
spec covers most of this case's observable, but a small number of
assertions are missing. Don't write a fresh
.spec.ts; the
implementer extends the covering spec with the gap assertions.
Emit an extension AFS at
test-specs/<feature>/lextend_<slug>_<tms-id>.md containing: the
covering spec at file:line, a one-paragraph behavioural-overlap
argument (what's already proven), and a Gap assertions section
listing exactly what the existing spec doesn't cover (the new
selectors / observations / expecteds the implementer needs to
append). Link the TMS case to the covering one in the tracker.
The lextend_ filename prefix is the contract — downstream audits
distinguish extension work from fresh-implementation and from full
lcovered_ dedup. Boundary call: if the gap is large enough that
the extension would be a near-rewrite of the covering spec, treat
as ready-for-automation instead and let the implementer decide
whether to extend or split.
- blocked — analyst hit a wall (access, data, env); the AFS's
"Blocked Steps" section lists what's needed to unblock
- defect-found — real product bug prevents completion. File the
ticket via your agent's bug-filing capability (see When you find a
defect below for the routing rules) before emitting the AFS;
reference the bug ID in the AFS
- un-automatable — keep as manual; do not emit an AFS; update
the TMS note
Reverse-masking guard — case-text drift is a CLARIFICATION, not
a defect. When the live product correctly diverges from the case
text (case says ≥44px, product = 40px and that's the design;
case says "Save button visible", product correctly removed Save),
the case text is what's stale, not the product. Don't classify
as defect-found; classify as ready-for-automation and assert
the live contract. File the case-text drift as a CLARIFICATION
per the project's Bug filing style, not a Bug. Full treatment
in test-automation-workflow
§ Reverse-masking guard.
When you find a defect during execution:
Do not force-continue past it hoping it "probably works later".
Always file a tracking ticket. Nothing slips through: every
finding (clarification, question, blocker, full defect) gets
tracked somewhere the team sees. How depends on profile.md.
Determine where the ticket lands by reading
.agents/profile.md § Project systems § Bug filing. Two orthogonal
fields drive the routing — scout's Step 0.7 fills both:
Issue tracker — the system the ticket lands in
(github-issues / gitlab-issues / jira / azure-devops /
linear / …). Your agent has a bug-filing capability wired in; use
it. Filing the ticket itself is not this skill's job — this skill
hands you the what (severity, repro, evidence) and the where
(tracker + style + target); your agent's bug-filing skill does
the how.
Bug filing style — the shape of the ticket. Three styles:
github-issue (default) — open a standalone issue in the
tracker named above. Same shape regardless of tracker system (a
standalone issue in GitHub / GitLab / Jira / …).
story-subtask — create a sub-task under the originating
story (Jira / Azure DevOps only; the story the TMS case is
linked to). Fetch the story ID via the TMS adapter's
get_test_case_links, then pass it as the parent when handing
off to the bug-filing skill.
separate-ticket — file in a dedicated QA/bugs project,
not the main development tracker. Target is named in
profile.md § Bug filing target. Same tracker system, different
project key.
Determine whether to bundle or split by reading
§ Bundling policy and classifying the finding's severity:
Classify the finding first:
- Lightweight clarification / question — expected behavior
unclear, minor UI copy ambiguity, missing doc, "should this
modal close on outside-click?"-type questions
- Real defect — reproducible bug, functional breakage,
incorrect data, blocker — anything where the product is
provably wrong
strict-per-bug (default) — every finding (either class)
gets its own ticket. Done.
bundle-per-case (opt-in, requires umbrella-ticket
convention already in place on the project):
- If the finding is a real defect → its own ticket (same as
strict-per-bug). Real defects never bundle.
- If the finding is a lightweight clarification → check if
there's already an open "umbrella" ticket for this TMS case.
- If yes: add the finding as a comment on the existing ticket.
- If no: file a new umbrella ticket (title e.g.
"Clarifications for SCRUM-T101") and make this the first
comment. Future lightweight findings on the same case
attach here.
The umbrella-lookup is the fragile step — getting it wrong
duplicates tickets. Defer to strict-per-bug unless the
operator's profile.md § Bug filing style explicitly selects
bundle-per-case and the project already has:
- A title convention for umbrella tickets (so the
find-or-create search has something stable to match on).
- A documented comment-anchor format that the analyst can
reference from the AFS (e.g. "comment-3" or a permalink fragment).
Without both,
strict-per-bug is the safe default; one more
ticket is cheaper than a missed clarification.
Hand the body, tracker, style, and (for story-subtask) parent
story ID to your agent's bug-filing skill. Do not run a dev-side
fix lifecycle (failing test → RCA → implement fix → verify) — those
steps belong to whoever picks the defect up later, not to you
during analysis. You file and walk away.
If .agents/profile.md § Bug filing is Unconfirmed, or your
agent has no wired tooling for the named tracker, stop and ask the
operator before filing — don't pick a default silently. Flag the
gap in the AFS so scout can fill the field on the next onboarding
pass.
Note the finding in the AFS under "Known Defects Found" with the
ticket ID, filing style, and a recommendation — soft-expect
(isolated) or natural-fail (blocking). Under bundle-per-case,
reference both the umbrella ticket ID and the comment anchor so
the downstream implementer can find the specific note (e.g.
"Known defect: JIRA SCRUM-BUG-42 comment-3 — soft-expect", or
"Known defect: GH#234 — natural-fail").
6. Emit AFS
A single markdown file per case, per the structure in
references/spec-format.md. Path:
test-specs/<feature>/l<priority>_<slug>_<tms-id>.md
The AFS is the contract. If it's ambiguous, the downstream engineer
will come back asking — which means the execution pass wasn't
complete. Make it stand alone.
Evidence paths (convention)
test-results/screenshots/<tms-id>-step-<n>-<action>.png
test-results/json/<tms-id>-<iso-timestamp>.json
Relative paths inside the AFS; the automation engineer re-uses the
same convention for CI artifacts.
Batching cases
When handed multiple cases:
- Single case → run directly. No delegation.
- Multiple cases → delegate one sub-agent per case via the host's
subagent dispatch —
Agent(...) (Claude Code), runSubagent(...)
(Copilot). Each sub-agent gets its own browser context.
- After sub-agents finish, retrieve each one's final message via the
host's result-retrieval tool (NOT a shell command), extract the
AFS path, verify the file exists on disk, and recreate it
yourself from the returned content if it didn't persist.
Handoff
When the AFS is ready:
- Commit the AFS on a feature branch —
test(spec): add AFS for <id>
- Push; open a small PR if the project reviews specs before
automation starts, otherwise hand the AFS path directly back to
the caller (your agent knows whether the automation role expects
the spec via PR or via direct handoff)
- If a defect was found, link the issue in the PR body
- If the case is
blocked or un-automatable, stop here and
report up — do not pass a broken spec downstream
Anti-patterns
- Writing automation code. Not this skill's scope. Stop.
- Copying the case text into the AFS verbatim without executing.
The AFS needs discovered selectors, observed network calls,
confirmed expected vs actual. A copy-paste AFS is lying.
- Skipping the console check because "the UI looks fine". Silent
errors are the ones that ship.
- Force-continuing past a defect to complete the AFS. A defect
invalidates downstream steps — you no longer know what "expected"
means.
- Inventing selectors. If you didn't click it, it doesn't go in
the selector table. Run the step.
test.fail()-style thinking. If a step fails for a real
product reason, that's a defect, not a caveat in the AFS.
- Skipping Phase 0 (case-gate) because the case "looked fine"
in a previous batch. Status / folder-membership / version drift
between batches — re-probe per case, every dispatch.
- Classifying case-text drift as
defect-found instead of
CLARIFICATION. If live product is correct and the case is
stale, the case is the bug, not the product. Asserting the
stale case-text is reverse-masking (see § Classify findings note).
- Re-implementing a case whose observable is already proven by
another merged spec. Rule-6 dedup →
already-covered with a
traceability AFS (lcovered_*.md), not a duplicate .spec.ts.
- Filing partial overlap as fresh
ready-for-automation. When
an existing merged spec covers most of the observable and only a
small number of assertions are missing, classify as
extend-existing with lextend_*.md + a Gap assertions section.
Forcing the implementer to rediscover the overlap defeats Rule-6
dedup and ends with two specs asserting the same behaviour.
References
- references/spec-format.md — the
Automation-Friendly Spec (AFS) structure, required sections,
examples. This is what the skill's output looks like.
1---2name: test-case-analysis3description: Use when a TMS test case needs manual execution, selector discovery, or defect investigation before automation — "analyse SCRUM-T101", "run this case and emit an AFS", any pre-automation case exploration. Produces an Automation-Friendly Spec (AFS); does not write test code.4license: Apache-2.05---67# Test Case Analysis89Execute a TMS test case against the live app, observe what actually10happens, and emit an **Automation-Friendly Spec (AFS)** a downstream11engineer can implement without re-exploring.1213**Core philosophy:** a written test case is a hypothesis. The app is14the only source of truth. This skill never trusts the case as15authored — it runs it step by step, captures stable selectors,16flags defects, and only then produces a spec.1718## Absolute boundaries1920- **No automation code.** No `.spec.ts`, no `test_*.py`, no step21 definitions. The output is a markdown AFS file. Automation is22 implemented downstream — your agent knows which role / workflow23 picks the AFS up.24- **No automating un-automatable cases.** Physical device, visual25 judgment that can't be asserted, flows that genuinely can't be26 scripted — mark the AFS `un-automatable` and stop.27- **No skipping exploration.** Even if the TMS case looks complete,28 execute it. The case describes intent; only execution reveals truth.2930## Analyst slot contract3132This skill IS the analyst slot in the test-automation pipeline. When33dispatched — by an orchestrator like `test-automation-lead`, or34standalone for "analyse SCRUM-T101" — role, context, parameters, and35return shape are fixed here so dispatch prompts don't have to inline36them.3738**Role.** Execute one TMS test case end-to-end against the live app,39capture stable selectors, classify the finding, emit an AFS. No40automation code (see § Absolute boundaries).4142**Session context — read once at session start.** Typically43auto-imported via `@-blocks` in your agent's `AGENT.md`; if your44agent doesn't auto-import, read them now:4546- `.agents/profile.md` — project systems, base URL, credentials47 matrix, sample users, bug filing target48- `.agents/workflow.md` — branch/PR rules, EPIC pattern49- `.agents/testing.md` — framework, locator strategy, TMS case-gate50 exclusion list51- `.agents/memory/<your-agent>/project_briefing.md` — accumulated52 project gotchas from prior sessions53- `.agents/architecture.md` — the surfaces you'll touch (also54 referenced in Phase 2)5556Missing context → flag the gap; don't fabricate defaults.5758**Per-case parameters** (caller provides at dispatch time):5960- TMS case ID (e.g. `SCRUM-T101`)61- User set — a key into `.agents/profile.md` § Roles & sample users62 (e.g. `${TEST_USER}` / `${TRIAL_USER}`)63- Base URL — usually from `.agents/profile.md`, but caller may64 override65- EPIC parent key — for defect filing under `story-subtask` style6667**Return contract:**6869- **Status** — one of `ready-for-automation` / `already-covered` /70 `extend-existing` / `blocked` / `defect-found` /71 `out-of-scope-by-author` / `un-automatable`. Full semantics in72 Phase 0 (out-of-scope) and § 5 Classify findings (the rest).73- **AFS path** — `test-specs/<feature>/l<pri>_<slug>_<tms-id>.md`74 for fresh-implementation, `lcovered_*` for already-covered,75 `lextend_*` for extend-existing. Omitted for `un-automatable` and76 `out-of-scope-by-author` (no AFS emitted).77- **Filed bug IDs** — if `defect-found`, the tracker IDs created78 per § 5's bug-filing routing.7980## Phase 0 — Case-gate (preflight, runs BEFORE Phase 1)8182Before fetching the case body, probe its TMS author metadata. Skip cases the author has marked as not actionable — there's no analyst value in executing them, and downstream the implementer / orchestrator will reject them.8384**What to probe** (project-defined in `.agents/testing.md` § TMS case-gate; if absent, default to fetching all and flag the gap):8586| Metadata field | Typical exclusions | Why |87|---|---|---|88| **Status** | `Out of Scope`, `Untested`, `Draft`, `Deprecated` | Author has signalled the case isn't currently a target — don't burn cycles |89| **Folder / parent membership** | Mismatch vs requested folder | Catches raw-key-ASC iteration drift across folders (e.g. `KEY-NNN` is in folder A, `KEY-NNN+1` jumped to folder B) — drift recurs when iterating by key |90| **Version / last-modified** | Stale per the project's freshness threshold | Stale cases often contradict the live product (case-text drift) — see [`test-automation-workflow`](../test-automation-workflow/SKILL.md) § Reverse-masking guard |9192**How to probe.** Probe the *single-case status field* directly via your adapter (`get_field_value` / `fetch_case(id, fields=[status])` / equivalent). **Don't query-set** — JQL-style `status in (...)` queries on TMS custom fields are unreliable across adapters; verify the field on each case directly.9394**Outcomes:**9596- All probes clear → continue to Phase 1.97- Status excluded → don't fetch the body; return `out-of-scope-by-author` with the field value as evidence; close the case in the tracker (or mark per project convention).98- Folder/membership mismatch → don't dispatch; return to the orchestrator with the discrepancy. Iteration drift is an orchestrator-side routing issue, not an analyst-side execution issue.99- TMS unreachable for the probe → fall back to fetching the body (Phase 1 will surface it); flag the gap for scout to fill in `.agents/testing.md`.100101## The six-phase loop (one case at a time, runs AFTER Phase 0)102103```1041. Fetch the case → TMS adapter (pluggable; see test-automation.yaml)1052. Read app context → .agents/architecture.md + previous AFS files1063. Execute → browser-driving capability (your agent's wired MCP), step-by-step1074. Capture selectors → stable, accessible, fallback-ready1085. Classify findings → ready / already-covered / extend-existing / blocked / defect-found / un-automatable1096. Emit AFS → test-specs/<feature>/l<pri>_<slug>_<tms-id>.md110```111112### 1. Fetch the case113114Use the adapter declared in `.agents/test-automation.yaml`. If115`transport: mcp` and the MCP server is online, prefer MCP tool calls116(`mcp__<server>__<tool>` / `<server>/<tool>` depending on host) —117no secrets travel through the agent's context. Otherwise use HTTP118with the configured `auth_env`. If no adapter is configured, read119the markdown case from `test-specs/`. If the TMS is unreachable,120open the case in the browser and copy it by hand — do not block on a121flaky TMS.122123Extract: name, priority, preconditions, steps, expected, cleanup,124linked story, attachments.125126### 2. Read app context127128- `.agents/architecture.md` — know the surfaces you'll touch129- Previous AFS files in `test-specs/<feature>/` — match their shape130- Existing page objects — selector notes should align with what exists131132### 3. Execute133134Three browser tools sit at different layers; pick by what's wired and135what challenge you're solving. Full triage:136[`../test-automation-workflow/references/browser-tools.md`](../test-automation-workflow/references/browser-tools.md).137In short:138139- **Default** — [`playwright-testing`](../playwright-testing/)140 (Playwright MCP). Prefer its accessibility-snapshot tool for accessible-name141 discovery — it yields both the ref you need to click and the142 role-name pair you'll assert on.143- **MCP server not wired** — [`playwright-cli`](../playwright-cli/)144 drives the same browser surface from the shell (`codegen`,145 `--trace`, multi-tab, storage, request mocking).146- **Visual / CDP / a11y** — [`browser-verify`](../browser-verify/)147 for computed styles, real CDP input events, storage/cookies, or axe148 audits.149150Soft guidance, not a hard rule: switching tools mid-case is fine when151the first one isn't producing useful evidence — note which tool152produced which observation in the AFS so the next reader can follow.153154For each step:1551561. Perform the real action. Never synthesize a click via157 `page.evaluate` — the app may react differently.1582. Screenshot. Always.1593. Check console messages. **Even when the UI looks fine.** Silent160 JS errors are the worst bugs.1614. Check network. Note which requests fire and which payloads matter.1625. Observe actual vs expected. Record both if they differ.163164### 4. Capture selectors165166Priority order — document in the AFS for every interactive element:1671681. `data-testid` / `data-test` — stable, intentional1692. ARIA role + accessible name — `getByRole('button', { name: 'Apply' })`1703. Accessible label — `getByLabel('Email')`1714. Text content — `getByText('Sign in')` (fragile to i18n)1725. CSS selector — last resort; prefer one anchored to a stable attribute173174Always give a **fallback**. Apps change. A single selector per175element is a single point of failure.176177### 5. Classify findings178179Status per case (goes in the AFS metadata block):180181- **ready-for-automation** — case executed end-to-end, selectors182 captured, no blockers183- **already-covered** — Rule-6 behavioural-equivalence dedup against184 an existing merged spec. The observable this case asserts is185 already proven by another spec on file. No own implementation186 needed. Emit a *traceability AFS* at187 `test-specs/<feature>/lcovered_<slug>_<tms-id>.md` containing the188 **dedup proof**: covering spec at `file:line` + a one-paragraph189 behavioural-equivalence argument (why the existing assertion190 satisfies this case's expected observable). Link the original191 TMS case to the covering one in the tracker so the audit trail192 resolves both ways. The `lcovered_` filename prefix is the193 contract — downstream audits grep for it to enumerate194 Rule-6-dedup coverage distinct from fresh-implementation coverage.195- **extend-existing** — Rule-6 *partial*-overlap. An existing merged196 spec covers most of this case's observable, but a small number of197 assertions are missing. Don't write a fresh `.spec.ts`; the198 implementer extends the covering spec with the gap assertions.199 Emit an *extension AFS* at200 `test-specs/<feature>/lextend_<slug>_<tms-id>.md` containing: the201 covering spec at `file:line`, a one-paragraph behavioural-overlap202 argument (what's already proven), and a **Gap assertions** section203 listing exactly what the existing spec doesn't cover (the new204 selectors / observations / expecteds the implementer needs to205 append). Link the TMS case to the covering one in the tracker.206 The `lextend_` filename prefix is the contract — downstream audits207 distinguish extension work from fresh-implementation and from full208 `lcovered_` dedup. Boundary call: if the gap is large enough that209 the extension would be a near-rewrite of the covering spec, treat210 as `ready-for-automation` instead and let the implementer decide211 whether to extend or split.212- **blocked** — analyst hit a wall (access, data, env); the AFS's213 "Blocked Steps" section lists what's needed to unblock214- **defect-found** — real product bug prevents completion. File the215 ticket via your agent's bug-filing capability (see *When you find a216 defect* below for the routing rules) before emitting the AFS;217 reference the bug ID in the AFS218- **un-automatable** — keep as manual; do not emit an AFS; update219 the TMS note220221> **Reverse-masking guard — case-text drift is a CLARIFICATION, not222> a defect.** When the live product correctly diverges from the case223> text (case says ≥44px, product = 40px and that's the design;224> case says "Save button visible", product correctly removed Save),225> the **case text** is what's stale, not the product. Don't classify226> as `defect-found`; classify as `ready-for-automation` and assert227> the live contract. File the case-text drift as a CLARIFICATION228> per the project's `Bug filing style`, not a Bug. Full treatment229> in [`test-automation-workflow`](../test-automation-workflow/SKILL.md)230> § Reverse-masking guard.231232When you find a defect during execution:233234- Do not force-continue past it hoping it "probably works later".235- **Always file a tracking ticket.** Nothing slips through: every236 finding (clarification, question, blocker, full defect) gets237 tracked somewhere the team sees. How depends on profile.md.238- Determine **where** the ticket lands by reading239 `.agents/profile.md` § Project systems § Bug filing. Two orthogonal240 fields drive the routing — scout's Step 0.7 fills both:241242 **Issue tracker** — the *system* the ticket lands in243 (`github-issues` / `gitlab-issues` / `jira` / `azure-devops` /244 `linear` / …). Your agent has a bug-filing capability wired in; use245 it. Filing the ticket itself is not this skill's job — this skill246 hands you the *what* (severity, repro, evidence) and the *where*247 (tracker + style + target); your agent's bug-filing skill does248 the *how*.249250 **Bug filing style** — the *shape* of the ticket. Three styles:251 - **`github-issue`** *(default)* — open a standalone issue in the252 tracker named above. Same shape regardless of tracker system (a253 standalone issue in GitHub / GitLab / Jira / …).254 - **`story-subtask`** — create a sub-task under the originating255 story (Jira / Azure DevOps only; the story the TMS case is256 linked to). Fetch the story ID via the TMS adapter's257 `get_test_case_links`, then pass it as the parent when handing258 off to the bug-filing skill.259 - **`separate-ticket`** — file in a dedicated QA/bugs project,260 not the main development tracker. Target is named in261 profile.md § Bug filing target. Same tracker system, different262 project key.263- Determine **whether to bundle or split** by reading264 § Bundling policy and classifying the finding's severity:265 - **Classify the finding first**:266 - *Lightweight clarification / question* — expected behavior267 unclear, minor UI copy ambiguity, missing doc, "should this268 modal close on outside-click?"-type questions269 - *Real defect* — reproducible bug, functional breakage,270 incorrect data, blocker — anything where the product is271 provably wrong272 - **`strict-per-bug`** *(default)* — every finding (either class)273 gets its own ticket. Done.274 - **`bundle-per-case`** *(opt-in, requires umbrella-ticket275 convention already in place on the project)*:276 - If the finding is a *real defect* → its own ticket (same as277 strict-per-bug). Real defects never bundle.278 - If the finding is a *lightweight clarification* → check if279 there's already an open "umbrella" ticket for this TMS case.280 - If yes: add the finding as a comment on the existing ticket.281 - If no: file a new umbrella ticket (title e.g.282 "Clarifications for SCRUM-T101") and make this the first283 comment. Future lightweight findings on the same case284 attach here.285286 The umbrella-lookup is the fragile step — getting it wrong287 duplicates tickets. Defer to `strict-per-bug` unless the288 operator's `profile.md § Bug filing style` explicitly selects289 `bundle-per-case` **and** the project already has:290 - A title convention for umbrella tickets (so the291 find-or-create search has something stable to match on).292 - A documented comment-anchor format that the analyst can293 reference from the AFS (e.g. "comment-3" or a permalink fragment).294 Without both, `strict-per-bug` is the safe default; one more295 ticket is cheaper than a missed clarification.296- Hand the body, tracker, style, and (for `story-subtask`) parent297 story ID to your agent's bug-filing skill. Do not run a dev-side298 fix lifecycle (failing test → RCA → implement fix → verify) — those299 steps belong to whoever picks the defect up later, not to you300 during analysis. You file and walk away.301- If `.agents/profile.md` § Bug filing is `Unconfirmed`, or your302 agent has no wired tooling for the named tracker, stop and ask the303 operator before filing — don't pick a default silently. Flag the304 gap in the AFS so scout can fill the field on the next onboarding305 pass.306- Note the finding in the AFS under "Known Defects Found" with the307 ticket ID, filing style, and a recommendation — soft-expect308 (isolated) or natural-fail (blocking). Under `bundle-per-case`,309 reference both the umbrella ticket ID and the comment anchor so310 the downstream implementer can find the specific note (e.g.311 "Known defect: JIRA SCRUM-BUG-42 comment-3 — soft-expect", or312 "Known defect: GH#234 — natural-fail").313314### 6. Emit AFS315316A single markdown file per case, per the structure in317[`references/spec-format.md`](references/spec-format.md). Path:318319```320test-specs/<feature>/l<priority>_<slug>_<tms-id>.md321```322323The AFS is the contract. If it's ambiguous, the downstream engineer324will come back asking — which means the execution pass wasn't325complete. Make it stand alone.326327## Evidence paths (convention)328329```330test-results/screenshots/<tms-id>-step-<n>-<action>.png331test-results/json/<tms-id>-<iso-timestamp>.json332```333334Relative paths inside the AFS; the automation engineer re-uses the335same convention for CI artifacts.336337## Batching cases338339When handed multiple cases:340341- Single case → run directly. No delegation.342- Multiple cases → delegate one sub-agent per case via the host's343 subagent dispatch — `Agent(...)` (Claude Code), `runSubagent(...)`344 (Copilot). Each sub-agent gets its own browser context.345- After sub-agents finish, retrieve each one's final message via the346 host's result-retrieval tool (NOT a shell command), extract the347 AFS path, **verify the file exists on disk**, and recreate it348 yourself from the returned content if it didn't persist.349350## Handoff351352When the AFS is ready:3533541. Commit the AFS on a feature branch — `test(spec): add AFS for <id>`3552. Push; open a small PR if the project reviews specs before356 automation starts, otherwise hand the AFS path directly back to357 the caller (your agent knows whether the automation role expects358 the spec via PR or via direct handoff)3593. If a defect was found, link the issue in the PR body3604. If the case is `blocked` or `un-automatable`, stop here and361 report up — do not pass a broken spec downstream362363## Anti-patterns364365- **Writing automation code.** Not this skill's scope. Stop.366- **Copying the case text into the AFS verbatim without executing.**367 The AFS needs *discovered* selectors, *observed* network calls,368 *confirmed* expected vs actual. A copy-paste AFS is lying.369- **Skipping the console check** because "the UI looks fine". Silent370 errors are the ones that ship.371- **Force-continuing past a defect** to complete the AFS. A defect372 invalidates downstream steps — you no longer know what "expected"373 means.374- **Inventing selectors.** If you didn't click it, it doesn't go in375 the selector table. Run the step.376- **`test.fail()`-style thinking.** If a step fails for a real377 product reason, that's a defect, not a caveat in the AFS.378- **Skipping Phase 0 (case-gate)** because the case "looked fine"379 in a previous batch. Status / folder-membership / version drift380 between batches — re-probe per case, every dispatch.381- **Classifying case-text drift as `defect-found` instead of382 CLARIFICATION.** If live product is correct and the case is383 stale, the case is the bug, not the product. Asserting the384 stale case-text is reverse-masking (see § Classify findings note).385- **Re-implementing a case whose observable is already proven by386 another merged spec.** Rule-6 dedup → `already-covered` with a387 traceability AFS (`lcovered_*.md`), not a duplicate `.spec.ts`.388- **Filing partial overlap as fresh `ready-for-automation`.** When389 an existing merged spec covers most of the observable and only a390 small number of assertions are missing, classify as391 `extend-existing` with `lextend_*.md` + a Gap assertions section.392 Forcing the implementer to rediscover the overlap defeats Rule-6393 dedup and ends with two specs asserting the same behaviour.394395## References396397- [references/spec-format.md](references/spec-format.md) — the398 Automation-Friendly Spec (AFS) structure, required sections,399 examples. This is what the skill's output looks like.