plan-qa

SkillAI & models

Generate QA test plan YAML from ticket

Available today. Use it from your connected AI after setup.

Connect ahel once, and every AI you use reads what you have installed.

Then ask your AI: use the plan-qa skill

What this skill tells your AI

The instructions your AI receives, as published by rlajous/claude-code-commands in skills/plan-qa/SKILL.md and read by ahel’s review.

Cross-runtime: follow runtime compatibility for invocation, delegation, configuration precedence, state paths, and permissions.

You are helping generate a QA test plan from a ticket description, issue tracker ticket, or requirements. This command creates a structured YAML test plan that can be executed with /start-qa.

Step 1: Load Configuration

Check for configuration:

if [ -f ".git-workflow/config.yaml" ]; then CONFIG_PATH=".git-workflow/config.yaml"; elif [ -f ".claude/config.yaml" ]; then CONFIG_PATH=".claude/config.yaml"; else CONFIG_PATH=""; fi

Load from the resolved CONFIG_PATH (canonical first, legacy read-only fallback):

qa:
  apiBaseUrl: ${API_BASE_URL}
  testPlansDir: tests/qa
  resultsDir: tests/qa/results
  timeout: 10
  sqsQueueUrl: ${SQS_QUEUE_URL}
issueTracker:
  type: auto

Default Values:

qa:
  apiBaseUrl: http://localhost:3000
  testPlansDir: tests/qa
  timeout: 10

Step 2: Parse Arguments

Extract from $ARGUMENTS:

$ARGUMENTS

Patterns to Extract:

PatternExampleMeaning
Ticket IDPROJ-123, ENG-456Issue tracker ticket
--url <base_url>--url https://api.example.comOverride API base URL
--sqs <queue_url>--sqs https://sqs.../queueSQS queue for events
--sqs-env <VAR_NAME>--sqs-env SQS_EVENTS_QUEUESQS queue from env var
Remaining textTest the /v2/risk endpointTask description

Parsing Logic:

// Extract ticket ID
const ticketMatch = args.match(/([A-Z]+-\d+)/);
const ticketId = ticketMatch ? ticketMatch[1] : null;

// Extract URL override
const urlMatch = args.match(/--url\s+(\S+)/);
const baseUrl = urlMatch ? urlMatch[1] : config.qa.apiBaseUrl;

// Extract SQS queue
const sqsMatch = args.match(/--sqs\s+(\S+)/);
const sqsEnvMatch = args.match(/--sqs-env\s+(\S+)/);
const sqsQueue = sqsMatch ? sqsMatch[1] : sqsEnvMatch ? `\${${sqsEnvMatch[1]}}` : null;

// Remaining is description
const description = args.replace(ticketMatch, '').replace(urlMatch, '').trim();

Step 3: Fetch Ticket Details (If ID Provided)

Linear Integration (via MCP)

If ticket matches Linear format, use the Linear MCP server:

mcp__linear__get_issue(id: ticketId)

Extract:

  • Title
  • Description
  • Acceptance criteria (from comments or description)
  • Labels/tags

The MCP server handles authentication automatically via OAuth.

Jira Integration (via MCP)

If Jira configured, use the Jira MCP server:

mcp__jira__get_issue(issueKey: ticketId)

Extract:

  • Summary (title)
  • Description
  • Acceptance criteria
  • Labels

The MCP server handles authentication automatically.

GitHub Issues

