From 42f536b351b5f8b24bb78285f818a2e14ccba6d2 Mon Sep 17 00:00:00 2001 From: Snider Date: Sun, 1 Feb 2026 19:03:32 +0000 Subject: [PATCH] docs: add /core:qa iterative QA fix loop skill task MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Skill that runs QA and fixes issues iteratively: - Detects Go/PHP project type - Runs core go qa / core php qa - Parses output, extracts actionable issues - Fixes in priority order: fmt → lint → test - Re-runs QA until all checks pass - Stop hook prevents stopping until QA clean Hooks: - PostToolUse: qa-filter (suppress verbose, show failures only) - Stop: qa-verify (block if QA still failing) Co-Authored-By: Claude Opus 4.5 --- claude/issues/019-skill-qa-loop.md | 301 +++++++++++++++++++++++++++++ 1 file changed, 301 insertions(+) create mode 100644 claude/issues/019-skill-qa-loop.md diff --git a/claude/issues/019-skill-qa-loop.md b/claude/issues/019-skill-qa-loop.md new file mode 100644 index 0000000..11d2fd5 --- /dev/null +++ b/claude/issues/019-skill-qa-loop.md @@ -0,0 +1,301 @@ +# feat(skill): Add /core:qa iterative QA fix loop + +## Summary + +Add a `/core:qa` skill that runs the full QA pipeline, extracts actionable issues, and guides Claude through fixing them one by one until everything passes. + +## Use Case + +``` +/core:qa +``` + +Runs `core go qa` or `core php qa`, parses output, and iteratively fixes issues: + +``` +[QA] Running core go qa... +[QA] Found 3 issues: + +1. ✗ lint: undefined: ErrNotFound (pkg/api/handler.go:42) +2. ✗ test: TestCreateUser failed - expected 200, got 500 +3. ✗ fmt: pkg/api/handler.go needs formatting + +Fixing issue 1/3: undefined: ErrNotFound +→ Reading pkg/api/handler.go... +→ Adding missing import... +→ Fixed. + +Fixing issue 2/3: TestCreateUser failed +→ Reading test output... +→ The handler returns 500 because ErrNotFound wasn't defined +→ Already fixed by issue 1, re-running test... +→ Passed. + +Fixing issue 3/3: formatting +→ Running core go fmt... +→ Fixed. + +[QA] Re-running full QA... +[QA] ✓ All checks passed! +``` + +## Skill Definition + +`claude/commands/qa.md`: + +```markdown +--- +name: qa +description: Run QA checks and fix all issues iteratively +hooks: + PostToolUse: + - matcher: "Bash" + hooks: + - type: command + command: "core ai qa-filter" + Stop: + - hooks: + - type: command + command: "core ai qa-verify" + once: true +--- + +# QA Fix Loop + +Run the full QA pipeline and fix all issues. + +## Detection + +First, detect the project type: +- If `go.mod` exists → Go project → `core go qa` +- If `composer.json` exists → PHP project → `core php qa` +- If both exist → ask user or check current directory + +## Process + +1. **Run QA**: Execute `core go qa` or `core php qa` +2. **Parse issues**: Extract failures from output (see format below) +3. **Fix each issue**: Address one at a time, simplest first +4. **Re-verify**: After fixes, re-run QA +5. **Repeat**: Until all checks pass +6. **Report**: Summary of what was fixed + +## Issue Priority + +Fix in this order (fastest feedback first): +1. **fmt** - formatting issues (auto-fix with `core go fmt`) +2. **lint** - static analysis (usually quick fixes) +3. **test** - failing tests (may need more investigation) +4. **build** - compilation errors (fix before tests can run) + +## Output Parsing + +### Go QA Output +``` +=== FMT === +FAIL: pkg/api/handler.go needs formatting + +=== LINT === +pkg/api/handler.go:42:15: undefined: ErrNotFound (typecheck) +pkg/api/handler.go:87:2: ineffectual assignment to err (ineffassign) + +=== TEST === +--- FAIL: TestCreateUser (0.02s) + handler_test.go:45: expected 200, got 500 +FAIL + +=== RESULT === +fmt: FAIL +lint: FAIL (2 issues) +test: FAIL (1 failed) +``` + +### PHP QA Output +``` +=== PINT === +FAIL: 2 files need formatting + +=== STAN === +src/Http/Controller.php:42 - Undefined variable $user + +=== TEST === +✗ CreateUserTest::testSuccess + Expected status 200, got 500 + +=== RESULT === +pint: FAIL +stan: FAIL (1 error) +test: FAIL (1 failed) +``` + +## Fixing Strategy + +**Formatting (fmt/pint):** +- Just run `core go fmt` or `core php fmt` +- No code reading needed + +**Lint errors:** +- Read the specific file:line +- Understand the error type +- Make minimal fix + +**Test failures:** +- Read the test file to understand expectation +- Read the implementation +- Fix the root cause (not just the symptom) + +**Build errors:** +- Usually missing imports or typos +- Fix before attempting other checks + +## Stop Condition + +Only stop when: +- All QA checks pass, OR +- User explicitly cancels, OR +- Same error repeats 3 times (stuck) +``` + +## Hook Implementations + +### core ai qa-filter + +Filters QA output to show only actionable issues: + +```bash +#!/bin/bash +INPUT=$(cat) +COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty') +OUTPUT=$(echo "$INPUT" | jq -r '.tool_response.output // empty') + +# Only process QA commands +case "$COMMAND" in + "core go qa"*|"core php qa"*|"core go test"*|"core php test"*|"core go lint"*|"core php stan"*) + ;; + *) + echo "$INPUT" + exit 0 + ;; +esac + +# Extract failures only +FAILURES=$(echo "$OUTPUT" | grep -E "^(FAIL|---\s*FAIL|✗|ERROR|undefined:|error:)" | head -20) +SUMMARY=$(echo "$OUTPUT" | grep -E "^(fmt:|lint:|test:|pint:|stan:|RESULT)" | tail -5) + +if [ -z "$FAILURES" ]; then + # All passed - show brief summary + echo '{"suppressOutput": true, "hookSpecificOutput": {"hookEventName": "PostToolUse", "additionalContext": "✓ QA passed"}}' +else + # Show only failures + cat << EOF +{ + "suppressOutput": true, + "hookSpecificOutput": { + "hookEventName": "PostToolUse", + "additionalContext": "## QA Issues\n\n\`\`\`\n$FAILURES\n\`\`\`\n\n**Summary:** $SUMMARY" + } +} +EOF +fi +``` + +### core ai qa-verify + +Stop hook that ensures QA passes before stopping: + +```bash +#!/bin/bash +INPUT=$(cat) +STOP_ACTIVE=$(echo "$INPUT" | jq -r '.stop_hook_active') + +if [ "$STOP_ACTIVE" = "true" ]; then + exit 0 +fi + +# Detect project type and run QA +if [ -f "go.mod" ]; then + RESULT=$(core go qa 2>&1) +elif [ -f "composer.json" ]; then + RESULT=$(core php qa 2>&1) +else + exit 0 +fi + +# Check if passed +if echo "$RESULT" | grep -q "FAIL\|ERROR\|✗"; then + ISSUES=$(echo "$RESULT" | grep -E "^(FAIL|ERROR|✗|undefined:)" | head -5) + cat << EOF +{ + "decision": "block", + "reason": "QA still has issues:\n$ISSUES\n\nPlease fix these before stopping." +} +EOF +else + exit 0 +fi +``` + +## Example Session + +``` +> /core:qa + +Detecting project type... Found go.mod → Go project + +Running: core go qa + +## QA Issues + +``` +pkg/api/handler.go:42:15: undefined: ErrNotFound +--- FAIL: TestCreateUser (0.02s) +``` + +**Summary:** lint: FAIL (1) | test: FAIL (1) + +--- + +Fixing lint issue: undefined ErrNotFound + +Reading pkg/api/handler.go... +The error variable is used but not defined. Adding: + +var ErrNotFound = errors.New("not found") + +Running: core go qa + +## QA Issues + +``` +--- FAIL: TestCreateUser (0.02s) + expected 200, got 404 +``` + +**Summary:** lint: PASS | test: FAIL (1) + +--- + +Fixing test issue: expected 200, got 404 + +Reading the test... it expects success but handler now returns 404. +The test data doesn't have a matching record. Fixing test setup... + +Running: core go qa + +✓ QA passed + +--- + +**Summary:** +- Fixed: undefined ErrNotFound (added error variable) +- Fixed: TestCreateUser (corrected test setup) +- 2 issues resolved, all checks passing +``` + +## Future CLI Integration + +```bash +core ai qa # Run QA fix loop +core ai qa --check-only # Just report, don't fix +core ai qa --fmt-only # Only fix formatting +```