# Phase 01.9: Detect Previous QA Failures [MANDATORY]

**STORY-658 cross-reference:** When `/dev STORY-XXX --fix` is invoked against a completed (phase 10) story, the SKILL.md Entry Dispatcher (see `01.0-entry-dispatcher.md`) opens the remediation cycle BEFORE Phase 01 ever runs — the dispatcher emits `next_action=phase-cycle-start` and executes the same `phase-cycle-start` CLI invocation that Step 01.9.6 below documents. Phase 01.9 (this file) remains the canonical entry point for **mid-workflow** QA-failure detection, where the workflow is paused at phase < 10 and a stale gaps.json or qa-recommendations.md surfaces during preflight steps. The two paths are complementary: dispatcher handles "completed story → restart in remediation"; Phase 01.9 handles "in-progress story → pivot to remediation."

**Check if story has failed QA due to deferral or other issues:**

```
# Search for QA reports for this story
Glob(pattern="devforgeai/qa/reports/${STORY_ID}-qa-report*.md")

IF reports found:
    # Read most recent report
    reports_sorted = sort_by_timestamp(reports)
    latest_report = reports_sorted[0]

    Read(file_path=latest_report)

    # Parse QA status
    IF report contains "Status: FAILED":
        # Extract failure type
        IF report contains "Deferral Validation FAILED":
            Display: "⚠ Previous QA failed due to deferral issues"
            Display: "  QA Report: {latest_report}"
            $QA_DEFERRAL_FAILURE = true
            $QA_FAILURE_REPORT = latest_report

        ELIF report contains "Coverage Below Threshold":
            Display: "⚠ Previous QA failed due to coverage issues"
            Display: "  Focus: Increase test coverage"
            $QA_COVERAGE_FAILURE = true

        ELIF report contains "Anti-Pattern Violations":
            Display: "⚠ Previous QA failed due to anti-patterns"
            Display: "  Focus: Refactor to remove violations"
            $QA_ANTIPATTERN_FAILURE = true

        ELSE:
            Display: "⚠ Previous QA failed (review report for details)"
            Display: "  Report: {latest_report}"
            $QA_GENERIC_FAILURE = true

    ELIF report contains "Status: PASSED":
        Display: "Note: QA already passed for this story"
        Display: "  Proceeding with development (may be enhancement or bug fix)"
        $QA_PASSED = true

ELSE:
    # No QA reports found - first development iteration
    Display: "✓ First development iteration (no previous QA attempts)"
    $QA_FIRST_ITERATION = true
```

**Token cost:** ~1,500 tokens

**Use in subsequent phases:**
- If `$QA_DEFERRAL_FAILURE == true` → Invoke "Handling QA Deferral Failures" workflow
- If `$QA_COVERAGE_FAILURE == true` → Focus on test coverage in Phase 02
- If `$QA_ANTIPATTERN_FAILURE == true` → Extra validation in Phase 04 (Refactor)

---

# Phase 01.9.5: Load Structured Gap Data (gaps.json) [IF QA FAILED]

**Purpose:** Parse machine-readable gap data for targeted remediation workflow.

**When to execute:** After Phase 01.9 detects `$QA_COVERAGE_FAILURE`, `$QA_ANTIPATTERN_FAILURE`, or `$QA_DEFERRAL_FAILURE`

```
# Check if structured gap export exists
gaps_file = "devforgeai/qa/reports/${STORY_ID}-gaps.json"

Glob(pattern=gaps_file)

IF gaps_file EXISTS:
    Display: ""
    Display: "╔═══════════════════════════════════════════════════════════════╗"
    Display: "║  📊 STRUCTURED GAP DATA DETECTED                              ║"
    Display: "╠═══════════════════════════════════════════════════════════════╣"
    Display: "║                                                               ║"
    Display: "║  QA-Dev Integration: ACTIVE                                   ║"
    Display: "║  Gap file: {gaps_file}                                        ║"
    Display: "║                                                               ║"
    Display: "║  Development will enter REMEDIATION MODE:                     ║"
    Display: "║  • Targeted test generation for specific gaps                 ║"
    Display: "║  • Focus on coverage failures by file                         ║"
    Display: "║  • Suggested tests provided                                   ║"
    Display: "║                                                               ║"
    Display: "╚═══════════════════════════════════════════════════════════════╝"
    Display: ""

    # Read and parse gaps.json
    Read(file_path=gaps_file)
    gaps_data = parse_json(file_content)

    # Build arrays for Phase 02-06 consumption
    $QA_COVERAGE_GAPS = gaps_data.coverage_gaps
    $QA_ANTIPATTERN_GAPS = gaps_data.anti_pattern_violations
    $QA_DEFERRAL_GAPS = gaps_data.deferral_issues

    # Display summary
    IF $QA_COVERAGE_GAPS.count > 0:
        Display: "📉 Coverage Gaps: {count} files below threshold"

    IF $QA_ANTIPATTERN_GAPS.count > 0:
        Display: "⚠️  Anti-Pattern Violations: {count} issues to resolve"

    IF $QA_DEFERRAL_GAPS.count > 0:
        Display: "📋 Deferral Issues: {count} items need resolution"

    $REMEDIATION_MODE = true
    Display: "✅ Remediation mode enabled"

ELSE:
    Display: "ℹ️  No structured gap data (gaps.json) found"
    Display: "   Using legacy QA report parsing for failure context"
    $REMEDIATION_MODE = false
```