If GitHub issue format (#123), use the gh CLI:

gh issue view ${ISSUE_NUMBER} --json title,body,labels

Step 4: Analyze Requirements

From the ticket or description, identify:

Endpoints to Test

Look for patterns in description:

  • GET /api/v1/users
  • POST /endpoint
  • Path patterns: /v1/, /v2/, /api/
  • HTTP methods: GET, POST, PUT, PATCH, DELETE

Expected Behaviors

Extract from acceptance criteria:

  • Success conditions
  • Error scenarios
  • Edge cases
  • Authentication requirements

SQS Events (If Mentioned)

Look for:

  • Event names (e.g., "USER_CREATED", "ORDER_PLACED")
  • Queue mentions
  • Async processing requirements

Step 5: Generate Test Plan

Create the test plan YAML:

# Auto-generated QA Test Plan
# Generated: {ISO_TIMESTAMP}
# Source: {TICKET_ID or "Manual description"}

name: "{DESCRIPTIVE_NAME}"
type: feature # feature | bug_fix | regression | smoke
description: |
  {DESCRIPTION_FROM_TICKET_OR_INPUT}

# Base configuration
base_url: "${API_BASE_URL}" # or explicit URL
timeout: 10 # seconds

# Endpoints under test
endpoints:
  - path: /api/v1/{endpoint}
    method: GET
    description: "{What this endpoint does}"
    auth_required: true

# Test cases
test_cases:
  # Happy path tests
  - id: TC-001
    name: "{Descriptive test name}"
    type: happy_path
    endpoint: /api/v1/{endpoint}
    method: GET
    headers:
      Authorization: "Bearer ${API_TOKEN}"
      Content-Type: application/json
    params:
      key: value
    expected:
      status: 200
      body:
        # Key assertions
        success: true
    priority: high
    tags: [smoke, happy-path]

  # Validation tests
  - id: TC-002
    name: "Invalid input returns 400"
    type: validation
    endpoint: /api/v1/{endpoint}
    method: POST
    body:
      invalid_field: "bad_value"
    expected:
      status: 400
      body:
        error: true
    priority: medium
    tags: [validation]

  # Authentication tests
  - id: TC-003
    name: "Missing auth returns 401"
    type: auth
    endpoint: /api/v1/{endpoint}
    method: GET
    # No Authorization header
    expected:
      status: 401
    priority: high
    tags: [auth, security]

  # Edge cases
  - id: TC-004
    name: "Empty list returns empty array"
    type: edge_case
    endpoint: /api/v1/{endpoint}
    method: GET
    params:
      filter: "nonexistent"
    expected:
      status: 200
      body:
        items: []
    priority: low
    tags: [edge-case]

# SQS assertions (if applicable)
sqs_assertions:
  - name: "Event published after action"
    queue_url: "${SQS_QUEUE_URL}"
    trigger_test: TC-001 # Which test triggers this event
    match_mode: json_field # json_field | contains | exact
    expected_fields:
      event_type: "EVENT_NAME"
      # Additional field assertions
    timeout_seconds: 30
    required: true

# Acceptance criteria (from ticket)
acceptance_criteria:
  - "{AC 1 from ticket}"
  - "{AC 2 from ticket}"
  - "{AC 3 from ticket}"

# Test environment
environment:
  variables:
    - API_BASE_URL
    - API_TOKEN
  setup_commands: []
  cleanup_commands: []

Step 6: Save Test File

Determine file name:

// From ticket ID (lowercase)
const slug = ticketId ? ticketId.toLowerCase() : null;

// Or from description (kebab-case)
const descSlug = description
  .toLowerCase()
  .replace(/[^a-z0-9]+/g, '-')
  .slice(0, 50);

const fileName = `${slug || descSlug}-test.yaml`;

Save to configured directory:

TEST_DIR=$(config.qa.testPlansDir || "tests/qa")
mkdir -p ${TEST_DIR}

# Write file
cat > "${TEST_DIR}/${FILE_NAME}" << 'EOF'
{GENERATED_YAML}
EOF

Step 7: Confirm

Test plan generated: {TEST_DIR}/{FILE_NAME}

Summary:
- Endpoints: {N} endpoints identified
- Test cases: {M} test cases generated
  - Happy path: {X}
  - Validation: {Y}
  - Auth: {Z}
  - Edge cases: {W}
- SQS assertions: {K}

To run tests:
/start-qa {FILE_NAME}

To edit:
Open {TEST_DIR}/{FILE_NAME} and customize as needed.

Configuration Reference

SettingDefaultDescription
qa.apiBaseUrlhttp://localhost:3000Default API base URL
qa.testPlansDirtests/qaDirectory for test plans
qa.timeout10Default timeout (seconds)
qa.sqsQueueUrl-Default SQS queue URL

Examples

From Ticket ID

/plan-qa PROJ-123

Fetches ticket from issue tracker and generates test plan.

From Ticket with SQS

/plan-qa PROJ-456 --sqs-env SQS_EVENTS_QUEUE

Generates test plan with SQS event assertions.

From Description

/plan-qa Test the new /v2/risk endpoint returns risk scores 0-100

Generates test plan from description without ticket.

With Custom URL

/plan-qa PROJ-789 --url https://staging-api.example.com

Generates test plan targeting staging environment.

Test Case Templates

REST API Endpoint

- id: TC-001
  name: "GET users returns paginated list"
  endpoint: /api/v1/users
  method: GET
  params:
    page: 1
    limit: 10
  expected:
    status: 200
    body:
      items: [] # Array of users
      pagination:
        page: 1
        limit: 10

Create Resource

- id: TC-002
  name: "POST creates new user"
  endpoint: /api/v1/users
  method: POST
  body:
    email: "test@example.com"
    name: "Test User"
  expected:
    status: 201
    body:
      id: "*" # Any non-null value
      email: "test@example.com"

Error Handling

- id: TC-003
  name: "Duplicate email returns 409"
  endpoint: /api/v1/users
  method: POST
  body:
    email: "existing@example.com"
  expected:
    status: 409
    body:
      error: "Email already exists"

Output Location

Test files are saved to: {qa.testPlansDir}/{slug}-test.yaml

Where {slug} is:

  • Ticket ID lowercase (e.g., proj-123-test.yaml)
  • Or kebab-case from description (e.g., risk-endpoint-test.yaml)

Signals

GitHub stars
30
Forks
2
Last commit
Aug 2026
Advanced
Catalog kind
skill
Gateway key
plan-qa-rlajous
Source
github.com/rlajous/claude-code-commands