Generate a structured test plan from a ticket, PR, or feature description — includes PR analysis and risk scoring when PR link is found
Create a comprehensive test plan from a Linear ticket, GitHub PR, feature description, or any combination of sources.
Accept any combination of inputs — a Linear ticket is NOT required. Route by what the user provides:
WebFetch to fetch the Notion page content (interim until Notion MCP auth is complete)fileKey and nodeId from the URL (convert - to : in nodeId)mcp__figma__get_design_context to get component structure, code hints, and annotationsmcp__figma__get_screenshot for visual referenceWhen the source is an API design doc, OpenAPI spec, PDF, or live docs site:
WebFetch to crawl the docs site — get overview, authentication, each endpoint reference, schemas, pagination, errors, and rate limiting pagescurl to hit each endpoint and capture actual response shapes. Compare against what docs say.tests/api_tests/ for existing API test patterns (e.g., RegisterApi helper pattern)mcp__github__get_pull_request and mcp__github__get_pull_request_files to read the diffmcp__github__get_pull_request_status to check CI status — are tests already failing on this PR?services, cottage-nextjs, pg-admin), fall back to CLI: gh pr view <number> --repo Cottage-Energy/<repo> --json title,body,state,files and gh pr diff <number> --repo Cottage-Energy/<repo>When a ticket references the services repo or mentions Inngest, email templates, or async processing:
gh api repos/Cottage-Energy/services/contents/<path> --jq '.content' | base64 -dtests/docs/inngest-functions.md for known event names and patternsWhen the user provides a Linear project URL (e.g., linear.app/public-grid/project/<slug>/overview):
mcp__linear__get_project with the full slug from the URL (e.g., multi-processor-payment-system-54806c1fd524) — short name alone may not matchincludeMembers: true, includeMilestones: true, includeResources: true to get full contextmcp__linear__list_issues with project: "<Project Name>" to pull all ticketsnode -e to group by projectMilestone.name — do NOT try to read the raw JSON file directly{project_name}.md (no ticket ID prefix)mcp__linear__get_issue for requirements, labels, linked issuesmcp__linear__list_comments to read all comments on the ticket — comments often contain linked tickets, Figma URLs, Notion links, PR references, and contextual decisions not captured in the descriptionmcp__linear__get_issue to pull its context too. Related tickets often contain acceptance criteria, edge cases, and technical details that expand the test scope significantly.tests/docs/onboarding-flows.md to confirm the exact URL, entry point, and code path. Similar-sounding flows can be completely different code paths — e.g., Bill Upload and Verify Utilities share a Next.js route group (bill-upload) but have separate page.tsx files. A PR fixing one may not fix the other.mcp__supabase__list_tables to understand current schemamcp__supabase__execute_sql to inspect table structures, column types, constraints, and existing data relevant to the featureWhen planning tests for an existing feature or flow, peek at the live app to ground your test cases in reality:
mcp__playwright__browser_navigate to the page or flow being testedmcp__playwright__browser_snapshot to see current UI elements, form fields, buttons, and component statesmcp__playwright__browser_take_screenshot to capture the current state for referencetests/e2e_tests/ with Glob/Grep for related tests already in the repoWhen mermaid flowchart blocks are found in Notion content or pasted by the user:
When a PR link is found — either provided directly or discovered in a Linear ticket — run this analysis to enrich the test plan with code-level context. Skip if no PR link exists.
For each changed file in the PR, classify:
| Category | Examples |
|---|---|
| UI | New/modified components, pages, forms, modals, changed text/labels/buttons (affects POM locators) |
| API | New/modified endpoints, request/response shapes, changed validation, error handling |
| Database | Migrations, new tables/columns, changed constraints, queries, feature flags |
| Business Logic | Validation rules, calculations, state transitions, permissions, conditional rendering |
| Configuration | Env vars, feature flags, third-party integrations, build/deploy config |
| Level | Criteria |
|---|---|
| High | Shared UI components (affects multiple pages), DB schema changes, auth/authz changes, navigation/routing changes, move-in or payment flow changes |
| Medium | Single feature UI/logic changes, new API endpoints, feature flag gating |
| Low | Styling-only, documentation, test-only, dev tooling, CI config |
Glob + Grep in tests/e2e_tests/ for tests covering the changed feature areatests/resources/page_objects/ for locators referencing changed elements → list POMs to updatetests/resources/fixtures/ for queries hitting changed tables → list fixtures to updateIf the PR is merged/deployed and includes UI changes:
mcp__playwright__browser_navigate to affected page(s) on devmcp__playwright__browser_take_screenshot to capture live statemcp__figma__get_screenshot for design comparison### PR Analysis: #[NUMBER] — [TITLE]
**Repo**: [owner/repo] | **CI**: [passing/failing] | **Risk**: [HIGH/MEDIUM/LOW]
| File | Category | Change |
|------|----------|--------|
| [file] | UI | [what changed] |
| [file] | Database | [what changed] |
**Test Impact**:
- Existing tests to verify: [list]
- POMs to update: [list or "none"]
- Fixtures to update: [list or "none"]
- New tests needed: [fed into Step 4]
Before writing detailed test cases, present a triage summary so the user can confirm scope:
## Triage: [Feature/Ticket Name]
### Summary
Brief description of what's being tested and why.
### Test Requirements
- [ ] Requirement 1
- [ ] Requirement 2
### Linked Resources
- Source: [Notion / Figma / PR / Linear — with links]
- Related files/components changed
### Regression Risk
- [Existing features that may be affected]
After presenting the triage, ask: "Want me to continue to a full test plan?"
From the gathered context, define:
While analyzing the feature context (designs, live app, PRs, ticket), actively note anything that could be improved from a user experience perspective — even if it's working as coded. You're reviewing the feature more thoroughly than most people on the team, so use that perspective.
Look for:
Capture these in the "UX & Improvement Opportunities" section of the test plan. They don't block test planning — they're a valuable byproduct of your analysis.
For each scenario, define:
# Test Plan: [Feature/Ticket Name]
## Overview
**Ticket**: [Linear ID or "N/A"]
**Source**: [Notion doc link / Figma link / PR link — list all inputs used]
**Date**: [date]
**Tester**: Christian
## Scope
### In Scope
- [Feature/flow 1]
- [Feature/flow 2]
### Out of Scope
- [Explicitly excluded items]
### Prerequisites
- [Required test data]
- [Environment/feature flag setup]
## Test Cases
### Happy Path
| ID | Title | Steps | Expected Result | Priority | Automate? |
|----|-------|-------|-----------------|----------|-----------|
| TC-001 | [title] | 1. ... 2. ... | [expected] | P0 | Yes |
| TC-002 | [title] | 1. ... 2. ... | [expected] | P1 | Yes |
### Edge Cases
| ID | Title | Steps | Expected Result | Priority | Automate? |
|----|-------|-------|-----------------|----------|-----------|
| TC-010 | [title] | 1. ... 2. ... | [expected] | P2 | No |
### Negative Tests
| ID | Title | Steps | Expected Result | Priority | Automate? |
|----|-------|-------|-----------------|----------|-----------|
| TC-020 | [title] | 1. ... 2. ... | [expected] | P2 | Yes |
### Database Verification
| ID | Title | Query/Check | Expected Result | Priority |
|----|-------|-------------|-----------------|----------|
| TC-030 | [title] | [what to check in Supabase] | [expected] | P1 |
### UX & Improvement Opportunities
| ID | Screen/Step | Observation | Impact | Suggestion |
|----|------------|-------------|--------|------------|
| UX-001 | [where in the flow] | [what could be better — friction, confusion, inconsistency, missing feedback] | [user impact — drop-off risk, confusion, frustration] | [concrete improvement idea] |
| UX-002 | [where] | [observation] | [impact] | [suggestion] |
> These are not test failures — the feature works as specified. These are opportunities to improve the user experience identified during test planning. File actionable ones as improvement tickets via `/log-bug`.
## Automation Plan
- **Smoke**: [which test cases to include in smoke suite]
- **Regression**: [which regression scope to assign]
- **Exploratory only**: [which cases stay manual]
## Risks & Notes
- [Known risks or blockers]
- [Dependencies on other teams]
tests/test_plans/ directory{TICKET_ID}_{feature_name}.md when a Linear ticket exists, or {feature_name}.md when there is no ticketWhen a Linear ticket was used as input (i.e., an issueId is available from Step 1):
IMPORTANT: NEVER modify the ticket description. Post the test plan as a COMMENT only. The @mseep/linear-mcp package does NOT have a comment tool — use the Linear GraphQL API directly:
This step is REQUIRED, not optional. If mcp__linear__get_issue returned 401 or any auth error, that does NOT excuse skipping the comment — fall back to the GraphQL approach below. Only skip if the user explicitly asked you to (or if no Linear ticket exists for this work). The comment is how the rest of the team learns QA coverage exists; silently skipping it makes the work invisible.
Env var trap when running Node: source .env only sets bash shell vars — child processes (node) get nothing. Use set -a; source .env; set +a so process.env.LINEAR_API_KEY is actually populated. Verify with node -e "console.log(!!process.env.LINEAR_API_KEY)" if a mutation returns 401 despite the key being correct.
// Post comment via Linear GraphQL API (NOT update_issue)
// Resolve ENG-XXXX → UUID first via the issue query, then commentCreate with the UUID.
fetch('https://api.linear.app/graphql', {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'Authorization': process.env.LINEAR_API_KEY },
body: JSON.stringify({
query: 'mutation($input: CommentCreateInput!) { commentCreate(input: $input) { success comment { id url } } }',
variables: { input: { issueId: '<issue-uuid>', body: commentMarkdown } }
})
});
Format the comment body as Markdown:
## Test Plan: [Feature Name]
**File**: `tests/test_plans/{filename}.md`
**Date**: [date]
**Tester**: Christian
### Summary
[1-2 sentence summary of what the test plan covers]
### Test Cases ([total count])
- **Happy Path**: [count] cases ([P0/P1 breakdown])
- **Edge Cases**: [count] cases
- **Negative Tests**: [count] cases
- **DB Verification**: [count] cases
### Automation Plan
- **Smoke**: [cases targeted for smoke]
- **Regression**: [cases targeted for regression]
- **Exploratory only**: [cases staying manual]
### UX Observations
- [Count] improvement opportunities identified (see full plan for details)
### Risks
- [Key risks or blockers, if any]
---
*Generated by QA automation — full test plan saved to cottage-tests repo*
After the test plan is approved:
/create-test to scaffold automated test cases (will reference this plan)/exploratory-test to interactively investigate items marked "Exploratory only"/log-bug if issues are found during analysis/run-tests to execute tests after they're createdAfter saving the test plan, check if the feature involves a backend flow or Inngest function not yet documented:
tests/docs/inngest-functions.mdtests/docs/| Tool | Purpose |
|---|---|
| Linear MCP | get_issue — pull ticket requirements and linked resources; list_comments — read ticket comments for linked tickets, Figma/Notion URLs, and contextual decisions; save_comment — post test plan summary back to ticket |
| GitHub MCP | get_pull_request, get_pull_request_files — read PR diffs for code-driven planning |
| Figma MCP | get_design_context, get_screenshot — extract UI components and design expectations |
| Supabase MCP | list_tables, execute_sql — inspect schema, constraints, and data context for DB-related test cases |
| Playwright MCP | browser_navigate, browser_snapshot, browser_take_screenshot — peek at live app to ground test cases in reality |
WebFetch | Fetch Notion page content (interim until Notion MCP auth) |
| Exa MCP | web_search_exa — search for testing patterns, edge cases, and domain context; get_code_context_exa — find code examples for similar test scenarios; crawling_exa — fetch and parse external documentation URLs |
Glob, Grep | Find existing test coverage in the repo |
Write | Save the test plan to tests/test_plans/ |
After completing this skill, check: did any step not match reality? Did a tool not work as expected? Did you discover a better approach? If so, update this SKILL.md with what you learned.
mcp__linear__get_project + mcp__linear__list_issues with project filter. Added "Linear Project Link" section to Step 1.get_project needs full URL slug: Short name (multi-processor-payment-system) returned "not found". Full slug from URL (multi-processor-payment-system-54806c1fd524) worked.node -e and group by projectMilestone.name (not milestone — that field doesn't exist on project issues).mcp__linear__list_comments step and explicit "follow linked tickets" instruction to Step 1.isConnectReady, isConnectAccount, cottageConnectUserType, and account status fields was critical for writing accurate test preconditions. The DB context step worked well once we queried information_schema.columns first.mcp__linear__get_issue returned 401, I saved the test plan to disk but skipped posting the summary comment. The user had to explicitly call this out ("why didn't you comment on the ticket?"). The skill DOES say to use the GraphQL API directly when the MCP can't post — I read it but treated MCP failure as authorization to skip rather than as the trigger to fall back. Tightened Step 6 to make this REQUIRED, not optional.source .env doesn't pass vars to Node — caught me twice in the same session. LINEAR_API_KEY was set in bash but process.env.LINEAR_API_KEY was undefined in Node child processes, so the commentCreate mutation got an empty Authorization header → 401. Fix: set -a; source .env; set +a. Same trap applies to FASTMAIL_API_KEY, MOVED_API_KEY, etc. Documented in Step 6 + saved to memory as feedback_env_export_for_node.md./api/v1/moved/embed; reality is /v1/... (404 on the documented path). Always curl the documented path FIRST before writing types — the 30-second probe caught discrepancies that would have wasted hours of test rewrite. Added emphasis to the "Probe the live API before finalizing test cases" instruction in the API Spec section of Step 1.[IMPROVEMENT] tickets with concrete evidence, and gave the backend team a single source of truth instead of trickling discoveries one by one. The format (# | Type | Doc says | Reality | Severity) worked well — keeping it as a template.