**Token cost:** ~800 tokens

**Variables set for Phases 02-06:**
- `$QA_COVERAGE_GAPS` - Array of coverage gap objects
- `$QA_ANTIPATTERN_GAPS` - Array of anti-pattern violations
- `$QA_DEFERRAL_GAPS` - Array of deferral issues
- `$REMEDIATION_MODE` - Boolean flag for targeted workflow

---

# Phase 01.9.6: Load QA Recommendations Status (qa-recommendations.md) [IF QA RECOMMENDATIONS EXIST]

**Purpose:** Detect the Sprint-1/2 qa-recommendations.md artifact and, when open recommendations exist, open a remediation cycle in phase-state.json via the Sprint-3 `phase-cycle-start` CLI.

**When to execute:** After Phase 01.9.5. Takes priority over legacy gaps.json detection when both are present.

**Precondition:** `dev-preflight` CLI (Phase 0) has already surfaced the `qa_recommendations` object in its JSON output. The `$QA_RECOMMENDATIONS_STATUS` variable below is the parsed JSON from that output.

```
# Read the qa_recommendations sub-object surfaced by dev-preflight
# (already parsed in Phase 0; available as $QA_RECOMMENDATIONS_STATUS)

IF $QA_RECOMMENDATIONS_STATUS is not null AND $QA_RECOMMENDATIONS_STATUS.open_count > 0:
    Display: ""
    Display: "╔═══════════════════════════════════════════════════════════════╗"
    Display: "║  📋 QA RECOMMENDATIONS DETECTED                               ║"
    Display: "╠═══════════════════════════════════════════════════════════════╣"
    Display: "║                                                               ║"
    Display: "║  Recommendations file: {recommendations_file}                 ║"
    Display: "║  Open recommendations: {open_count}                           ║"
    Display: "║  Blocking: {blocking_count} (has_blocking={has_blocking})     ║"
    Display: "║  By severity: C={CRITICAL} H={HIGH} M={MEDIUM} L={LOW}        ║"
    Display: "║                                                               ║"
    Display: "║  Development will enter REMEDIATION MODE (qa-recommendations) ║"
    Display: "║                                                               ║"
    Display: "╚═══════════════════════════════════════════════════════════════╝"
    Display: ""

    $REMEDIATION_MODE = true
    $REMEDIATION_SOURCE = "qa-recommendations"
    $OPEN_REC_IDS = $QA_RECOMMENDATIONS_STATUS.open_rec_ids

    # Open a new cycle in phase-state.json via Sprint-3 CLI.
    # Auto-assigns cycle number; rejects if a prior cycle is still open.
    EXECUTE: devforgeai-validate phase-cycle-start \
        --story-id=${STORY_ID} \
        --source=qa-recommendations \
        --trigger="/dev ${STORY_ID} --fix (01.9.6)" \
        --rec-ids="{comma-joined $OPEN_REC_IDS}" \
        --target-phase=02 \
        --project-root=${PROJECT_ROOT} \
        --format=json

    VERIFY: Exit 0 = cycle opened. Capture payload.cycle as $CURRENT_CYCLE.
    IF exit code == 1 with error containing "still open":
        HALT — "A prior remediation cycle is still open for ${STORY_ID}. Close it (phase-cycle-close) or reset phase-state before retrying."
    IF exit code == 1 with any other error: HALT with the error message verbatim.
    IF exit code == 2: HALT — "IO error opening cycle: {error}".

    Display: "✅ Cycle ${CURRENT_CYCLE} opened (source=qa-recommendations)"
    Display: "   Open rec IDs: ${OPEN_REC_IDS}"

    RECORD: `devforgeai-validate phase-record ${STORY_ID} --phase=01 --step=9.6`

ELSE:
    # No open qa-recommendations. $REMEDIATION_MODE retains whatever 01.9.5 set
    # based on gaps.json detection. $REMEDIATION_SOURCE defaults to "gaps-json"
    # if gaps.json was found, else "none".
    IF $REMEDIATION_MODE == true:
        $REMEDIATION_SOURCE = "gaps-json"
    ELSE:
        $REMEDIATION_SOURCE = "none"
    Display: "ℹ️  No open qa-recommendations for ${STORY_ID}"
```

**Token cost:** ~900 tokens

**Variables set for Phases 02-10:**
- `$REMEDIATION_MODE` — Boolean flag (true if qa-recommendations OR gaps.json source present)
- `$REMEDIATION_SOURCE` — Enum: `"qa-recommendations"`, `"gaps-json"`, `"none"`
- `$OPEN_REC_IDS` — List of REC-IDs from qa-recommendations.md (empty when source != `"qa-recommendations"`)
- `$CURRENT_CYCLE` — Integer cycle number assigned by `phase-cycle-start` (only set when source == `"qa-recommendations"`)

**Precedence rule:** When both gaps.json AND qa-recommendations.md exist for the same story, qa-recommendations takes priority. `$REMEDIATION_SOURCE = "qa-recommendations"`. Step 01.9.5 still sets `$QA_COVERAGE_GAPS`, `$QA_ANTIPATTERN_GAPS`, `$QA_DEFERRAL_GAPS` for Phase 02 consumption — those variables remain populated even when qa-recommendations takes source priority.
