Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .claude/agents/plan-integration-test.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ A complete test template with:

### Step 1: Read the QA YAML

Find the TC in `packages/rangelink-vscode-extension/qa/qa-test-cases-v1.1.0*.yaml` (use the latest file). Extract:
Find the TC in `packages/rangelink-vscode-extension/qa/qa-test-cases-unreleased.yaml` (or `qa-test-cases-v<version>.yaml` for a released version — use the latest file). Extract:

- `scenario` — what the test verifies
- `preconditions` — what setup is needed
Expand Down
26 changes: 19 additions & 7 deletions .claude/skills/qa-suggest/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,10 @@ Read packages/rangelink-vscode-extension/package.json

Extract:

- `nextTargetVersion` — the upcoming release version (e.g., `1.1.0`)
- `nextTargetVersion` — the upcoming release version (`"Unreleased"` during trunk-based development, or a SemVer like `"1.1.0"` once locked in)
- `version` — the last published version (e.g., `1.0.0`)

**If `nextTargetVersion` is not set**, STOP: "Set `nextTargetVersion` in `packages/rangelink-vscode-extension/package.json` before running `/qa-suggest`."
**If `nextTargetVersion` is not set**, STOP: "Set `nextTargetVersion` in `packages/rangelink-vscode-extension/package.json` (e.g., `"Unreleased"`) before running `/qa-suggest`."

## Step 2: Locate QA YAMLs

Expand All @@ -34,10 +34,10 @@ Find the current cycle's YAML and the previous version's YAML:
Glob(pattern="packages/rangelink-vscode-extension/qa/qa-test-cases-*.yaml")
```

- **Current YAML**: the file matching `qa-test-cases-v<nextTargetVersion>.yaml` (e.g., `qa-test-cases-v1.1.0.yaml` — a single file, no suffix)
- **Previous YAML**: the most recent file that does NOT contain `v<nextTargetVersion>` — this is the baseline for diffing
- **Current YAML**: `qa-test-cases-unreleased.yaml` during trunk-based development, or `qa-test-cases-v<version>.yaml` once the version is locked in
- **Previous YAML**: the most recent released version's YAML (e.g., `qa-test-cases-v1.0.0.yaml`) — this is the baseline for diffing

**If the current YAML doesn't exist**, STOP: "No QA YAML found for v`<nextTargetVersion>`. Run `pnpm generate:qa-test-plan` first."
**If the current YAML doesn't exist**, STOP: "No QA YAML found. Run `pnpm generate:qa-test-plan` first."

Read both YAML files in parallel.

Expand Down Expand Up @@ -144,23 +144,35 @@ Create a scratchpad file for the report. Use the `/scratchpad` conventions:

1. Determine the issue context from the current git branch (e.g., `issues/382` → issue ID `382`)
2. Find the next available sequence number in `.claude-work/issues/<ID>/scratchpads/`
3. Write the scratchpad to `.claude-work/issues/<ID>/scratchpads/NNNN-qa-suggest-v<nextTargetVersion>.txt`
3. Write the scratchpad. Choose the filename based on `nextTargetVersion`:
- If `nextTargetVersion` is `"Unreleased"`: `.claude-work/issues/<ID>/scratchpads/NNNN-qa-suggest.txt`
- If `nextTargetVersion` is a locked SemVer (e.g., `"2.0.0"`): `.claude-work/issues/<ID>/scratchpads/NNNN-qa-suggest-v<nextTargetVersion>.txt`

If no issue context can be determined, use `.claude-work/scratchpads/` instead.

The scratchpad should contain these sections in order:

### Header

If `nextTargetVersion` is `"Unreleased"`, use this header:

```text
# QA Suggest — v<version> → Unreleased
```

If `nextTargetVersion` is a locked SemVer (e.g., `"2.0.0"`), use this header instead:

```text
# QA Suggest — v<version> → v<nextTargetVersion>
```

Then continue with the shared body:

## What to do next

1. Review the suggested TCs below — edit descriptions, remove irrelevant ones
2. Copy the YAML block at the bottom into the QA file at the appropriate section
3. Verify the IDs don't collide with existing entries
```

### Change Summary

