Generate Test Plan from Source (FRS / Raw Code)
This skill produces QA-focused E2E test plans from a Functional Requirements Specification (FRS) or raw application source code. It does three things in parallel that QA reviews have shown are all needed for good coverage:
- Walks the FRS structurally — every section that can produce TCs (FRs, ACs, Exception Flows, Edge Cases, Business Rules, Alternative Flows, Notifications, Form Fields) is processed in turn so no section is silently skipped.
- Walks a Required Coverage Matrix — implicit TCs (duplicate values, max length, required fields, format, modal dismissal patterns, read-only enforcement, state transitions, cross-field rules, session edge cases, multi-tenancy, authorization, concurrency) are emitted whenever the data model or operation type implies them, regardless of whether the source enumerates them.
- Traces every TC back to its source — FR-ID, AC-ID, BR-ID, EC-ID, exception flow ID, or "Matrix" — so QA can audit coverage section by section.
- Generates Test Data per TC — populates a
## Test Data section in every TC, with concrete or templated values derived from each field's constraints and the TC's matrix intent. Consumed by generate-test-suite (phase 2) at codegen time. See references/test-data-generation.md for the value-generation rules.
| Input type |
Selector behaviour |
| FRS |
All step selectors → (discovered by explorer) — FRS describes what the system does, not how the UI is wired. |
| Raw code |
Selectors extracted from code (data-testid, id, name, aria-label, route paths). |
Workspace Layout Assumption
This skill assumes a multi-repo workspace where the docs/wiki repo lives alongside the UI and API repos — typically as a sibling, sometimes one level higher:
workspace/
ui/ ← frontend repo (skill may be run from here)
api/ ← backend repo (skill may be run from here)
docs/ ← wiki / knowledge repo (TC files land here)
Or a nested layout where the docs repo sits at the workspace root:
workspace/
frontend/
ui/ ← skill may be run from here
backend/
api/ ← skill may be run from here
wiki/ ← docs repo lives here
Common names for the docs/wiki repo: docs, wiki, knowledge, kb, documentation. The skill discovers the path automatically (Step 1) and asks the user when discovery is ambiguous or fails.
Throughout this skill, the resolved path is referred to as {docs_repo}. All TC paths take the form {docs_repo}/test-plans/{feature}/{use-case}/{feature}-TC-{NNN}.md.
Directory Structure
{docs_repo}/test-plans/
{feature}/
{use-case-a}/
{feature}-TC-001.md ← happy path
{feature}-TC-002.md ← validation / edge case
{use-case-b}/
{feature}-TC-003.md ← happy path
{feature}-TC-004.md ← error handling
...
Naming rules:
- Feature folder: kebab-case (
checklist, service-type, verification-preview)
- Use case sub-folder: kebab-case verb or action (
display, add, edit, delete, toggle, reorder, search, export, view, preview, auth)
- TC file:
{feature}-TC-{NNN}.md — sequential across the entire feature, not per sub-folder
TC File Format
# {feature}-TC-{NNN}: {Title} ({Category})
**Feature:** {Feature Name}
**Scenario:** {Letter} — {Scenario description}
**Priority:** {High | Medium | Low}
**Type:** Functional
**Tags:** @smoke @{feature} @{feature}-TC-{NNN}
**Traces to:** {FR-ID(s) | AC-ID(s) | BR-ID(s) | EC-ID(s) | Exception-Flow-ID(s) | Matrix: <row name>}
---
## Steps
| # | Step | Selector | Expected Result |
|---|------|----------|-----------------|
| 1 | Navigate to {path} | `n/a` | Page loads, {visible landmark} |
| 2 | Click "{button}" button | `[data-testid="{testid}"]` | {what happens} |
| 3 | Enter "{value}" in {field} | `[data-testid="{testid}"]` | {what appears} |
| 4 | Verify {element} | `(discovered by explorer)` | {expected state} |
---
## Preconditions
- {precondition 1}
- {precondition 2}
## Postconditions
- {postcondition 1}
- {postcondition 2}
## Test Data
### Pre-existing State
- {prose description of database state required before the test runs — e.g. "A Rule Template with Code 'TC-DUP-001' exists"}
- {prose description of user/role/permission state}
### Form Input
* {Field Name 1}: {value or templated value or `description → directive(args)`}
* {Field Name 2}: {value}
* {Field Name 3}: {value}
### Pre-existing State is omitted entirely when the TC needs no database state (most happy-path TCs). ### Form Input is omitted when the TC has no fill / select / toggle steps (pure read/view TCs).
Format rules
- Title includes the category in parentheses:
(Happy Path), (Validation), (Duplicate), (Max Length), (Authorization), (Read-Only), (Dismissal), (Edge Case), (Error Handling), (State Transition), (Cross-Field), (Concurrency), (Multi-Tenant), (Guard), (PENDING — OQ-NN)
- Scenario line uses a letter prefix:
A — Preview face row (Happy Path), B — Preview row with capture missing (Edge Case)
**Traces to:** line is mandatory — list every source ID this TC verifies, plus matrix row name if matrix-driven
@smoke tag only on happy-path TCs
- Steps table columns:
#, Step, Selector, Expected Result — in that order
- Selectors are backtick-wrapped:
`[data-testid="..."]`, `n/a`, or (discovered by explorer) (no backticks for placeholder)
- Dynamic selectors use the template notation with curly braces:
`[data-testid="checklist-row-{item.id}"]`
- Preconditions describe the required state before the test runs (data, role, environment, session state)
- Postconditions describe the expected system state after all steps pass (UI state, DB state if applicable, audit log entry, modal closed/open)
- Test Data has up to two sub-sections:
### Pre-existing State (prose, for DB/auth/permission state) and ### Form Input (one bullet per field touched by fill/select/toggle steps). Field names match the FRS Form Fields section verbatim (human-readable, not camelCase). Values are concrete literals, templated tokens ({timestamp}, {counter}, {uuid}, {tcNumber}), or description → directive(args) for matrix-driven violations. See references/test-data-generation.md for the value-generation rules and directive vocabulary.
- Horizontal rules (
---) separate the header, steps, and conditions sections
- For PENDING TCs, the title is prefixed
PENDING — OQ-NN — and Postconditions name the OQ-ID being awaited
FRS Section Walkthrough
When the input is an FRS, walk every section that produces TCs in the order below. Each section has its own extraction recipe. The frs-generator skill emits FRSes with sections numbered 1–23; this walkthrough refers to sections by name so it applies to any well-structured FRS, with the typical section number in parentheses for reference.
The walkthrough is the first TC source. Source-driven TCs from raw code (Step 4) and matrix-driven TCs (Step 6 Pass 2) are layered on top. Dedup across sources is done in Step 6.
Trigger (§9) + Main Flow (§10)
- Emit one Happy Path TC that exercises the trigger and walks every step of the main flow.
- Tag with
@smoke. Priority: High.
- Trace to: every FR-ID invoked along the flow + AC-IDs that map to flow steps.
- The Postconditions section quotes Section 13 (Postconditions, "On success") of the FRS verbatim where applicable.
Alternative Flows (§11)
- One TC per alternative flow (11a, 11b, …).
- Title pattern:
{Feature} — {Alt flow name} (Alternative Flow).
- Trace to: the alt flow's branch step + FR-IDs the alt flow exercises.
- If an alt flow only changes input data (e.g. "Face Verification Row Preview" vs. main flow being agnostic), the alt flow TC may consolidate into the main flow TC for the variant; in that case, add a separate TC for the other variant explicitly.
Exception Flows (§12)
- One TC per exception flow (12a, 12b, 12c, …).
- The exception's Trigger → reproduce as a Step in the TC.
- The exception's Outcome → assert as the Expected Result of the triggering step.
- The exception's Resolution → noted in Postconditions as the recovery path.
- Title pattern:
{Feature} — {Exception name} (Error Handling) or (Edge Case) for non-error exceptions.
- Trace to: the exception flow ID (e.g.
EX-12a) + any FR-IDs that mandate the exception behaviour.
Functional Requirements (§16)
- Every FR-ID must trace to at least one TC by the end of Step 6. This is verified in Step 10's coverage report.
- FRs that are pure flow restatements (e.g. "system SHALL present a modal when icon is selected") are typically already covered by the Main Flow TC — note the trace, do not emit a duplicate.
- FRs that assert negative properties — "SHALL be read-only", "SHALL NOT allow X", "SHALL contain no input fields" — get their own dedicated Read-Only / Guard TCs because they require explicit absence verification, not flow execution.
- FRs that assert quantitative properties — "exactly two images", "at most 50 results" — get their own assertion TC.
- FRs that assert labelling / identification properties — "clearly labelled", "unambiguously identified" — get their own UI-content TC.
Acceptance Criteria (§17)
- One TC per AC-ID unless the AC is a pure restatement of a flow already covered by an exception or alt-flow TC; in that case, attach the AC-ID to the existing TC's
Traces to line and do not duplicate.
- AC table in the FRS already shows traceability (
Traces to: FR-VP-01-01, FR-VP-01-02). Carry that traceability into the TC.
- ACs framed as observable conditions (
After X, the user sees Y) translate directly: precondition = X, expected result = Y.
Business Rules (§20)
- One TC per verifiable BR.
- BRs that are tautologies or pure scope statements (e.g. "this is read-only") may be skipped if a Read-Only Guard TC already covers them — in that case, attach the BR-ID to the existing TC's
Traces to.
- BRs that constrain availability ("preview is available only for the Agent's currently active session") map to authorization / scope TCs.
- BRs that constrain presentation ("two images shall be presented in a consistent, labelled arrangement for every row type") map to UI-consistency TCs that exercise multiple inputs and assert the consistent rendering.
Edge Cases (§21)
- One TC per EC-ID.
- An EC may overlap with an exception flow (e.g. EC-01 "captured image not yet taken" overlaps with 12a "image retrieval failure"). When an EC is genuinely a sub-case of an exception, attach the EC-ID to the exception TC's
Traces to and emit a more specific TC only if the EC adds a distinct precondition or assertion.
- ECs referencing an Open Question → emit as a
PENDING — OQ-NN TC.
Notifications (§14)
- If the section is
None, skip — do not invent notification TCs.
- Otherwise, emit one TC per notification covering: trigger event, recipient, channel, content. If the notification is sent to an external system (email, SMS, webhook), include verification of delivery in Postconditions.
Form Fields (§15)
- If the section is
None, skip the per-field validation walk — the operation has no actor-facing data entry.
- Otherwise, for each field in the table, walk the Required Coverage Matrix rows for Create/Edit (required, max length, format, duplicate, whitespace, special chars, range) using the field's declared constraints.
Postconditions (§13) and Preconditions (§6)
- These feed the Preconditions and Postconditions sections of every TC, not standalone TCs.
- The FRS Preconditions list constrains the test setup; the FRS "On success" Postcondition is the assertion target for happy-path TCs; "On failure" Postconditions inform exception-flow TCs.
Open Questions (§22)
- Do not invent answers.
- Any TC whose content depends on an OQ → prefix title with
PENDING — OQ-NN — and note the OQ in Postconditions.
- TCs that mention an OQ should still be emitted as placeholders so they aren't forgotten when the OQ resolves.
Scope — Out of Scope (§3)
- Items in the "Out of scope" list are NOT test targets.
- If an out-of-scope item could plausibly appear in the UI as a defect (e.g. an "annotation tool" in a read-only modal), emit at most one Guard TC asserting its absence. Label it
(Guard).
Auditability (§19)
- If the FRS specifies operation-specific audit obligations beyond cross-cutting defaults, emit one TC per audit assertion (verify the audit log entry contains the expected fields after the action).
- If the section defers entirely to cross-cutting defaults and the operation is read-only, no TCs are required from this section.
Sections that do NOT produce TCs
- Purpose (§1), Assumptions (§2), Glossary (§4), Actors (§5), Dependencies (§7), Data Entities (§8), NFRs (§18), Revision History (§23) — these provide context for extraction but do not directly emit TCs. Data Entities (§8) feeds the data-model fact sheet used in matrix Pass 2.
Required Coverage Matrix
This is the floor for every test plan, regardless of input type. For every use case category present in the feature, generate at minimum the TC types listed below. The source may add scenarios beyond these — generate those too. But matrix entries are mandatory whenever their conditions apply, even if the source never enumerates them.
The matrix exists because FRS authors describe behaviour, not exhaustive validation surface, and the same blind spots appear over and over in QA reviews — duplicate, max-length, required, format, modal dismissal patterns, read-only enforcement, cross-field rules, state transitions, multi-tenancy, concurrency, session edge cases. They're almost always implicit in the data model or operation type.
How to apply the matrix: For each requirement and each FRS section processed in the walkthrough, identify which categories the operation touches (Create, Update, Display, View/Modal, State Change…). Then walk the matrix rows for those categories and generate one TC per applicable row.
Display / List
| TC type |
When to generate |
Priority |
| Display populated list |
Always |
High |
| Display empty state |
Always |
Medium |
| Pagination — first / middle / last page |
If pagination is in scope |
Medium |
| Sorting — each sortable column ascending and descending |
If sorting in scope |
Medium |
| Filtering — each filter applied independently |
If filters in scope |
Medium |
| Search — match found, no match, special chars, empty query |
If search in scope |
Medium |
| Loading state visible during fetch |
Always |
Low |
| Server error during fetch — error message + retry |
Always |
Medium |
| Unauthorized user attempts list view |
When access control applies |
High |
View / Preview / Modal Detail (read-only views and dialogs)
This category covers any operation that opens a modal, dialog, or detail view to display information without editing. Drives test plans like FRS-VP-01.
| TC type |
When to generate |
Priority |
| Trigger opens the view (happy path) |
Always |
High |
| Verify expected content is displayed (count, fields, labels) |
Always |
High |
| Verify content labelling is correct and unambiguous |
Always |
High |
| Read-only enforcement — no editable inputs, no submit, no annotation tools |
Always |
High |
| Dismiss via primary close button |
Always |
High |
| Dismiss via ESC key |
Always |
Medium |
| Dismiss via backdrop click — verify behaviour matches design (close OR no-op) |
Always |
Medium |
| After dismissal — parent view returns to prior state |
Always |
High |
| Re-open the same target — content matches the first open |
Always |
Medium |
| Open view A, dismiss, open view B — view B shows B's content (no leakage) |
When the trigger is on a list with multiple targets |
Medium |
| Missing data — placeholder shown gracefully without breaking layout |
When the source identifies a missing-data exception (e.g. image unavailable) |
High |
| Partial data — some fields present, some missing — view stays usable |
When partial-failure is plausible |
Medium |
| Unauthorized target — view does not open, error shown |
When access control applies |
High |
| Session expires while view is open — view closes gracefully with notice |
When session lifetime applies |
Medium |
Create / Add
| TC type |
When to generate |
Priority |
| Happy path — all valid input |
Always |
High |
| Required field missing — one TC per required field |
Always |
High |
| Maximum length exceeded — one TC per text field with a length cap |
Always |
High |
| Minimum length violation — one TC per field with a minimum |
When a minimum exists |
Medium |
| Duplicate value — one TC per uniqueness constraint |
Always for unique fields |
High |
Duplicate with whitespace variants — "Admin" vs " admin " |
Always for unique text fields |
Medium |
Duplicate with case variants — "Admin" vs "ADMIN" |
When uniqueness is case-insensitive per spec, or when the spec is silent (flag for confirmation) |
Medium |
| Invalid format — email / phone / URL / date / number / decimal precision |
One TC per typed or formatted field |
High |
| Whitespace-only value for required text field |
Always |
Medium |
| Special characters / injection-style payload in free-text fields |
Always for free-text fields |
Medium |
| Out-of-range numeric value (negative when not allowed, exceeds upper bound) |
When numeric ranges apply |
Medium |
| Foreign-key reference to non-existent parent |
When FK fields exist |
Medium |
Cross-field comparison violation — End < Start, Discount > Subtotal |
When the FRS or schema specifies a cross-field rule |
High |
| Conditional required — Field B required only when Field A = X |
When conditional rules exist |
High |
| Either-or rule — at least one of A or B must be provided |
When the FRS specifies either-or |
High |
| Sum / total constraint — line totals must equal header total |
When applicable |
High |
| Unauthorized user attempts create |
When access control applies |
High |
Update / Edit
| TC type |
When to generate |
Priority |
| Happy path — valid edit |
Always |
High |
| Edit creates a duplicate value — one TC per uniqueness constraint |
Always for unique fields |
High |
| All Create validations re-apply on Edit (required, max length, format, cross-field, conditional) |
Always |
(varies) |
| Edit non-existent record (record deleted by another user / wrong ID) |
Always |
Medium |
| Concurrent edit / stale record (optimistic concurrency) |
Always for ABP entities — ConcurrencyStamp is default; only skip if concurrency is explicitly disabled |
High |
| Unauthorized user attempts edit |
When access control applies |
High |
| Cancel without saving — verify no changes persisted |
Always |
Low |
Delete
| TC type |
When to generate |
Priority |
| Happy path — delete with confirmation |
Always |
High |
| Cancel deletion — confirm record still exists |
Always |
Medium |
| Delete non-existent record |
Always |
Medium |
| Delete with dependent records — FK constraint blocks or cascades |
When dependencies exist |
High |
| Soft-delete verification — record hidden from list but retrievable |
When soft delete is the policy |
Medium |
| Restore after soft delete — record returns to list |
When restore is in scope |
Medium |
| Recreate after soft delete — uniqueness constraint behaviour |
When uniqueness applies and soft delete is the policy |
Medium |
| Unauthorized user attempts delete |
When access control applies |
High |
State / Workflow Transitions
For entities with a status, state, or workflow stage. Drives plans for approval workflows, order lifecycles, document states.
| TC type |
When to generate |
Priority |
| Each valid transition (e.g. Draft → Submitted, Submitted → Approved) |
One TC per valid transition |
High |
| Each invalid transition is rejected (e.g. Draft → Approved when Submitted is required) |
One TC per invalid transition |
High |
| Terminal state — record is read-only (no edit, no further transitions) |
When a terminal state exists |
High |
| Pre-condition state check — action only allowed when entity is in correct state |
One TC per state-gated action |
High |
| Concurrent transition by another actor — second actor sees current state |
When workflow is multi-actor |
Medium |
| Audit trail — transitions are recorded with actor, timestamp, from-state, to-state |
When auditability is in scope |
Medium |
Toggle / State change
| TC type |
When to generate |
Priority |
| Toggle on → off |
Always |
High |
| Toggle off → on |
Always |
High |
| Cascade effect on dependents (e.g. deactivating a parent hides children) |
When toggle has cascade semantics |
High |
| Unauthorized user attempts toggle |
When access control applies |
High |
Reorder / Sort
| TC type |
When to generate |
Priority |
| Move item up |
Always |
High |
| Move item down |
Always |
High |
| Move first item up — boundary, should be no-op or disabled |
Always |
Medium |
| Move last item down — boundary, should be no-op or disabled |
Always |
Medium |
| Reorder persists across page reload |
Always |
Medium |
Search / Filter
| TC type |
When to generate |
Priority |
| Exact match returns the record |
Always |
High |
| Partial match returns the record |
If partial / fuzzy matching is supported |
High |
| No match returns empty result with appropriate message |
Always |
Medium |
| Empty query — return all records or prompt |
Always |
Medium |
| Special characters in query do not break the search |
Always |
Medium |
| Case sensitivity behaviour matches spec |
Always |
Medium |
Export / Download
| TC type |
When to generate |
Priority |
| Export with data — file downloads with correct format |
Always |
High |
| Export with empty result set — file format still valid |
Always |
Medium |
| Export respects active filters |
When filters are in scope |
High |
| Unauthorized user attempts export |
When access control applies |
High |
Bulk Operations
| TC type |
When to generate |
Priority |
| Bulk select all — verify count and selection state |
When bulk select is in scope |
High |
| Bulk action (delete / update / export) on full selection — happy path |
When bulk action is in scope |
High |
| Bulk action with no selection — disabled or appropriate error |
When bulk action is in scope |
Medium |
| Bulk action partial failure — some succeed, some fail, summary reported |
When bulk action could fail per-row |
High |
| Bulk action authorization — unauthorized user blocked or partial success per-row |
When access control applies |
High |
Multi-Tenancy (ABP IMultiTenant entities)
| TC type |
When to generate |
Priority |
| Tenant A user cannot read Tenant B's record |
Always for IMultiTenant entities |
High |
| Tenant A user cannot edit / delete Tenant B's record |
Always for IMultiTenant entities |
High |
| Cross-tenant duplicate is allowed — same value in Tenant A and Tenant B does not collide |
Always for IMultiTenant entities with unique fields |
High |
| Host-level user accessing tenant data — behaviour matches access policy |
When host vs tenant boundaries are in scope |
Medium |
Session / Authentication Lifecycle
| TC type |
When to generate |
Priority |
| Action while session is active — succeeds |
Always (covered by happy path) |
High |
| Action while session is expired — redirected to login or rejected |
Always |
High |
| Session expires DURING an in-flight action — graceful handling, no orphaned UI state |
When the operation has a non-trivial duration (modals open, multi-step flows) |
High |
| Concurrent session limit — second login behaviour matches policy |
When concurrent-session policy is in scope |
Medium |
| Insufficient role / permission — request rejected with appropriate error |
Always when access control applies |
High |
Notifications (when present per FRS §14)
| TC type |
When to generate |
Priority |
| Notification sent on trigger event — recipient, channel, content correct |
One TC per notification |
High |
| Notification not sent when trigger does not fire |
One TC per notification |
Medium |
| Notification delivery failure — retry / log behaviour |
When delivery is asynchronous |
Medium |
The matrix is the floor, not the ceiling. The source may add scenarios beyond these — generate those too. But never skip a matrix entry whose conditions apply by saying "the FRS doesn't mention it" — the matrix is what the source leaves implicit.
Overview
Use this skill when a QA engineer provides an FRS document (.md, .docx, .pdf, or pasted text) or source code files / a directory and asks for a test plan. The skill produces individual TC files in the docs/wiki repo, organized by feature and use case category, covering:
- Every FRS section that produces TCs (Section Walkthrough)
- Every applicable Required Coverage Matrix row
- Every source-driven scenario from raw code (when applicable)
Every TC traces to its source — FR-ID, AC-ID, BR-ID, EC-ID, exception flow ID, or matrix row — so the resulting plan is auditable section by section.
Core principle: One flow variant = one TC file. One use case category = one sub-folder. Never mix unrelated actions in a single TC. Always cover the matrix floor and the section walkthrough before considering the plan complete.
Anti-Pattern: "I Can Guess the Selector"
When reading an FRS that says "Agent dismisses the modal", it is tempting to emit `[data-testid="btn-close-modal"]` because it seems obvious. Don't. The FRS describes behaviour, not markup. Guessed selectors cause silent test failures. Leave them as (discovered by explorer) and let the explorer skill resolve them against the real DOM.
Anti-Pattern: "I'll Just Write Files Wherever"
When the docs/wiki repo isn't immediately at ../docs/, it is tempting to fall back to a ./docs/ folder inside the current UI or API repo, or to write into the user's home directory. Don't. TC files belong in the docs/wiki repo, full stop. Run the discovery cascade in Step 1 and, if it fails, ask the user.
Anti-Pattern: "The FRS Didn't Mention It So I Won't Test It"
When the FRS describes only the happy path for "Add Service Type" — Name, Code, Description fields, Save button, success toast — it is tempting to emit only a happy-path TC. Don't. The data model implies a validation surface the FRS leaves unstated:
- Name and Code are almost certainly required → required-field TCs
- Name, Code, Description almost certainly have length caps → max-length TCs
- Code is almost certainly unique → duplicate TC
- Only authorized users can create → authorization TC
- The entity is
IMultiTenant → cross-tenant isolation TCs
The Required Coverage Matrix is what the FRS forgets. Generate the matrix entries even when no requirement statement maps directly to them.
Anti-Pattern: "It's Just a View, How Many TCs Could There Be?"
Read-only modal/preview operations look small at first glance — the FRS has a few FRs about showing two images, a couple of exception flows, and that's it. In reality, a single read-only modal needs around 10–18 TCs once the matrix is walked: trigger, content correctness, labelling, read-only enforcement (FRs that say "shall NOT allow editing" need explicit absence assertions), three or four dismissal paths (close button, ESC, backdrop click, browser back), re-open behaviour, multi-target leakage, missing data placeholders, partial data, unauthorized target, session expiry during view. The View / Preview / Modal Detail matrix category exists because this surface gets under-tested every time it's not made explicit.
Anti-Pattern: "FR-04 Is Already in the Main Flow Somehow"
FRs that assert negative properties — "SHALL be read-only", "SHALL contain no input fields", "SHALL NOT allow annotation" — are NOT covered by the Main Flow TC. The Main Flow demonstrates that the system does something; the negative FR demonstrates the system does not do something else. These need their own dedicated TC that explicitly asserts absence (no editable fields, no submit buttons, no annotation tools, etc.). Trace the negative FR to that dedicated guard TC, not to the happy path.
Anti-Pattern: "The Open Question Is Probably X, Let's Just Use That"
If the FRS has an Open Question (typically Section 22, e.g. "For document verification rows, what are the two specific images displayed?"), do NOT invent the answer. Emit any dependent TC with the title prefixed PENDING — OQ-NN — and Postconditions noting the OQ. The TC stays as a placeholder so it isn't forgotten when the OQ resolves. Inventing answers produces TCs that test something the system was never specced to do.
When to Use
Use when:
- A QA engineer provides an FRS document and asks for a test plan
- A QA engineer points to raw source code (components, pages, API routes) and asks for a test plan
- A QA engineer provides a mix of FRS + code and asks for a test plan
- A QA engineer wants a test plan for a new requirement that will be tested via UI / E2E
Do NOT use when:
- Input is user stories or acceptance criteria → use
skill:generate-test-plan-from-stories instead
- User wants to update selectors in existing TCs → use
updateSelector operation
- User wants to run or execute tests → different workflow entirely
Checklist
You MUST complete these in order:
- Locate the docs/wiki repo — run the discovery cascade (sibling → grandparent → ask) and record
{docs_repo}
- Classify input — determine FRS, raw code, or mixed
- Identify feature name — derive the kebab-case feature name
- Extract — three parallel outputs: (a) Section-walkthrough TC list (FRS only); (b) Source-driven requirement list; (c) Data-model fact sheet
- Identify use case categories — determine the sub-folders (display, view, add, edit, delete, etc.)
- Group into TCs per category — three passes: Pass 0 from the Section Walkthrough; Pass 1 from source-driven requirements; Pass 2 from the Required Coverage Matrix. Dedup across passes by attaching extra trace IDs to existing TCs rather than duplicating.
- Generate Test Data per TC — populate
### Pre-existing State and ### Form Input sub-sections per references/test-data-generation.md, derived from the data-model fact sheet and the TC's matrix intent
- Derive steps per TC — write Step / Selector / Expected Result rows
- Resolve selectors (code input only) — scan source for
data-testid, id, name, aria-label
- Emit TC files — create each file under the correct sub-folder in
{docs_repo}/test-plans/
- Print summary — show a table grouped by use case, including any skipped files, matrix coverage, and FR/AC/BR/EC traceability coverage
Process Flow
digraph process {
rankdir=TB;
locate [label="Locate docs/wiki repo\n(sibling → grandparent → ask)" shape=diamond];
classify [label="Classify input\n(FRS / code / mixed)" shape=diamond];
extract [label="Step 4 — three outputs:\n(a) Section walkthrough TCs (FRS)\n(b) Source-driven requirements\n(c) Data-model fact sheet" shape=box];
categories [label="Identify use case\ncategories (sub-folders)" shape=box];
pass0 [label="Pass 0: Walk the FRS\nsection walkthrough" shape=box];
pass1 [label="Pass 1: Source-driven TCs\n(from FRS REQs / code branches)" shape=box];
pass2 [label="Pass 2: Walk the Required\nCoverage Matrix" shape=box];
dedup [label="Dedup across passes\n(merge trace IDs)" shape=box];
steps [label="Derive steps\nper TC" shape=box];
selectors [label="Resolve selectors\nfrom code (code/mixed only)" shape=box];
emit [label="Emit TC files under\n{docs_repo}/test-plans/{feature}/{use-case}/" shape=box];
summary [label="Print summary table\n(created + skipped + matrix coverage\n+ FR/AC traceability coverage)" shape=doublecircle];
locate -> classify -> extract -> categories;
categories -> pass0 -> pass1 -> pass2 -> dedup;
dedup -> steps -> selectors -> emit -> summary;
}
The Process
Step 1: Locate the Docs/Wiki Repo
The TC files must land in the docs/wiki repo, not inside the UI or API repo. Resolve the path in this order — stop at the first match:
1a. Check the conventional sibling path. Test ../docs/ relative to the current working directory. If it exists and contains .git/ (or a test-plans/ directory from a prior run), accept it.
1b. Scan for sibling repos with conventional names. From the parent of the current working directory, list immediate subdirectories and match any of: docs, wiki, knowledge, kb, documentation (case-insensitive). Prefer ones that contain .git/ over plain folders. If exactly one match: accept it. If multiple matches: list them and ask the user which one.
1c. Walk up one more level. If the current repo is nested (e.g. workspace/frontend/ui/), check the grandparent for the same name patterns as 1b, with the same .git/ preference and same ambiguity rule.
1d. Ask the user. If 1a–1c all fail, stop and ask:
"I couldn't find a docs/wiki repo near here. Where should I write the TC files? (e.g. ../docs, ~/work/wiki, or an absolute path)"
Once the user replies, validate the path exists and is writable before proceeding.
Record the resolved base path as {docs_repo}. All subsequent file operations write to {docs_repo}/test-plans/....
- Verify:
{docs_repo} exists, is a directory, is writable, and is outside the current UI/API repo.
- On failure: Do not fall back to writing inside the current repo. Stop and ask.
Step 2: Classify Input
- Document (FRS, SRS, requirements doc): mode =
frs
- Source code files or directory: mode =
code
- Both provided: mode =
mixed (FRS drives the section walkthrough, code provides selectors and supplements implicit behaviour)
- Verify: You can name the mode and list every input file/section.
- On failure: Ask the user to clarify what they provided.
Step 3: Identify Feature Name
- Derive a kebab-case feature name from the FRS title or module name.
- FRS "Service Type Management" →
service-type
- FRS "Preview Verification Images Side-by-Side" (FRS-VP-01) →
verification-preview
- Verify: Feature name is kebab-case, module title is human-readable.
- On failure: Ask the user to confirm.
Step 4: Extract — Three Parallel Outputs
This step produces three outputs that feed Step 6's three passes.
(a) Section-walkthrough TC list (FRS only)
For each FRS section in the FRS Section Walkthrough above, walk the section and record candidate TCs with their trace IDs. The output is a structured list:
Section walkthrough TCs (FRS-VP-01):
Trigger + Main Flow:
- TC-cand-A: Preview face row — Happy Path [trace: FR-01, FR-02, FR-03, AC-01, AC-02]
Alternative Flows:
- 11a Face Verification Row Preview → consolidates with TC-cand-A
Exception Flows:
- TC-cand-B: Image retrieval failure — capture missing [trace: EX-12a, FR-04, EC-01]
- TC-cand-C: Image retrieval failure — reference missing [trace: EX-12a, FR-04, EC-02]
- TC-cand-D: Unauthorized session access [trace: EX-12b, BR-01]
- TC-cand-E: Session expired during preview [trace: EX-12c]
Functional Requirements (gap-fill — FRs not yet covered):
- FR-02 (exactly two images, labelled): partly covered by A; emit dedicated assertion TC-cand-F
- FR-05 (read-only — no input fields, no annotation, no submission): NOT covered → TC-cand-G
- FR-06 (modal dismissible, table state restored): partly covered by A; emit dedicated dismissal TC-cand-H
Acceptance Criteria (gap-fill):
- AC-04 (dismissal restores table state): mapped to TC-cand-H
- AC-05 (no input fields / submission controls): mapped to TC-cand-G
Business Rules (gap-fill):
- BR-04 (consistent labelling across row types): NOT covered → TC-cand-I
Edge Cases (gap-fill):
- EC-03 (document row preview): depends on OQ-01 → PENDING TC-cand-J
Notifications: None — skip
Form Fields: None — skip
Out of scope: capture, decision recording, reference-photo management → no TCs; consider one Guard TC if absence is verifiable
…(truncated)
1---2name: generate-test-plan3description: Use when a QA engineer needs a test plan generated from an FRS document or raw application code — NOT from user stories. Walks every structured section of the FRS (Functional Requirements, Acceptance Criteria, Main Flow, Alternative Flows, Exception Flows, Edge Cases, Business Rules, Notifications, Form Fields), auto-generates implicit TCs the source leaves unstated (validation, cross-field rules, state transitions, modal/view interactions, session edge cases, multi-tenancy, authorization, concurrency), AND populates a per-TC Test Data section with concrete or templated values derived from each field's constraints. Produces TC files in the docs/wiki repo at {docs_repo}/test-plans/{feature}/{use-case}/{feature}-TC-NNN.md, with each TC tracing back to its FR-ID / AC-ID / BR-ID / EC-ID / matrix row so coverage is auditable. The skill auto-discovers the docs/wiki repo (sibling lookup, then grandparent lookup, then asks). FRS input → placeholder selectors; raw-code input → selectors extracted from code. Test Data 4---56# Generate Test Plan from Source (FRS / Raw Code)78This skill produces **QA-focused E2E test plans** from a Functional Requirements Specification (FRS) or raw application source code. It does three things in parallel that QA reviews have shown are all needed for good coverage:9101. **Walks the FRS structurally** — every section that can produce TCs (FRs, ACs, Exception Flows, Edge Cases, Business Rules, Alternative Flows, Notifications, Form Fields) is processed in turn so no section is silently skipped.112. **Walks a Required Coverage Matrix** — implicit TCs (duplicate values, max length, required fields, format, modal dismissal patterns, read-only enforcement, state transitions, cross-field rules, session edge cases, multi-tenancy, authorization, concurrency) are emitted whenever the data model or operation type implies them, regardless of whether the source enumerates them.123. **Traces every TC back to its source** — FR-ID, AC-ID, BR-ID, EC-ID, exception flow ID, or "Matrix" — so QA can audit coverage section by section.134. **Generates Test Data per TC** — populates a `## Test Data` section in every TC, with concrete or templated values derived from each field's constraints and the TC's matrix intent. Consumed by `generate-test-suite` (phase 2) at codegen time. See `references/test-data-generation.md` for the value-generation rules.1415| Input type | Selector behaviour |16|---|---|17| **FRS** | All step selectors → `(discovered by explorer)` — FRS describes *what* the system does, not *how* the UI is wired. |18| **Raw code** | Selectors extracted from code (`data-testid`, `id`, `name`, `aria-label`, route paths). |1920<HARD-GATE>21Do NOT invent selectors when the input is an FRS. Every step selector MUST be `(discovered by explorer)` or `n/a` (for pure-navigation steps). This applies to EVERY TC regardless of how obvious the UI element seems.22</HARD-GATE>2324<HARD-GATE>25Do NOT write TC files inside the UI or API repo. Resolve the docs/wiki repo path via the discovery cascade in Step 1. If the cascade fails, STOP and ask the user — never silently fall back to a `./docs/` folder inside the current repo.26</HARD-GATE>2728<HARD-GATE>29For FRS input, walk EVERY applicable FRS section in the Section Walkthrough (below) — Functional Requirements, Acceptance Criteria, Exception Flows, Edge Cases, Business Rules, Alternative Flows, Notifications, Form Fields. Producing TCs only from the Main Flow is a Pass-1 failure. Every FR-ID and AC-ID MUST trace to at least one TC.30</HARD-GATE>3132<HARD-GATE>33Generate implicit TCs from the Required Coverage Matrix EVEN WHEN THE SOURCE DOES NOT ENUMERATE THEM. Duplicate-value, maximum-length, required-field, format, read-only enforcement, modal dismissal, state-transition, cross-field, multi-tenancy, concurrency, and authorization TCs are mandatory whenever the data model or operation type implies them. "The FRS didn't mention max length" or "the FRS didn't list ESC dismissal" is NEVER a valid reason to skip a matrix row whose conditions apply.34</HARD-GATE>3536<HARD-GATE>37Every TC MUST trace to its source. Add a `**Traces to:**` line in the TC header listing the FR-IDs, AC-IDs, BR-IDs, EC-IDs, exception-flow IDs, or `Matrix` (with row name) that this TC verifies. A TC with no traceability is a Pass-1 failure.38</HARD-GATE>3940<HARD-GATE>41Do NOT generate TCs for items listed in the FRS "Out of Scope" section (typically Section 3). If an out-of-scope item could plausibly appear due to a defect (e.g. an "annotation tool" that should NOT be present in a read-only modal), emit at most one low-priority **guard TC** that asserts its absence — and label it clearly.42</HARD-GATE>4344<HARD-GATE>45Do NOT invent answers to FRS Open Questions (typically Section 22). If a TC depends on an unresolved OQ, prefix the TC title with `PENDING — ` and note the OQ-ID in Postconditions. The TC stays in the plan as a placeholder so it isn't forgotten when the OQ resolves.46</HARD-GATE>4748<HARD-GATE>49Do NOT fabricate concrete Test Data values when the FRS doesn't constrain the field. The skill generates Test Data per `references/test-data-generation.md`, which uses the data-model fact sheet (Step 4) — required, max length, format, uniqueness, etc. — to produce values. When a constraint is genuinely absent (free-form notes field, no length cap, no format), emit `Field Name: TODO — constraint not parseable; manual fill needed` rather than guessing. Same honest-failure model as TODO selectors. Better than a value the test types and then fails on.50</HARD-GATE>5152<HARD-GATE>53Test Data values for unique fields MUST include `{timestamp}` or `{uuid}` in their happy-path templated form. Without per-run uniqueness, parallel test runs collide and re-runs of a single test fail because the value is already in the database from the prior run. The Step 6.5 verification rules (per `test-data-generation.md`) check this; failures block Step 9.54</HARD-GATE>5556---5758## Workspace Layout Assumption5960This skill assumes a multi-repo workspace where the docs/wiki repo lives **alongside** the UI and API repos — typically as a sibling, sometimes one level higher:6162```63workspace/64 ui/ ← frontend repo (skill may be run from here)65 api/ ← backend repo (skill may be run from here)66 docs/ ← wiki / knowledge repo (TC files land here)67```6869Or a nested layout where the docs repo sits at the workspace root:7071```72workspace/73 frontend/74 ui/ ← skill may be run from here75 backend/76 api/ ← skill may be run from here77 wiki/ ← docs repo lives here78```7980Common names for the docs/wiki repo: `docs`, `wiki`, `knowledge`, `kb`, `documentation`. The skill discovers the path automatically (Step 1) and asks the user when discovery is ambiguous or fails.8182Throughout this skill, the resolved path is referred to as `{docs_repo}`. All TC paths take the form `{docs_repo}/test-plans/{feature}/{use-case}/{feature}-TC-{NNN}.md`.8384---8586## Directory Structure8788```89{docs_repo}/test-plans/90 {feature}/91 {use-case-a}/92 {feature}-TC-001.md ← happy path93 {feature}-TC-002.md ← validation / edge case94 {use-case-b}/95 {feature}-TC-003.md ← happy path96 {feature}-TC-004.md ← error handling97 ...98```99100**Naming rules:**101- Feature folder: kebab-case (`checklist`, `service-type`, `verification-preview`)102- Use case sub-folder: kebab-case verb or action (`display`, `add`, `edit`, `delete`, `toggle`, `reorder`, `search`, `export`, `view`, `preview`, `auth`)103- TC file: `{feature}-TC-{NNN}.md` — sequential across the entire feature, not per sub-folder104105---106107## TC File Format108109```markdown110# {feature}-TC-{NNN}: {Title} ({Category})111112**Feature:** {Feature Name}113**Scenario:** {Letter} — {Scenario description}114**Priority:** {High | Medium | Low}115**Type:** Functional116**Tags:** @smoke @{feature} @{feature}-TC-{NNN}117**Traces to:** {FR-ID(s) | AC-ID(s) | BR-ID(s) | EC-ID(s) | Exception-Flow-ID(s) | Matrix: <row name>}118119---120121## Steps122123| # | Step | Selector | Expected Result |124|---|------|----------|-----------------|125| 1 | Navigate to {path} | `n/a` | Page loads, {visible landmark} |126| 2 | Click "{button}" button | `[data-testid="{testid}"]` | {what happens} |127| 3 | Enter "{value}" in {field} | `[data-testid="{testid}"]` | {what appears} |128| 4 | Verify {element} | `(discovered by explorer)` | {expected state} |129130---131132## Preconditions133- {precondition 1}134- {precondition 2}135136## Postconditions137- {postcondition 1}138- {postcondition 2}139140## Test Data141142### Pre-existing State143- {prose description of database state required before the test runs — e.g. "A Rule Template with Code 'TC-DUP-001' exists"}144- {prose description of user/role/permission state}145146### Form Input147* {Field Name 1}: {value or templated value or `description → directive(args)`}148* {Field Name 2}: {value}149* {Field Name 3}: {value}150```151152`### Pre-existing State` is omitted entirely when the TC needs no database state (most happy-path TCs). `### Form Input` is omitted when the TC has no `fill` / `select` / `toggle` steps (pure read/view TCs).153154### Format rules155156- Title includes the category in parentheses: `(Happy Path)`, `(Validation)`, `(Duplicate)`, `(Max Length)`, `(Authorization)`, `(Read-Only)`, `(Dismissal)`, `(Edge Case)`, `(Error Handling)`, `(State Transition)`, `(Cross-Field)`, `(Concurrency)`, `(Multi-Tenant)`, `(Guard)`, `(PENDING — OQ-NN)`157- Scenario line uses a letter prefix: `A — Preview face row (Happy Path)`, `B — Preview row with capture missing (Edge Case)`158- `**Traces to:**` line is mandatory — list every source ID this TC verifies, plus matrix row name if matrix-driven159- `@smoke` tag only on happy-path TCs160- Steps table columns: `#`, `Step`, `Selector`, `Expected Result` — in that order161- Selectors are backtick-wrapped: `` `[data-testid="..."]` ``, `` `n/a` ``, or `(discovered by explorer)` (no backticks for placeholder)162- Dynamic selectors use the template notation with curly braces: `` `[data-testid="checklist-row-{item.id}"]` ``163- Preconditions describe the required state **before** the test runs (data, role, environment, session state)164- Postconditions describe the expected system state **after** all steps pass (UI state, DB state if applicable, audit log entry, modal closed/open)165- **Test Data** has up to two sub-sections: `### Pre-existing State` (prose, for DB/auth/permission state) and `### Form Input` (one bullet per field touched by `fill`/`select`/`toggle` steps). Field names match the FRS Form Fields section verbatim (human-readable, not camelCase). Values are concrete literals, templated tokens (`{timestamp}`, `{counter}`, `{uuid}`, `{tcNumber}`), or `description → directive(args)` for matrix-driven violations. See `references/test-data-generation.md` for the value-generation rules and directive vocabulary.166- Horizontal rules (`---`) separate the header, steps, and conditions sections167- For PENDING TCs, the title is prefixed `PENDING — OQ-NN —` and Postconditions name the OQ-ID being awaited168169---170171## FRS Section Walkthrough172173When the input is an FRS, walk every section that produces TCs in the order below. Each section has its own extraction recipe. The frs-generator skill emits FRSes with sections numbered 1–23; this walkthrough refers to sections by name so it applies to any well-structured FRS, with the typical section number in parentheses for reference.174175The walkthrough is the **first** TC source. Source-driven TCs from raw code (Step 4) and matrix-driven TCs (Step 6 Pass 2) are layered on top. Dedup across sources is done in Step 6.176177### Trigger (§9) + Main Flow (§10)178179- Emit **one Happy Path TC** that exercises the trigger and walks every step of the main flow.180- Tag with `@smoke`. Priority: High.181- Trace to: every FR-ID invoked along the flow + AC-IDs that map to flow steps.182- The Postconditions section quotes Section 13 (Postconditions, "On success") of the FRS verbatim where applicable.183184### Alternative Flows (§11)185186- **One TC per alternative flow** (11a, 11b, …).187- Title pattern: `{Feature} — {Alt flow name} (Alternative Flow)`.188- Trace to: the alt flow's branch step + FR-IDs the alt flow exercises.189- If an alt flow only changes input data (e.g. "Face Verification Row Preview" vs. main flow being agnostic), the alt flow TC may consolidate into the main flow TC for the variant; in that case, add a separate TC for the *other* variant explicitly.190191### Exception Flows (§12)192193- **One TC per exception flow** (12a, 12b, 12c, …).194- The exception's **Trigger** → reproduce as a Step in the TC.195- The exception's **Outcome** → assert as the Expected Result of the triggering step.196- The exception's **Resolution** → noted in Postconditions as the recovery path.197- Title pattern: `{Feature} — {Exception name} (Error Handling)` or `(Edge Case)` for non-error exceptions.198- Trace to: the exception flow ID (e.g. `EX-12a`) + any FR-IDs that mandate the exception behaviour.199200### Functional Requirements (§16)201202- Every FR-ID **must trace to at least one TC** by the end of Step 6. This is verified in Step 10's coverage report.203- FRs that are pure flow restatements (e.g. "system SHALL present a modal when icon is selected") are typically already covered by the Main Flow TC — note the trace, do not emit a duplicate.204- FRs that assert *negative* properties — "SHALL be read-only", "SHALL NOT allow X", "SHALL contain no input fields" — get their own dedicated **Read-Only / Guard TCs** because they require explicit absence verification, not flow execution.205- FRs that assert *quantitative* properties — "exactly two images", "at most 50 results" — get their own assertion TC.206- FRs that assert *labelling / identification* properties — "clearly labelled", "unambiguously identified" — get their own UI-content TC.207208### Acceptance Criteria (§17)209210- **One TC per AC-ID** unless the AC is a pure restatement of a flow already covered by an exception or alt-flow TC; in that case, attach the AC-ID to the existing TC's `Traces to` line and do not duplicate.211- AC table in the FRS already shows traceability (`Traces to: FR-VP-01-01, FR-VP-01-02`). Carry that traceability into the TC.212- ACs framed as observable conditions (`After X, the user sees Y`) translate directly: precondition = X, expected result = Y.213214### Business Rules (§20)215216- **One TC per verifiable BR**.217- BRs that are tautologies or pure scope statements (e.g. "this is read-only") may be skipped if a Read-Only Guard TC already covers them — in that case, attach the BR-ID to the existing TC's `Traces to`.218- BRs that constrain **availability** ("preview is available only for the Agent's currently active session") map to authorization / scope TCs.219- BRs that constrain **presentation** ("two images shall be presented in a consistent, labelled arrangement for every row type") map to UI-consistency TCs that exercise multiple inputs and assert the consistent rendering.220221### Edge Cases (§21)222223- **One TC per EC-ID**.224- An EC may overlap with an exception flow (e.g. EC-01 "captured image not yet taken" overlaps with 12a "image retrieval failure"). When an EC is genuinely a sub-case of an exception, attach the EC-ID to the exception TC's `Traces to` and emit a more specific TC only if the EC adds a distinct precondition or assertion.225- ECs referencing an Open Question → emit as a `PENDING — OQ-NN` TC.226227### Notifications (§14)228229- If the section is `None`, skip — do **not** invent notification TCs.230- Otherwise, emit one TC per notification covering: trigger event, recipient, channel, content. If the notification is sent to an external system (email, SMS, webhook), include verification of delivery in Postconditions.231232### Form Fields (§15)233234- If the section is `None`, skip the per-field validation walk — the operation has no actor-facing data entry.235- Otherwise, for **each field** in the table, walk the Required Coverage Matrix rows for Create/Edit (required, max length, format, duplicate, whitespace, special chars, range) using the field's declared constraints.236237### Postconditions (§13) and Preconditions (§6)238239- These feed the Preconditions and Postconditions sections of every TC, not standalone TCs.240- The FRS Preconditions list constrains the test setup; the FRS "On success" Postcondition is the assertion target for happy-path TCs; "On failure" Postconditions inform exception-flow TCs.241242### Open Questions (§22)243244- Do not invent answers.245- Any TC whose content depends on an OQ → prefix title with `PENDING — OQ-NN — ` and note the OQ in Postconditions.246- TCs that mention an OQ should still be emitted as placeholders so they aren't forgotten when the OQ resolves.247248### Scope — Out of Scope (§3)249250- Items in the "Out of scope" list are NOT test targets.251- If an out-of-scope item could plausibly appear in the UI as a defect (e.g. an "annotation tool" in a read-only modal), emit at most one **Guard TC** asserting its absence. Label it `(Guard)`.252253### Auditability (§19)254255- If the FRS specifies operation-specific audit obligations beyond cross-cutting defaults, emit one TC per audit assertion (verify the audit log entry contains the expected fields after the action).256- If the section defers entirely to cross-cutting defaults and the operation is read-only, no TCs are required from this section.257258### Sections that do NOT produce TCs259260- Purpose (§1), Assumptions (§2), Glossary (§4), Actors (§5), Dependencies (§7), Data Entities (§8), NFRs (§18), Revision History (§23) — these provide context for extraction but do not directly emit TCs. Data Entities (§8) feeds the data-model fact sheet used in matrix Pass 2.261262263---264265## Required Coverage Matrix266267This is the **floor** for every test plan, regardless of input type. For every use case category present in the feature, generate **at minimum** the TC types listed below. The source may add scenarios beyond these — generate those too. But matrix entries are mandatory whenever their conditions apply, even if the source never enumerates them.268269The matrix exists because FRS authors describe behaviour, not exhaustive validation surface, and the same blind spots appear over and over in QA reviews — duplicate, max-length, required, format, modal dismissal patterns, read-only enforcement, cross-field rules, state transitions, multi-tenancy, concurrency, session edge cases. They're almost always implicit in the data model or operation type.270271**How to apply the matrix:** For each requirement and each FRS section processed in the walkthrough, identify which categories the operation touches (Create, Update, Display, View/Modal, State Change…). Then walk the matrix rows for those categories and generate one TC per applicable row.272273### Display / List274275| TC type | When to generate | Priority |276|---|---|---|277| Display populated list | Always | High |278| Display empty state | Always | Medium |279| Pagination — first / middle / last page | If pagination is in scope | Medium |280| Sorting — each sortable column ascending and descending | If sorting in scope | Medium |281| Filtering — each filter applied independently | If filters in scope | Medium |282| Search — match found, no match, special chars, empty query | If search in scope | Medium |283| Loading state visible during fetch | Always | Low |284| Server error during fetch — error message + retry | Always | Medium |285| Unauthorized user attempts list view | When access control applies | High |286287### View / Preview / Modal Detail (read-only views and dialogs)288289This category covers any operation that opens a modal, dialog, or detail view to **display** information without editing. Drives test plans like FRS-VP-01.290291| TC type | When to generate | Priority |292|---|---|---|293| Trigger opens the view (happy path) | Always | High |294| Verify expected content is displayed (count, fields, labels) | Always | High |295| Verify content labelling is correct and unambiguous | Always | High |296| Read-only enforcement — no editable inputs, no submit, no annotation tools | Always | High |297| Dismiss via primary close button | Always | High |298| Dismiss via ESC key | Always | Medium |299| Dismiss via backdrop click — verify behaviour matches design (close OR no-op) | Always | Medium |300| After dismissal — parent view returns to prior state | Always | High |301| Re-open the same target — content matches the first open | Always | Medium |302| Open view A, dismiss, open view B — view B shows B's content (no leakage) | When the trigger is on a list with multiple targets | Medium |303| Missing data — placeholder shown gracefully without breaking layout | When the source identifies a missing-data exception (e.g. image unavailable) | High |304| Partial data — some fields present, some missing — view stays usable | When partial-failure is plausible | Medium |305| Unauthorized target — view does not open, error shown | When access control applies | High |306| Session expires while view is open — view closes gracefully with notice | When session lifetime applies | Medium |307308### Create / Add309310| TC type | When to generate | Priority |311|---|---|---|312| Happy path — all valid input | Always | High |313| Required field missing — **one TC per required field** | Always | High |314| Maximum length exceeded — **one TC per text field with a length cap** | Always | High |315| Minimum length violation — one TC per field with a minimum | When a minimum exists | Medium |316| Duplicate value — **one TC per uniqueness constraint** | Always for unique fields | High |317| Duplicate with whitespace variants — `"Admin"` vs `" admin "` | Always for unique text fields | Medium |318| Duplicate with case variants — `"Admin"` vs `"ADMIN"` | When uniqueness is case-insensitive per spec, or when the spec is silent (flag for confirmation) | Medium |319| Invalid format — email / phone / URL / date / number / decimal precision | One TC per typed or formatted field | High |320| Whitespace-only value for required text field | Always | Medium |321| Special characters / injection-style payload in free-text fields | Always for free-text fields | Medium |322| Out-of-range numeric value (negative when not allowed, exceeds upper bound) | When numeric ranges apply | Medium |323| Foreign-key reference to non-existent parent | When FK fields exist | Medium |324| Cross-field comparison violation — `End < Start`, `Discount > Subtotal` | When the FRS or schema specifies a cross-field rule | High |325| Conditional required — Field B required only when Field A = X | When conditional rules exist | High |326| Either-or rule — at least one of A or B must be provided | When the FRS specifies either-or | High |327| Sum / total constraint — line totals must equal header total | When applicable | High |328| Unauthorized user attempts create | When access control applies | High |329330### Update / Edit331332| TC type | When to generate | Priority |333|---|---|---|334| Happy path — valid edit | Always | High |335| Edit creates a duplicate value — **one TC per uniqueness constraint** | Always for unique fields | High |336| All Create validations re-apply on Edit (required, max length, format, cross-field, conditional) | Always | (varies) |337| Edit non-existent record (record deleted by another user / wrong ID) | Always | Medium |338| Concurrent edit / stale record (optimistic concurrency) | **Always for ABP entities** — `ConcurrencyStamp` is default; only skip if concurrency is explicitly disabled | High |339| Unauthorized user attempts edit | When access control applies | High |340| Cancel without saving — verify no changes persisted | Always | Low |341342### Delete343344| TC type | When to generate | Priority |345|---|---|---|346| Happy path — delete with confirmation | Always | High |347| Cancel deletion — confirm record still exists | Always | Medium |348| Delete non-existent record | Always | Medium |349| Delete with dependent records — FK constraint blocks or cascades | When dependencies exist | High |350| Soft-delete verification — record hidden from list but retrievable | When soft delete is the policy | Medium |351| Restore after soft delete — record returns to list | When restore is in scope | Medium |352| Recreate after soft delete — uniqueness constraint behaviour | When uniqueness applies and soft delete is the policy | Medium |353| Unauthorized user attempts delete | When access control applies | High |354355### State / Workflow Transitions356357For entities with a `status`, `state`, or workflow stage. Drives plans for approval workflows, order lifecycles, document states.358359| TC type | When to generate | Priority |360|---|---|---|361| Each valid transition (e.g. Draft → Submitted, Submitted → Approved) | One TC per valid transition | High |362| Each invalid transition is rejected (e.g. Draft → Approved when Submitted is required) | One TC per invalid transition | High |363| Terminal state — record is read-only (no edit, no further transitions) | When a terminal state exists | High |364| Pre-condition state check — action only allowed when entity is in correct state | One TC per state-gated action | High |365| Concurrent transition by another actor — second actor sees current state | When workflow is multi-actor | Medium |366| Audit trail — transitions are recorded with actor, timestamp, from-state, to-state | When auditability is in scope | Medium |367368### Toggle / State change369370| TC type | When to generate | Priority |371|---|---|---|372| Toggle on → off | Always | High |373| Toggle off → on | Always | High |374| Cascade effect on dependents (e.g. deactivating a parent hides children) | When toggle has cascade semantics | High |375| Unauthorized user attempts toggle | When access control applies | High |376377### Reorder / Sort378379| TC type | When to generate | Priority |380|---|---|---|381| Move item up | Always | High |382| Move item down | Always | High |383| Move first item up — boundary, should be no-op or disabled | Always | Medium |384| Move last item down — boundary, should be no-op or disabled | Always | Medium |385| Reorder persists across page reload | Always | Medium |386387### Search / Filter388389| TC type | When to generate | Priority |390|---|---|---|391| Exact match returns the record | Always | High |392| Partial match returns the record | If partial / fuzzy matching is supported | High |393| No match returns empty result with appropriate message | Always | Medium |394| Empty query — return all records or prompt | Always | Medium |395| Special characters in query do not break the search | Always | Medium |396| Case sensitivity behaviour matches spec | Always | Medium |397398### Export / Download399400| TC type | When to generate | Priority |401|---|---|---|402| Export with data — file downloads with correct format | Always | High |403| Export with empty result set — file format still valid | Always | Medium |404| Export respects active filters | When filters are in scope | High |405| Unauthorized user attempts export | When access control applies | High |406407### Bulk Operations408409| TC type | When to generate | Priority |410|---|---|---|411| Bulk select all — verify count and selection state | When bulk select is in scope | High |412| Bulk action (delete / update / export) on full selection — happy path | When bulk action is in scope | High |413| Bulk action with no selection — disabled or appropriate error | When bulk action is in scope | Medium |414| Bulk action partial failure — some succeed, some fail, summary reported | When bulk action could fail per-row | High |415| Bulk action authorization — unauthorized user blocked or partial success per-row | When access control applies | High |416417### Multi-Tenancy (ABP `IMultiTenant` entities)418419| TC type | When to generate | Priority |420|---|---|---|421| Tenant A user cannot read Tenant B's record | Always for `IMultiTenant` entities | High |422| Tenant A user cannot edit / delete Tenant B's record | Always for `IMultiTenant` entities | High |423| Cross-tenant duplicate is allowed — same value in Tenant A and Tenant B does not collide | Always for `IMultiTenant` entities with unique fields | High |424| Host-level user accessing tenant data — behaviour matches access policy | When host vs tenant boundaries are in scope | Medium |425426### Session / Authentication Lifecycle427428| TC type | When to generate | Priority |429|---|---|---|430| Action while session is active — succeeds | Always (covered by happy path) | High |431| Action while session is expired — redirected to login or rejected | Always | High |432| Session expires DURING an in-flight action — graceful handling, no orphaned UI state | When the operation has a non-trivial duration (modals open, multi-step flows) | High |433| Concurrent session limit — second login behaviour matches policy | When concurrent-session policy is in scope | Medium |434| Insufficient role / permission — request rejected with appropriate error | Always when access control applies | High |435436### Notifications (when present per FRS §14)437438| TC type | When to generate | Priority |439|---|---|---|440| Notification sent on trigger event — recipient, channel, content correct | One TC per notification | High |441| Notification not sent when trigger does not fire | One TC per notification | Medium |442| Notification delivery failure — retry / log behaviour | When delivery is asynchronous | Medium |443444**The matrix is the floor, not the ceiling.** The source may add scenarios beyond these — generate those too. But never skip a matrix entry whose conditions apply by saying "the FRS doesn't mention it" — the matrix is what the source leaves implicit.445446447---448449## Overview450451Use this skill when a QA engineer provides an FRS document (`.md`, `.docx`, `.pdf`, or pasted text) or source code files / a directory and asks for a test plan. The skill produces individual TC files in the **docs/wiki repo**, organized by feature and use case category, covering:4524531. Every FRS section that produces TCs (Section Walkthrough)4542. Every applicable Required Coverage Matrix row4553. Every source-driven scenario from raw code (when applicable)456457Every TC traces to its source — FR-ID, AC-ID, BR-ID, EC-ID, exception flow ID, or matrix row — so the resulting plan is auditable section by section.458459**Core principle:** One flow variant = one TC file. One use case category = one sub-folder. Never mix unrelated actions in a single TC. Always cover the matrix floor and the section walkthrough before considering the plan complete.460461---462463## Anti-Pattern: "I Can Guess the Selector"464465When reading an FRS that says *"Agent dismisses the modal"*, it is tempting to emit `` `[data-testid="btn-close-modal"]` `` because it seems obvious. Don't. The FRS describes behaviour, not markup. Guessed selectors cause silent test failures. Leave them as `(discovered by explorer)` and let the explorer skill resolve them against the real DOM.466467---468469## Anti-Pattern: "I'll Just Write Files Wherever"470471When the docs/wiki repo isn't immediately at `../docs/`, it is tempting to fall back to a `./docs/` folder inside the current UI or API repo, or to write into the user's home directory. Don't. TC files belong in the docs/wiki repo, full stop. Run the discovery cascade in Step 1 and, if it fails, ask the user.472473---474475## Anti-Pattern: "The FRS Didn't Mention It So I Won't Test It"476477When the FRS describes only the happy path for "Add Service Type" — Name, Code, Description fields, Save button, success toast — it is tempting to emit only a happy-path TC. Don't. The data model implies a validation surface the FRS leaves unstated:478479- Name and Code are almost certainly required → required-field TCs480- Name, Code, Description almost certainly have length caps → max-length TCs481- Code is almost certainly unique → duplicate TC482- Only authorized users can create → authorization TC483- The entity is `IMultiTenant` → cross-tenant isolation TCs484485The Required Coverage Matrix is what the FRS forgets. **Generate the matrix entries even when no requirement statement maps directly to them.**486487---488489## Anti-Pattern: "It's Just a View, How Many TCs Could There Be?"490491Read-only modal/preview operations look small at first glance — the FRS has a few FRs about showing two images, a couple of exception flows, and that's it. In reality, a single read-only modal needs around 10–18 TCs once the matrix is walked: trigger, content correctness, labelling, **read-only enforcement** (FRs that say "shall NOT allow editing" need explicit absence assertions), three or four dismissal paths (close button, ESC, backdrop click, browser back), re-open behaviour, multi-target leakage, missing data placeholders, partial data, unauthorized target, session expiry during view. The View / Preview / Modal Detail matrix category exists because this surface gets under-tested every time it's not made explicit.492493---494495## Anti-Pattern: "FR-04 Is Already in the Main Flow Somehow"496497FRs that assert *negative* properties — "SHALL be read-only", "SHALL contain no input fields", "SHALL NOT allow annotation" — are NOT covered by the Main Flow TC. The Main Flow demonstrates that the system *does* something; the negative FR demonstrates the system *does not* do something else. These need their own dedicated TC that explicitly asserts absence (no editable fields, no submit buttons, no annotation tools, etc.). Trace the negative FR to that dedicated guard TC, not to the happy path.498499---500501## Anti-Pattern: "The Open Question Is Probably X, Let's Just Use That"502503If the FRS has an Open Question (typically Section 22, e.g. *"For document verification rows, what are the two specific images displayed?"*), do NOT invent the answer. Emit any dependent TC with the title prefixed `PENDING — OQ-NN — ` and Postconditions noting the OQ. The TC stays as a placeholder so it isn't forgotten when the OQ resolves. Inventing answers produces TCs that test something the system was never specced to do.504505---506507## When to Use508509**Use when:**510- A QA engineer provides an FRS document and asks for a test plan511- A QA engineer points to raw source code (components, pages, API routes) and asks for a test plan512- A QA engineer provides a mix of FRS + code and asks for a test plan513- A QA engineer wants a test plan for a new requirement that will be tested via UI / E2E514515**Do NOT use when:**516- Input is user stories or acceptance criteria → use `skill:generate-test-plan-from-stories` instead517- User wants to update selectors in existing TCs → use `updateSelector` operation518- User wants to run or execute tests → different workflow entirely519520---521522## Checklist523524You MUST complete these in order:5255261. **Locate the docs/wiki repo** — run the discovery cascade (sibling → grandparent → ask) and record `{docs_repo}`5272. **Classify input** — determine FRS, raw code, or mixed5283. **Identify feature name** — derive the kebab-case feature name5294. **Extract — three parallel outputs:** (a) Section-walkthrough TC list (FRS only); (b) Source-driven requirement list; (c) Data-model fact sheet5305. **Identify use case categories** — determine the sub-folders (display, view, add, edit, delete, etc.)5316. **Group into TCs per category — three passes:** Pass 0 from the Section Walkthrough; Pass 1 from source-driven requirements; Pass 2 from the Required Coverage Matrix. Dedup across passes by attaching extra trace IDs to existing TCs rather than duplicating.5327. **Generate Test Data per TC** — populate `### Pre-existing State` and `### Form Input` sub-sections per `references/test-data-generation.md`, derived from the data-model fact sheet and the TC's matrix intent5338. **Derive steps per TC** — write Step / Selector / Expected Result rows5349. **Resolve selectors (code input only)** — scan source for `data-testid`, `id`, `name`, `aria-label`53510. **Emit TC files** — create each file under the correct sub-folder in `{docs_repo}/test-plans/`53611. **Print summary** — show a table grouped by use case, including any skipped files, matrix coverage, **and FR/AC/BR/EC traceability coverage**537538539---540541## Process Flow542543```dot544digraph process {545 rankdir=TB;546547 locate [label="Locate docs/wiki repo\n(sibling → grandparent → ask)" shape=diamond];548 classify [label="Classify input\n(FRS / code / mixed)" shape=diamond];549 extract [label="Step 4 — three outputs:\n(a) Section walkthrough TCs (FRS)\n(b) Source-driven requirements\n(c) Data-model fact sheet" shape=box];550 categories [label="Identify use case\ncategories (sub-folders)" shape=box];551 pass0 [label="Pass 0: Walk the FRS\nsection walkthrough" shape=box];552 pass1 [label="Pass 1: Source-driven TCs\n(from FRS REQs / code branches)" shape=box];553 pass2 [label="Pass 2: Walk the Required\nCoverage Matrix" shape=box];554 dedup [label="Dedup across passes\n(merge trace IDs)" shape=box];555 steps [label="Derive steps\nper TC" shape=box];556 selectors [label="Resolve selectors\nfrom code (code/mixed only)" shape=box];557 emit [label="Emit TC files under\n{docs_repo}/test-plans/{feature}/{use-case}/" shape=box];558 summary [label="Print summary table\n(created + skipped + matrix coverage\n+ FR/AC traceability coverage)" shape=doublecircle];559560 locate -> classify -> extract -> categories;561 categories -> pass0 -> pass1 -> pass2 -> dedup;562 dedup -> steps -> selectors -> emit -> summary;563}564```565566---567568## The Process569570### Step 1: Locate the Docs/Wiki Repo571572The TC files must land in the docs/wiki repo, not inside the UI or API repo. Resolve the path in this order — stop at the first match:573574**1a. Check the conventional sibling path.** Test `../docs/` relative to the current working directory. If it exists and contains `.git/` (or a `test-plans/` directory from a prior run), accept it.575576**1b. Scan for sibling repos with conventional names.** From the parent of the current working directory, list immediate subdirectories and match any of: `docs`, `wiki`, `knowledge`, `kb`, `documentation` (case-insensitive). Prefer ones that contain `.git/` over plain folders. If exactly one match: accept it. If multiple matches: list them and ask the user which one.577578**1c. Walk up one more level.** If the current repo is nested (e.g. `workspace/frontend/ui/`), check the grandparent for the same name patterns as 1b, with the same `.git/` preference and same ambiguity rule.579580**1d. Ask the user.** If 1a–1c all fail, stop and ask:581> "I couldn't find a docs/wiki repo near here. Where should I write the TC files? (e.g. `../docs`, `~/work/wiki`, or an absolute path)"582583Once the user replies, validate the path exists and is writable before proceeding.584585**Record the resolved base path** as `{docs_repo}`. All subsequent file operations write to `{docs_repo}/test-plans/...`.586587- **Verify:** `{docs_repo}` exists, is a directory, is writable, and is **outside** the current UI/API repo.588- **On failure:** Do not fall back to writing inside the current repo. Stop and ask.589590### Step 2: Classify Input591592- Document (FRS, SRS, requirements doc): mode = `frs`593- Source code files or directory: mode = `code`594- Both provided: mode = `mixed` (FRS drives the section walkthrough, code provides selectors and supplements implicit behaviour)595- **Verify:** You can name the mode and list every input file/section.596- **On failure:** Ask the user to clarify what they provided.597598### Step 3: Identify Feature Name599600- Derive a kebab-case feature name from the FRS title or module name.601 - FRS "Service Type Management" → `service-type`602 - FRS "Preview Verification Images Side-by-Side" (FRS-VP-01) → `verification-preview`603- **Verify:** Feature name is kebab-case, module title is human-readable.604- **On failure:** Ask the user to confirm.605606### Step 4: Extract — Three Parallel Outputs607608This step produces **three** outputs that feed Step 6's three passes.609610#### (a) Section-walkthrough TC list (FRS only)611612For each FRS section in the **FRS Section Walkthrough** above, walk the section and record candidate TCs with their trace IDs. The output is a structured list:613614```615Section walkthrough TCs (FRS-VP-01):616 Trigger + Main Flow:617 - TC-cand-A: Preview face row — Happy Path [trace: FR-01, FR-02, FR-03, AC-01, AC-02]618 Alternative Flows:619 - 11a Face Verification Row Preview → consolidates with TC-cand-A620 Exception Flows:621 - TC-cand-B: Image retrieval failure — capture missing [trace: EX-12a, FR-04, EC-01]622 - TC-cand-C: Image retrieval failure — reference missing [trace: EX-12a, FR-04, EC-02]623 - TC-cand-D: Unauthorized session access [trace: EX-12b, BR-01]624 - TC-cand-E: Session expired during preview [trace: EX-12c]625 Functional Requirements (gap-fill — FRs not yet covered):626 - FR-02 (exactly two images, labelled): partly covered by A; emit dedicated assertion TC-cand-F627 - FR-05 (read-only — no input fields, no annotation, no submission): NOT covered → TC-cand-G628 - FR-06 (modal dismissible, table state restored): partly covered by A; emit dedicated dismissal TC-cand-H629 Acceptance Criteria (gap-fill):630 - AC-04 (dismissal restores table state): mapped to TC-cand-H631 - AC-05 (no input fields / submission controls): mapped to TC-cand-G632 Business Rules (gap-fill):633 - BR-04 (consistent labelling across row types): NOT covered → TC-cand-I634 Edge Cases (gap-fill):635 - EC-03 (document row preview): depends on OQ-01 → PENDING TC-cand-J636 Notifications: None — skip637 Form Fields: None — skip638 Out of scope: capture, decision recording, reference-photo management → no TCs; consider one Guard TC if absence is verifiable639```640641642…(truncated)