Expand Down
8 changes: 4 additions & 4 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -605,11 +605,11 @@

<rule id="QA002" priority="critical">
<title>QA YAML is a single file per release cycle</title>
<do>Edit `qa-test-cases-v<version>.yaml` in place during the current release cycle — add TCs, update automated status, adjust preconditions</do>
<do>When moving to a new version (e.g., v1.1.0 → v1.2.0), use `pnpm generate:qa-test-plan:vscode-extension` to create the new file from the previous one</do>
<never>Edit a YAML file from a previous release cycle (e.g., don't touch v1.0.0.yaml after v1.1.0 work starts)</never>
<do>During trunk-based development, edit `qa-test-cases-unreleased.yaml` — add TCs, update automated status, adjust preconditions. The version is deferred until `finalize-release` locks it in.</do>
<do>When starting a new release cycle, use `pnpm generate:qa-test-plan:vscode-extension` to create a fresh `qa-test-cases-unreleased.yaml` carrying forward all TCs from the previous version's YAML</do>
<never>Edit a YAML file from a past release (e.g., don't touch v1.0.0.yaml after it has shipped)</never>
<exception>Fixing typos or updating `automated` status (`true`/`assisted`/`false`) in the current file is always allowed</exception>
<rationale>Each version has exactly one QA file. Suffix files (v1.1.0-001.yaml, -002.yaml, etc.) were journal snapshots from an older workflow and are no longer created.</rationale>
<rationale>Each version has exactly one QA file. The `Unreleased` placeholder defers version naming until release time so the filename need not change mid-cycle.</rationale>
</rule>

<rule id="QA003" priority="critical">
Expand Down
8 changes: 4 additions & 4 deletions packages/rangelink-vscode-extension/TESTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ All `test:release*` commands accept `--label <tag>` (include TCs with QA YAML la
```mermaid
flowchart TD
Z[generate:release-testing-instructions] -.->|generates guide| A
A[Set nextTargetVersion] --> B[generate:qa-test-plan]
A[nextTargetVersion: Unreleased] --> B[generate:qa-test-plan]
B --> C[/qa-suggest in Claude Code/]
C --> D[Review + append new TCs]
D --> E[Commit YAML]
Expand Down Expand Up @@ -275,12 +275,12 @@ The QA test plan is a version-scoped YAML file that tracks both automated and ma
### File location and naming

```text
qa/qa-test-cases-v<version>.yaml
qa/qa-test-cases-unreleased.yaml
```

Example: `qa/qa-test-cases-v1.1.0.yaml`
During trunk-based development the file is `qa/qa-test-cases-unreleased.yaml`. At release time `finalize-release` renames it to `qa/qa-test-cases-v<version>.yaml`.

The version is the target release (`nextTargetVersion` from `package.json`). It is embedded in the filename and parsed automatically by the `generate-qa-issue` script — no extra flags needed. One file per release — Git tracks history across versions.
The filename mirrors `nextTargetVersion` from `package.json` (`"Unreleased"` during development). It is parsed automatically by the `generate-qa-issue` script — no extra flags needed. One file per release — Git tracks history across versions.

New QA YAML files are created by `pnpm generate:qa-test-plan`. The script carries forward all TCs from the most recent YAML, resets `status:` fields to `pending`, and preserves `automated:` flags.

Expand Down
2 changes: 1 addition & 1 deletion packages/rangelink-vscode-extension/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -982,6 +982,6 @@
"vscode": "^1.49.0"
},
"icon": "icon.png",
"nextTargetVersion": "1.1.0",
"nextTargetVersion": "Unreleased",
"pricing": "Free"
}
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
# RangeLink QA Test Cases — v1.0.0 → v1.1.0
# RangeLink QA Test Cases — v1.0.0 → Unreleased
#
# Scope: Changes accumulated between the vscode-extension-v1.0.0 release tag and the current
# main branch tip, targeting v1.1.0.
# main branch tip, targeting Unreleased.
#
# Source of truth for this QA cycle. Run `pnpm generate:qa-issue -- qa/qa-test-cases-v1.1.0-003.yaml`
# Source of truth for this QA cycle. Run `pnpm generate:qa-issue -- qa/qa-test-cases-unreleased.yaml`
# to create the corresponding GitHub issue tracker (parent issue + per-section sub-issues).
#
# Schema:
# id: Unique test case identifier (<feature-slug>-NNN)
# feature: Feature area
# scenario: One-line description of the specific scenario being tested
# scenario: One-line description of what is tested
# labels: Optional tags (e.g., cursor, ubuntu, requires-extensions)
# preconditions: Required setup steps — only on `automated: false` entries
# steps: Ordered test actions — only on `automated: false` entries
Expand All @@ -24,10 +24,6 @@
# automated (even assisted):
# platform-specific — requires Win/Linux; CI runs on macOS
# ide-specific — requires Cursor IDE; not in VS Code host
#
# For `automated: true` and `automated: assisted` entries the integration test in
# src/__integration-tests__/suite/ is the canonical source of setup and actions —
# `preconditions:` and `steps:` are omitted to prevent duplication and drift.

test_cases:
# ---------------------------------------------------------------------------
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
set -euo pipefail

# Usage: ./scripts/generate-qa-issue.sh [--dry-run] [--local] [yaml-file]
# Example: ./scripts/generate-qa-issue.sh qa/qa-test-cases-v1.1.0.yaml
# Example: ./scripts/generate-qa-issue.sh qa/qa-test-cases-unreleased.yaml
# ./scripts/generate-qa-issue.sh --local
#
# Creates a single GitHub issue with checkboxes grouped by TC ID prefix (feature domain).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ set -euo pipefail
# Reads nextTargetVersion from package.json to name the output file.
# Reads version (last published) to document the scope in the header.
#
# Filename: qa-test-cases-v<version>.yaml
# If the file already exists, exits successfully (no-op).
# Filename: qa-test-cases-v<version>.yaml, or qa-test-cases-unreleased.yaml when nextTargetVersion is "Unreleased".
# Always regenerates: header is freshly emitted, body is carried forward from the highest-sorted existing yaml.
#
# Requires: jq

Expand All @@ -32,47 +32,60 @@ if [[ -z "$PUBLISHED_VERSION" ]]; then
exit 1
fi

COMMIT=$(git -C "$REPO_ROOT" rev-parse --short HEAD)
BASE_NAME="qa-test-cases-v${NEXT_VERSION}"
# Version-aware filename + label. "Unreleased" is the placeholder used during
# trunk-based development before finalize-release locks in a SemVer.
if [[ "$NEXT_VERSION" == "Unreleased" ]]; then
NEXT_LABEL="Unreleased"
BASE_NAME="qa-test-cases-unreleased"
else
NEXT_LABEL="v${NEXT_VERSION}"
BASE_NAME="qa-test-cases-v${NEXT_VERSION}"
fi
OUTPUT_FILE="$QA_DIR/${BASE_NAME}.yaml"

Comment thread
coderabbitai[bot] marked this conversation as resolved.
# Prefer the existing target file as the carry-forward source so in-progress
# unreleased edits aren't clobbered by a versioned file that would win the
# ASCII sort ('u' < 'v').
if [[ -f "$OUTPUT_FILE" ]]; then
echo "$OUTPUT_FILE already exists — nothing to generate"
exit 0
PREVIOUS_YAML="$OUTPUT_FILE"
else
# Pick the highest-SemVer versioned YAML (or unreleased as last resort).
# Sort by MAJOR, MINOR, PATCH numerically so v1.10.0 > v1.9.0. Unsuffixed
# files get -000 for tie-breaking so the primary file sorts before backups.
PREVIOUS_YAML=$(
for f in "$QA_DIR"/qa-test-cases-*.yaml; do
[[ -e "$f" ]] || continue
name=$(basename "$f")
base="${name%.yaml}"
if [[ "$base" =~ ^qa-test-cases-v([0-9]+)\.([0-9]+)\.([0-9]+)(-[0-9]{3})?$ ]]; then
major="${BASH_REMATCH[1]}"
minor="${BASH_REMATCH[2]}"
patch="${BASH_REMATCH[3]}"
suff="${BASH_REMATCH[4]:-"-000"}"
printf '%d\t%d\t%d\t%s\t%s\n' "$major" "$minor" "$patch" "$suff" "$f"
elif [[ "$base" == *unreleased* ]]; then
printf '0\t0\t0\t-000\t%s\n' "$f"
fi
done | sort -t$'\t' -k1,1n -k2,2n -k3,3n -k4,4 | tail -1 | cut -f5
)
if [[ -z "$PREVIOUS_YAML" ]]; then
echo "Error: no previous QA YAML found in $QA_DIR" >&2
exit 1
fi
fi

# Suffix sort fix: unsuffixed files (v1.1.0.yaml) sort AFTER suffixed files
# (v1.1.0-001.yaml) because '.' > '-' in ASCII. Normalize by appending -000
# to unsuffixed names for sorting purposes, then pick the highest.
PREVIOUS_YAML=$(
for f in "$QA_DIR"/qa-test-cases-*.yaml; do
[[ -e "$f" ]] || continue
name=$(basename "$f")
base="${name%.yaml}"
if [[ "$base" =~ -[0-9]{3}$ ]]; then
printf '%s\t%s\n' "$base" "$f"
else
printf '%s-000\t%s\n' "$base" "$f"
fi
done | sort -t$'\t' -k1,1 | tail -1 | cut -f2
)
if [[ -z "$PREVIOUS_YAML" ]]; then
echo "Error: no previous QA YAML found in $QA_DIR" >&2
exit 1
fi

HEADER="# RangeLink QA Test Cases — v${PUBLISHED_VERSION} → v${NEXT_VERSION}
HEADER="# RangeLink QA Test Cases — v${PUBLISHED_VERSION} → ${NEXT_LABEL}
#
# Scope: Changes accumulated between the vscode-extension-v${PUBLISHED_VERSION} release tag and the current
# main branch tip, targeting v${NEXT_VERSION}. Created at commit ${COMMIT}.
# main branch tip, targeting ${NEXT_LABEL}.
#
# Source of truth for this QA cycle. Run \`pnpm generate:qa-issue -- qa/$(basename "$OUTPUT_FILE")\`
# to create the corresponding GitHub issue tracker (parent issue + per-section sub-issues).
#
# Schema:
# id: Unique test case identifier (<feature-slug>-NNN)
# feature: Feature area / CHANGELOG section
# scenario: One-line description of the specific scenario being tested
# feature: Feature area
# scenario: One-line description of what is tested
# labels: Optional tags (e.g., cursor, ubuntu, requires-extensions)
# preconditions: Required setup steps — only on \`automated: false\` entries
# steps: Ordered test actions — only on \`automated: false\` entries
Expand All @@ -86,13 +99,9 @@ HEADER="# RangeLink QA Test Cases — v${PUBLISHED_VERSION} → v${NEXT_VERSION}
# non_automatable_reason: Required when \`automated: false\`. Why this TC cannot be
# automated (even assisted):
# platform-specific — requires Win/Linux; CI runs on macOS
# ide-specific — requires Cursor IDE; not in VS Code host
#
# For \`automated: true\` and \`automated: assisted\` entries the integration test in
# src/__integration-tests__/suite/ is the canonical source of setup and actions —
# \`preconditions:\` and \`steps:\` are omitted to prevent duplication and drift."
# ide-specific — requires Cursor IDE; not in VS Code host"

BODY=$(sed '1,/^test_cases:/{ /^#/d; }' "$PREVIOUS_YAML")
BODY=$(sed -n '/^test_cases:/,$p' "$PREVIOUS_YAML")

{
echo "$HEADER"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,21 @@ elif ! gh auth status &>/dev/null; then
WARNINGS+=" ${YELLOW}Warning: gh CLI not authenticated — Phase 2 (GitHub QA Issues) requires auth${NC}\n"
fi

echo -e "${GREEN}Generating release testing instructions for v${NEXT_VERSION}${NC}"
# Version-aware filename + label. "Unreleased" is the placeholder used during
# trunk-based development before finalize-release locks in a SemVer.
if [[ "$NEXT_VERSION" == "Unreleased" ]]; then
NEXT_LABEL="Unreleased"
BASE_NAME="release-testing-instructions-unreleased"
QA_YAML_FILENAME="qa-test-cases-unreleased.yaml"
QA_CHECKLIST_SLUG="unreleased"
else
NEXT_LABEL="v${NEXT_VERSION}"
BASE_NAME="release-testing-instructions-v${NEXT_VERSION}"
QA_YAML_FILENAME="qa-test-cases-v${NEXT_VERSION}.yaml"
QA_CHECKLIST_SLUG="v${NEXT_VERSION}"
fi

echo -e "${GREEN}Generating release testing instructions for ${NEXT_LABEL}${NC}"

if [[ -n "$WARNINGS" ]]; then
echo -e "\n${YELLOW}Prerequisites warnings (non-blocking):${NC}"
Expand All @@ -51,7 +65,6 @@ fi

# --- Output file ---

BASE_NAME="release-testing-instructions-v${NEXT_VERSION}"
OUTPUT_FILE="$QA_DIR/${BASE_NAME}.md"

if [[ -f "$OUTPUT_FILE" ]]; then
Expand All @@ -62,10 +75,10 @@ fi
# --- Generate markdown ---

cat > "$OUTPUT_FILE" <<EOF
# Release Testing Instructions: RangeLink VS Code Extension v${NEXT_VERSION}
# Release Testing Instructions: RangeLink VS Code Extension ${NEXT_LABEL}

**Generated:** $(date -u +"%Y-%m-%d %H:%M:%S UTC")
**Scope:** Changes from v${PUBLISHED_VERSION} → v${NEXT_VERSION}
**Scope:** Changes from v${PUBLISHED_VERSION} → ${NEXT_LABEL}

This file contains version-specific, copy-paste ready commands for the full release testing lifecycle.
Work through each phase in order — later phases depend on earlier ones completing successfully.
Expand Down Expand Up @@ -101,13 +114,13 @@ gh auth status

## Phase 1: Generate QA Test Plan

Create or carry forward the QA test plan YAML for v${NEXT_VERSION}.
Create or carry forward the QA test plan YAML for ${NEXT_LABEL}.

\`\`\`bash
pnpm generate:qa-test-plan:vscode-extension
\`\`\`

This creates \`qa/qa-test-cases-v${NEXT_VERSION}.yaml\` by carrying forward all TCs from the previous plan with statuses reset to pending.
This creates \`qa/${QA_YAML_FILENAME}\` by carrying forward all TCs from the previous plan with statuses reset to pending.

### AI-powered gap detection

Expand All @@ -120,8 +133,8 @@ Run the \`/qa-suggest\` skill in Claude Code to identify features in the CHANGEL
3. Commit the YAML:

\`\`\`bash
git add packages/rangelink-vscode-extension/qa/qa-test-cases-v${NEXT_VERSION}.yaml
git commit -m "Add QA test plan for v${NEXT_VERSION}"
git add packages/rangelink-vscode-extension/qa/${QA_YAML_FILENAME}
git commit -m "Add QA test plan for ${NEXT_LABEL}"
\`\`\`

---
Expand Down Expand Up @@ -178,7 +191,7 @@ Generate a local QA checklist:
pnpm generate:qa-issue:vscode-extension --local
\`\`\`

The generated checklist is at \`qa/output/qa-checklist-v${NEXT_VERSION}-<timestamp>.md\`.
The generated checklist is at \`qa/output/qa-checklist-${QA_CHECKLIST_SLUG}-<timestamp>.md\`.
Each TC is annotated with its automation status and reason. Cursor and Ubuntu TCs are grouped into their own sections with the required run commands inline.

---
Expand Down Expand Up @@ -212,7 +225,7 @@ When all checks pass, generate the publishing instructions:
pnpm generate:publish-instructions:vscode-extension
\`\`\`

This validates the release environment and creates a version-specific publishing guide at \`publishing-instructions/publish-vscode-extension-v${NEXT_VERSION}.md\`.
This validates the release environment and creates a version-specific publishing guide at \`publishing-instructions/publish-vscode-extension-${NEXT_LABEL}.md\`.
EOF

# Format with prettier
Expand Down
Loading
Loading