diff --git a/scripts/epic-execute-lib/decision-log.sh b/scripts/epic-execute-lib/decision-log.sh
new file mode 100644
index 000000000..93998560a
--- /dev/null
+++ b/scripts/epic-execute-lib/decision-log.sh
@@ -0,0 +1,102 @@
+#!/bin/bash
+#
+# BMAD Epic Execute - Decision Log Module
+#
+# Provides functions to maintain a cumulative decision log across phases
+# for context preservation during epic execution.
+#
+# Usage: Sourced by epic-execute.sh
+#
+
+# =============================================================================
+# Decision Log Functions
+# =============================================================================
+
+DECISION_LOG=""
+
+# Initialize the decision log for an epic
+# Creates a new decision log file or appends to existing one
+init_decision_log() {
+ if [ -z "$SPRINT_ARTIFACTS_DIR" ] || [ -z "$EPIC_ID" ]; then
+ log_warn "Cannot initialize decision log: SPRINT_ARTIFACTS_DIR or EPIC_ID not set"
+ return 1
+ fi
+
+ DECISION_LOG="$SPRINT_ARTIFACTS_DIR/epic-${EPIC_ID}-decisions.md"
+ mkdir -p "$(dirname "$DECISION_LOG")"
+
+ # Create new decision log if it doesn't exist
+ if [ ! -f "$DECISION_LOG" ]; then
+ cat > "$DECISION_LOG" << EOF
+# Epic $EPIC_ID Decision Log
+
+This file tracks implementation decisions for context continuity across phases.
+
+**Epic:** $EPIC_ID
+**Started:** $(date '+%Y-%m-%d %H:%M:%S')
+
+---
+
+EOF
+ log "Decision log initialized: $DECISION_LOG"
+ else
+ log "Using existing decision log: $DECISION_LOG"
+ fi
+
+ return 0
+}
+
+# Append a decision to the log
+# Arguments:
+# $1 - phase name (e.g., "DEV", "DESIGN", "FIX")
+# $2 - story_id
+# $3 - content (the decision details)
+append_to_decision_log() {
+ local phase="$1"
+ local story_id="$2"
+ local content="$3"
+
+ if [ -z "$DECISION_LOG" ] || [ ! -f "$DECISION_LOG" ]; then
+ [ "$VERBOSE" = true ] && log_warn "Decision log not initialized"
+ return 1
+ fi
+
+ cat >> "$DECISION_LOG" << EOF
+
+## $phase: $story_id
+**Timestamp:** $(date '+%Y-%m-%d %H:%M:%S')
+
+$content
+
+---
+EOF
+
+ [ "$VERBOSE" = true ] && log "Appended $phase decision for $story_id to decision log"
+ return 0
+}
+
+# Get the decision log contents for inclusion in prompts
+# Returns the full contents of the decision log, or empty string if not available
+get_decision_log_context() {
+ if [ -z "$DECISION_LOG" ] || [ ! -f "$DECISION_LOG" ]; then
+ echo ""
+ return
+ fi
+
+ cat "$DECISION_LOG"
+}
+
+# Get a summary of decisions for a specific story
+# Arguments:
+# $1 - story_id
+get_story_decisions() {
+ local story_id="$1"
+
+ if [ -z "$DECISION_LOG" ] || [ ! -f "$DECISION_LOG" ]; then
+ echo ""
+ return
+ fi
+
+ # Extract sections related to this story
+ grep -A 50 "## .*: $story_id" "$DECISION_LOG" | head -60 || true
+}
diff --git a/scripts/epic-execute-lib/design-phase.sh b/scripts/epic-execute-lib/design-phase.sh
new file mode 100644
index 000000000..8a2b31cc2
--- /dev/null
+++ b/scripts/epic-execute-lib/design-phase.sh
@@ -0,0 +1,201 @@
+#!/bin/bash
+#
+# BMAD Epic Execute - Design Phase Module
+#
+# Provides pre-implementation design phase functionality to catch
+# architectural issues early before coding begins.
+#
+# Usage: Sourced by epic-execute.sh
+#
+
+# =============================================================================
+# Design Phase Variables
+# =============================================================================
+
+# Stores the last design output for passing to dev phase
+LAST_DESIGN=""
+
+# =============================================================================
+# Design Phase Functions
+# =============================================================================
+
+# Execute pre-implementation design phase
+# Generates an implementation plan before coding begins
+# Arguments:
+# $1 - story_file path
+execute_design_phase() {
+ local story_file="$1"
+ local story_id=$(basename "$story_file" .md)
+
+ # Reset last design
+ LAST_DESIGN=""
+
+ log ">>> DESIGN PHASE: $story_id"
+
+ local story_contents=$(cat "$story_file")
+
+ # Load architecture file if available
+ local arch_contents=""
+ for search_path in "$PROJECT_ROOT/docs/architecture.md" "$PROJECT_ROOT/docs/architecture/architecture.md" "$PROJECT_ROOT/architecture.md"; do
+ if [ -f "$search_path" ]; then
+ arch_contents=$(cat "$search_path")
+ break
+ fi
+ done
+
+ # Load previous decisions for context
+ local decision_context=""
+ if type get_decision_log_context >/dev/null 2>&1; then
+ decision_context=$(get_decision_log_context)
+ fi
+
+ local design_prompt="You are a senior developer planning the implementation of a story.
+
+## Your Task
+
+Create an implementation plan for: $story_id
+
+Do NOT write any code yet. Output only your design plan.
+
+### CRITICAL RULES
+- Plan thoroughly BEFORE any implementation
+- Consider existing patterns in the codebase
+- Map each acceptance criterion to specific files/functions
+- Identify potential risks and dependencies
+
+## Story to Plan
+
+**Story Path:** $story_file
+**Story ID:** $story_id
+
+
+$story_contents
+
+
+## Architecture Reference
+
+
+$arch_contents
+
+
+## Previous Decisions in This Epic
+
+
+$decision_context
+
+
+## Exploration Commands
+
+First, explore the codebase to understand existing patterns:
+\`\`\`bash
+# Find similar implementations
+find . -type f -name \"*.ts\" -o -name \"*.js\" | head -20
+# Check project structure
+ls -la src/ 2>/dev/null || ls -la
+\`\`\`
+
+## Required Output
+
+Output your implementation plan in this exact format:
+
+\`\`\`
+DESIGN START
+story_id: $story_id
+
+files_to_modify:
+ - path:
+ action: create|modify
+ purpose:
+
+patterns_to_use:
+ - :
+
+dependencies:
+ - :
+
+acceptance_criteria_mapping:
+ - AC1:
+ - AC2:
+
+risks:
+ -
+
+estimated_test_files:
+ - :
+
+implementation_order:
+ 1.
+ 2.
+ ...
+DESIGN END
+\`\`\`
+
+Be specific and concrete. This plan will guide the implementation phase.
+
+## Completion Signal
+
+After outputting the design block, output exactly:
+DESIGN COMPLETE: $story_id"
+
+ if [ "$DRY_RUN" = true ]; then
+ echo "[DRY RUN] Would execute design phase for $story_id"
+ return 0
+ fi
+
+ local result
+ result=$(claude --dangerously-skip-permissions -p "$design_prompt" 2>&1) || true
+
+ echo "$result" >> "$LOG_FILE"
+
+ # Extract design block
+ LAST_DESIGN=$(echo "$result" | sed -n '/DESIGN START/,/DESIGN END/p')
+
+ if [ -n "$LAST_DESIGN" ]; then
+ # Save to decision log
+ if type append_to_decision_log >/dev/null 2>&1; then
+ append_to_decision_log "DESIGN" "$story_id" "$LAST_DESIGN"
+ fi
+
+ log_success "Design phase complete: $story_id"
+ return 0
+ else
+ log_error "Design phase did not produce valid output"
+ return 1
+ fi
+}
+
+# Get the last design for inclusion in dev phase prompt
+# Returns the design output or empty string if not available
+get_last_design() {
+ echo "$LAST_DESIGN"
+}
+
+# Build the design context block for dev phase prompt
+# Returns formatted design context for inclusion in prompts
+build_design_context_for_dev() {
+ local story_id="$1"
+
+ if [ -z "$LAST_DESIGN" ]; then
+ echo ""
+ return
+ fi
+
+ cat << EOF
+## Pre-Implementation Design
+
+The following design was created in the planning phase. Follow this plan:
+
+
+$LAST_DESIGN
+
+
+### Implementation Guidelines Based on Design
+
+1. Follow the implementation_order specified
+2. Create/modify files as listed in files_to_modify
+3. Use the patterns specified in patterns_to_use
+4. Ensure each acceptance_criteria_mapping is implemented
+5. Be aware of the identified risks
+
+EOF
+}
diff --git a/scripts/epic-execute-lib/regression-gate.sh b/scripts/epic-execute-lib/regression-gate.sh
new file mode 100644
index 000000000..61e61925e
--- /dev/null
+++ b/scripts/epic-execute-lib/regression-gate.sh
@@ -0,0 +1,164 @@
+#!/bin/bash
+#
+# BMAD Epic Execute - Regression Gate Module
+#
+# Provides functions to track test baselines and verify no regressions
+# occur during epic execution.
+#
+# Usage: Sourced by epic-execute.sh
+#
+
+# =============================================================================
+# Regression Gate Variables
+# =============================================================================
+
+BASELINE_PASSING_TESTS=0
+BASELINE_COVERAGE=0
+REGRESSION_INITIALIZED=false
+
+# =============================================================================
+# Regression Gate Functions
+# =============================================================================
+
+# Initialize regression baseline before epic starts
+# Captures current test count and coverage (if available)
+init_regression_baseline() {
+ if [ -z "$PROJECT_ROOT" ]; then
+ log_warn "Cannot initialize regression baseline: PROJECT_ROOT not set"
+ return 1
+ fi
+
+ log "Initializing regression baseline..."
+
+ # Detect project type and capture baseline
+ if [ -f "$PROJECT_ROOT/package.json" ]; then
+ # Node.js/TypeScript project
+ if grep -q '"test"' "$PROJECT_ROOT/package.json" 2>/dev/null; then
+ log "Capturing baseline test count..."
+ local test_output
+ test_output=$(cd "$PROJECT_ROOT" && npm test 2>&1) || true
+
+ # Try multiple patterns to extract passing test count
+ # Pattern 1: "X passing" (mocha/jest)
+ BASELINE_PASSING_TESTS=$(echo "$test_output" | grep -oE '[0-9]+ passing' | grep -oE '[0-9]+' | head -1 || echo "0")
+
+ # Pattern 2: "Tests: X passed" (jest)
+ if [ "$BASELINE_PASSING_TESTS" = "0" ]; then
+ BASELINE_PASSING_TESTS=$(echo "$test_output" | grep -oE 'Tests:.*[0-9]+ passed' | grep -oE '[0-9]+' | head -1 || echo "0")
+ fi
+
+ # Pattern 3: "X tests passed" (generic)
+ if [ "$BASELINE_PASSING_TESTS" = "0" ]; then
+ BASELINE_PASSING_TESTS=$(echo "$test_output" | grep -oE '[0-9]+ tests? passed' | grep -oE '[0-9]+' | head -1 || echo "0")
+ fi
+
+ log "Baseline passing tests: $BASELINE_PASSING_TESTS"
+ fi
+
+ # Capture baseline coverage if available
+ if grep -q '"coverage"' "$PROJECT_ROOT/package.json" 2>/dev/null; then
+ log "Capturing baseline coverage..."
+ local coverage_output
+ coverage_output=$(cd "$PROJECT_ROOT" && npm run coverage -- --json 2>/dev/null) || true
+
+ # Try to extract coverage percentage
+ if command -v jq >/dev/null 2>&1; then
+ BASELINE_COVERAGE=$(echo "$coverage_output" | jq '.total.lines.pct // 0' 2>/dev/null || echo "0")
+ fi
+
+ [ "$BASELINE_COVERAGE" != "0" ] && log "Baseline coverage: ${BASELINE_COVERAGE}%"
+ fi
+
+ elif [ -f "$PROJECT_ROOT/Cargo.toml" ]; then
+ # Rust project
+ log "Capturing baseline test count (Rust)..."
+ local test_output
+ test_output=$(cd "$PROJECT_ROOT" && cargo test 2>&1) || true
+ BASELINE_PASSING_TESTS=$(echo "$test_output" | grep -oE '[0-9]+ passed' | grep -oE '[0-9]+' | head -1 || echo "0")
+ log "Baseline passing tests: $BASELINE_PASSING_TESTS"
+
+ elif [ -f "$PROJECT_ROOT/go.mod" ]; then
+ # Go project
+ log "Capturing baseline test count (Go)..."
+ local test_output
+ test_output=$(cd "$PROJECT_ROOT" && go test ./... -v 2>&1) || true
+ BASELINE_PASSING_TESTS=$(echo "$test_output" | grep -c "^--- PASS" || echo "0")
+ log "Baseline passing tests: $BASELINE_PASSING_TESTS"
+
+ elif [ -f "$PROJECT_ROOT/requirements.txt" ] || [ -f "$PROJECT_ROOT/pyproject.toml" ]; then
+ # Python project
+ if command -v pytest >/dev/null 2>&1; then
+ log "Capturing baseline test count (Python)..."
+ local test_output
+ test_output=$(cd "$PROJECT_ROOT" && pytest --co -q 2>&1) || true
+ BASELINE_PASSING_TESTS=$(echo "$test_output" | grep -oE '[0-9]+ tests? collected' | grep -oE '[0-9]+' | head -1 || echo "0")
+ log "Baseline test count: $BASELINE_PASSING_TESTS"
+ fi
+ fi
+
+ REGRESSION_INITIALIZED=true
+ log_success "Regression baseline initialized: $BASELINE_PASSING_TESTS tests"
+ return 0
+}
+
+# Execute regression gate after a story completes
+# Compares current test count against baseline
+# Arguments:
+# $1 - story_id
+execute_regression_gate() {
+ local story_id="$1"
+
+ if [ "$REGRESSION_INITIALIZED" != true ]; then
+ log_warn "Regression gate skipped: baseline not initialized"
+ return 0
+ fi
+
+ log ">>> REGRESSION GATE: $story_id"
+
+ local current_tests=0
+
+ # Get current test count based on project type
+ if [ -f "$PROJECT_ROOT/package.json" ]; then
+ local test_output
+ test_output=$(cd "$PROJECT_ROOT" && npm test 2>&1) || true
+
+ current_tests=$(echo "$test_output" | grep -oE '[0-9]+ passing' | grep -oE '[0-9]+' | head -1 || echo "0")
+ if [ "$current_tests" = "0" ]; then
+ current_tests=$(echo "$test_output" | grep -oE 'Tests:.*[0-9]+ passed' | grep -oE '[0-9]+' | head -1 || echo "0")
+ fi
+ if [ "$current_tests" = "0" ]; then
+ current_tests=$(echo "$test_output" | grep -oE '[0-9]+ tests? passed' | grep -oE '[0-9]+' | head -1 || echo "0")
+ fi
+
+ elif [ -f "$PROJECT_ROOT/Cargo.toml" ]; then
+ local test_output
+ test_output=$(cd "$PROJECT_ROOT" && cargo test 2>&1) || true
+ current_tests=$(echo "$test_output" | grep -oE '[0-9]+ passed' | grep -oE '[0-9]+' | head -1 || echo "0")
+
+ elif [ -f "$PROJECT_ROOT/go.mod" ]; then
+ local test_output
+ test_output=$(cd "$PROJECT_ROOT" && go test ./... -v 2>&1) || true
+ current_tests=$(echo "$test_output" | grep -c "^--- PASS" || echo "0")
+
+ elif [ -f "$PROJECT_ROOT/requirements.txt" ] || [ -f "$PROJECT_ROOT/pyproject.toml" ]; then
+ if command -v pytest >/dev/null 2>&1; then
+ local test_output
+ test_output=$(cd "$PROJECT_ROOT" && pytest -v 2>&1) || true
+ current_tests=$(echo "$test_output" | grep -oE '[0-9]+ passed' | grep -oE '[0-9]+' | head -1 || echo "0")
+ fi
+ fi
+
+ # Check for regression
+ if [ "$current_tests" -lt "$BASELINE_PASSING_TESTS" ]; then
+ log_error "REGRESSION DETECTED: Test count decreased ($BASELINE_PASSING_TESTS -> $current_tests)"
+ add_metrics_issue "$story_id" "regression" "Test count decreased from $BASELINE_PASSING_TESTS to $current_tests"
+ return 1
+ fi
+
+ # Update baseline for next story (allow growth)
+ local previous_baseline=$BASELINE_PASSING_TESTS
+ BASELINE_PASSING_TESTS=$current_tests
+
+ log_success "Regression gate passed: $current_tests tests (baseline was $previous_baseline)"
+ return 0
+}
diff --git a/scripts/epic-execute.sh b/scripts/epic-execute.sh
index 018c052c5..b3e80c076 100755
--- a/scripts/epic-execute.sh
+++ b/scripts/epic-execute.sh
@@ -15,6 +15,7 @@
# --skip-arch Skip architecture compliance check
# --skip-test-quality Skip test quality review
# --skip-traceability Skip traceability check (not recommended)
+# --skip-static-analysis Skip static analysis gate (runs real tooling)
#
set -e
@@ -27,6 +28,15 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
BMAD_DIR="$PROJECT_ROOT/bmad"
+# =============================================================================
+# Source Modular Components
+# =============================================================================
+
+LIB_DIR="$SCRIPT_DIR/epic-execute-lib"
+[ -f "$LIB_DIR/decision-log.sh" ] && source "$LIB_DIR/decision-log.sh"
+[ -f "$LIB_DIR/regression-gate.sh" ] && source "$LIB_DIR/regression-gate.sh"
+[ -f "$LIB_DIR/design-phase.sh" ] && source "$LIB_DIR/design-phase.sh"
+
STORIES_DIR="$PROJECT_ROOT/docs/stories"
SPRINT_ARTIFACTS_DIR="$PROJECT_ROOT/docs/sprint-artifacts"
SPRINTS_DIR="$PROJECT_ROOT/docs/sprints"
@@ -369,6 +379,9 @@ SKIP_DONE=false
SKIP_ARCH=false
SKIP_TEST_QUALITY=false
SKIP_TRACEABILITY=false
+SKIP_STATIC_ANALYSIS=false
+SKIP_DESIGN=false
+SKIP_REGRESSION=false
while [[ $# -gt 0 ]]; do
case $1 in
@@ -412,6 +425,18 @@ while [[ $# -gt 0 ]]; do
SKIP_TRACEABILITY=true
shift
;;
+ --skip-static-analysis)
+ SKIP_STATIC_ANALYSIS=true
+ shift
+ ;;
+ --skip-design)
+ SKIP_DESIGN=true
+ shift
+ ;;
+ --skip-regression)
+ SKIP_REGRESSION=true
+ shift
+ ;;
-*)
echo "Unknown option: $1"
exit 1
@@ -437,6 +462,9 @@ if [ -z "$EPIC_ID" ]; then
echo " --skip-arch Skip architecture compliance check"
echo " --skip-test-quality Skip test quality review"
echo " --skip-traceability Skip traceability check (not recommended)"
+ echo " --skip-static-analysis Skip static analysis gate (runs real tooling)"
+ echo " --skip-design Skip pre-implementation design phase"
+ echo " --skip-regression Skip regression test gate"
exit 1
fi
@@ -509,6 +537,16 @@ EPIC_START_TIME=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
EPIC_START_SECONDS=$(date +%s)
init_metrics
+# Initialize decision log (if module loaded)
+if type init_decision_log >/dev/null 2>&1; then
+ init_decision_log
+fi
+
+# Initialize regression baseline (if module loaded and not skipped)
+if [ "$SKIP_REGRESSION" = false ] && type init_regression_baseline >/dev/null 2>&1; then
+ init_regression_baseline
+fi
+
# Find epic file (supports both epic-39-*.md and epic-039-*.md formats)
EPIC_FILE=""
# Pad epic ID with leading zero for 3-digit format (e.g., 40 -> 040)
@@ -623,6 +661,18 @@ execute_dev_phase() {
local workflow_executor=$(cat "$WORKFLOW_EXECUTOR")
local story_contents=$(cat "$story_file")
+ # Get decision log context if available
+ local decision_context=""
+ if type get_decision_log_context >/dev/null 2>&1; then
+ decision_context=$(get_decision_log_context)
+ fi
+
+ # Get design context if available (from design phase)
+ local design_context=""
+ if type build_design_context_for_dev >/dev/null 2>&1; then
+ design_context=$(build_design_context_for_dev "$story_id")
+ fi
+
# Build the dev prompt using BMAD workflow
local dev_prompt="You are executing a BMAD dev-story workflow in automated mode.
@@ -670,6 +720,12 @@ $workflow_checklist
$story_contents
+$design_context
+## Previous Implementation Context
+
+
+$decision_context
+
## Execution Variables (Pre-resolved)
@@ -886,6 +942,7 @@ execute_fix_phase() {
local story_file="$1"
local review_findings="$2"
local attempt_num="$3"
+ local static_analysis_context="${4:-}" # Optional: real tooling output
local story_id=$(basename "$story_file" .md)
log ">>> FIX PHASE: $story_id (attempt $attempt_num, using BMAD dev-story workflow)"
@@ -906,6 +963,19 @@ execute_fix_phase() {
local workflow_executor=$(cat "$WORKFLOW_EXECUTOR")
local story_contents=$(cat "$story_file")
+ # Build real tooling output section if available
+ local tooling_section=""
+ if [ -n "$static_analysis_context" ]; then
+ tooling_section="
+## Actual Tooling Output
+
+The following are REAL errors from running the project's tooling (not AI-generated).
+These must be fixed first as they represent actual compilation/test failures:
+
+$static_analysis_context
+"
+ fi
+
# Build the fix prompt using BMAD dev-story workflow with review context
local fix_prompt="You are executing a BMAD dev-story workflow in FIX MODE to address code review findings.
@@ -928,7 +998,7 @@ The following issues were identified during code review and MUST be fixed:
$review_findings
-
+$tooling_section
## Workflow Executor Engine
@@ -1028,11 +1098,255 @@ Address all review findings now. This is attempt $attempt_num of 3."
fi
}
+# =============================================================================
+# Static Analysis Gate - Real Tooling Verification
+# =============================================================================
+
+execute_static_analysis_gate() {
+ local story_file="$1"
+ local story_id=$(basename "$story_file" .md)
+ local failures=0
+ local failure_details=""
+
+ # Reset failures
+ LAST_STATIC_ANALYSIS_FAILURES=""
+
+ log ">>> STATIC ANALYSIS GATE: $story_id (running real tooling)"
+
+ if [ "$DRY_RUN" = true ]; then
+ echo "[DRY RUN] Would run static analysis gate for $story_id"
+ return 0
+ fi
+
+ # Detect project type and run appropriate checks
+ if [ -f "$PROJECT_ROOT/package.json" ]; then
+ log "Detected Node.js/TypeScript project"
+
+ # 1. Type checking (catches type errors AI might miss)
+ if grep -q '"typecheck"\|"type-check"\|"tsc"' "$PROJECT_ROOT/package.json" 2>/dev/null; then
+ log "Running type check..."
+ local typecheck_output
+ typecheck_output=$(cd "$PROJECT_ROOT" && npm run typecheck 2>&1) || {
+ local exit_code=$?
+ log_error "Type check failed (exit code: $exit_code)"
+ failure_details+="
+### Type Check Failures
+\`\`\`
+$typecheck_output
+\`\`\`
+"
+ ((failures++))
+ }
+ echo "$typecheck_output" >> "$LOG_FILE"
+ elif [ -f "$PROJECT_ROOT/tsconfig.json" ]; then
+ # Fallback: run tsc directly if tsconfig exists
+ log "Running tsc directly..."
+ local tsc_output
+ tsc_output=$(cd "$PROJECT_ROOT" && npx tsc --noEmit 2>&1) || {
+ local exit_code=$?
+ log_error "TypeScript compilation failed (exit code: $exit_code)"
+ failure_details+="
+### TypeScript Compilation Failures
+\`\`\`
+$tsc_output
+\`\`\`
+"
+ ((failures++))
+ }
+ echo "$tsc_output" >> "$LOG_FILE"
+ fi
+
+ # 2. Linting (catches code style/quality issues)
+ if grep -q '"lint"' "$PROJECT_ROOT/package.json" 2>/dev/null; then
+ log "Running lint..."
+ local lint_output
+ lint_output=$(cd "$PROJECT_ROOT" && npm run lint 2>&1) || {
+ local exit_code=$?
+ log_error "Lint failed (exit code: $exit_code)"
+ failure_details+="
+### Lint Failures
+\`\`\`
+$lint_output
+\`\`\`
+"
+ ((failures++))
+ }
+ echo "$lint_output" >> "$LOG_FILE"
+ fi
+
+ # 3. Build (catches compilation errors)
+ if grep -q '"build"' "$PROJECT_ROOT/package.json" 2>/dev/null; then
+ log "Running build..."
+ local build_output
+ build_output=$(cd "$PROJECT_ROOT" && npm run build 2>&1) || {
+ local exit_code=$?
+ log_error "Build failed (exit code: $exit_code)"
+ failure_details+="
+### Build Failures
+\`\`\`
+$build_output
+\`\`\`
+"
+ ((failures++))
+ }
+ echo "$build_output" >> "$LOG_FILE"
+ fi
+
+ # 4. Tests (catches actual test failures)
+ if grep -q '"test"' "$PROJECT_ROOT/package.json" 2>/dev/null; then
+ log "Running tests..."
+ local test_output
+ test_output=$(cd "$PROJECT_ROOT" && npm test 2>&1) || {
+ local exit_code=$?
+ log_error "Tests failed (exit code: $exit_code)"
+ failure_details+="
+### Test Failures
+\`\`\`
+$test_output
+\`\`\`
+"
+ ((failures++))
+ }
+ echo "$test_output" >> "$LOG_FILE"
+ fi
+
+ elif [ -f "$PROJECT_ROOT/Cargo.toml" ]; then
+ log "Detected Rust project"
+
+ # Cargo check (type checking)
+ log "Running cargo check..."
+ local cargo_output
+ cargo_output=$(cd "$PROJECT_ROOT" && cargo check 2>&1) || {
+ log_error "Cargo check failed"
+ failure_details+="
+### Cargo Check Failures
+\`\`\`
+$cargo_output
+\`\`\`
+"
+ ((failures++))
+ }
+ echo "$cargo_output" >> "$LOG_FILE"
+
+ # Cargo test
+ log "Running cargo test..."
+ local test_output
+ test_output=$(cd "$PROJECT_ROOT" && cargo test 2>&1) || {
+ log_error "Cargo tests failed"
+ failure_details+="
+### Cargo Test Failures
+\`\`\`
+$test_output
+\`\`\`
+"
+ ((failures++))
+ }
+ echo "$test_output" >> "$LOG_FILE"
+
+ elif [ -f "$PROJECT_ROOT/go.mod" ]; then
+ log "Detected Go project"
+
+ # Go build
+ log "Running go build..."
+ local build_output
+ build_output=$(cd "$PROJECT_ROOT" && go build ./... 2>&1) || {
+ log_error "Go build failed"
+ failure_details+="
+### Go Build Failures
+\`\`\`
+$build_output
+\`\`\`
+"
+ ((failures++))
+ }
+ echo "$build_output" >> "$LOG_FILE"
+
+ # Go test
+ log "Running go test..."
+ local test_output
+ test_output=$(cd "$PROJECT_ROOT" && go test ./... 2>&1) || {
+ log_error "Go tests failed"
+ failure_details+="
+### Go Test Failures
+\`\`\`
+$test_output
+\`\`\`
+"
+ ((failures++))
+ }
+ echo "$test_output" >> "$LOG_FILE"
+
+ elif [ -f "$PROJECT_ROOT/requirements.txt" ] || [ -f "$PROJECT_ROOT/pyproject.toml" ]; then
+ log "Detected Python project"
+
+ # pytest
+ if command -v pytest >/dev/null 2>&1; then
+ log "Running pytest..."
+ local test_output
+ test_output=$(cd "$PROJECT_ROOT" && pytest 2>&1) || {
+ log_error "Pytest failed"
+ failure_details+="
+### Pytest Failures
+\`\`\`
+$test_output
+\`\`\`
+"
+ ((failures++))
+ }
+ echo "$test_output" >> "$LOG_FILE"
+ fi
+
+ # mypy (if available)
+ if command -v mypy >/dev/null 2>&1 && [ -f "$PROJECT_ROOT/mypy.ini" ] || [ -f "$PROJECT_ROOT/setup.cfg" ]; then
+ log "Running mypy..."
+ local mypy_output
+ mypy_output=$(cd "$PROJECT_ROOT" && mypy . 2>&1) || {
+ log_error "Mypy type check failed"
+ failure_details+="
+### Mypy Type Check Failures
+\`\`\`
+$mypy_output
+\`\`\`
+"
+ ((failures++))
+ }
+ echo "$mypy_output" >> "$LOG_FILE"
+ fi
+
+ else
+ log_warn "No recognized project type found - skipping static analysis"
+ return 0
+ fi
+
+ # Check results
+ if [ $failures -gt 0 ]; then
+ log_error "Static analysis gate failed with $failures issue(s)"
+
+ # Store failures for fix phase
+ LAST_STATIC_ANALYSIS_FAILURES="## Static Analysis Failures for $story_id
+
+The following REAL tooling failures were detected. These are NOT AI-generated - they are actual errors from running the project's tooling.
+
+$failure_details
+
+## Instructions
+
+Fix ALL the errors shown above. These are real compilation/test failures that must be resolved."
+
+ add_metrics_issue "$story_id" "static_analysis_failed" "Static analysis gate failed with $failures issue(s)"
+ return 1
+ fi
+
+ log_success "Static analysis gate passed: $story_id"
+ return 0
+}
+
# Maximum number of fix attempts before giving up
MAX_FIX_ATTEMPTS=3
MAX_ARCH_FIX_ATTEMPTS=2
MAX_TEST_QUALITY_FIX_ATTEMPTS=2
MAX_TRACEABILITY_FIX_ATTEMPTS=3
+MAX_STATIC_ANALYSIS_FIX_ATTEMPTS=3
# Global variable to store arch violations for fix loop
LAST_ARCH_VIOLATIONS=""
@@ -1043,6 +1357,9 @@ LAST_TEST_QUALITY_ISSUES=""
# Global variable to store traceability gaps for fix loop
LAST_TRACEABILITY_GAPS=""
+# Global variable to store static analysis failures for fix loop
+LAST_STATIC_ANALYSIS_FAILURES=""
+
execute_arch_compliance_phase() {
local story_file="$1"
local story_id=$(basename "$story_file" .md)
@@ -1534,12 +1851,51 @@ execute_story_with_fix_loop() {
local test_quality_fix_attempt=0
local needs_fixes=false
+ # DESIGN PHASE (Context 0) - Pre-implementation planning
+ if [ "$SKIP_DESIGN" = false ] && type execute_design_phase >/dev/null 2>&1; then
+ if ! execute_design_phase "$story_file"; then
+ log_warn "Design phase did not complete cleanly for $story_id - proceeding to dev"
+ # Don't fail - design is advisory
+ fi
+ fi
+
# DEV PHASE (Context 1)
if ! execute_dev_phase "$story_file"; then
log_error "Dev phase failed for $story_id"
return 1
fi
+ # STATIC ANALYSIS GATE (Real Tooling) - Per Story
+ local static_analysis_fix_attempt=0
+ if [ "$SKIP_STATIC_ANALYSIS" = false ]; then
+ while true; do
+ if execute_static_analysis_gate "$story_file"; then
+ log_success "Static analysis passed: $story_id"
+ break
+ fi
+
+ # Check if we have failures to fix
+ if [ -z "$LAST_STATIC_ANALYSIS_FAILURES" ]; then
+ log_warn "Static analysis unclear, proceeding anyway"
+ break
+ fi
+
+ ((static_analysis_fix_attempt++))
+ if [ $static_analysis_fix_attempt -gt $MAX_STATIC_ANALYSIS_FIX_ATTEMPTS ]; then
+ log_error "Max static analysis fix attempts ($MAX_STATIC_ANALYSIS_FIX_ATTEMPTS) reached for $story_id"
+ add_metrics_issue "$story_id" "static_analysis_max_retries" "Static analysis failures after $MAX_STATIC_ANALYSIS_FIX_ATTEMPTS attempts"
+ # Fail the story - real tooling errors must be fixed
+ return 1
+ fi
+
+ log_warn "Static analysis failed, attempting fix $static_analysis_fix_attempt of $MAX_STATIC_ANALYSIS_FIX_ATTEMPTS"
+ # Use the regular fix phase with static analysis context
+ if ! execute_fix_phase "$story_file" "$LAST_STATIC_ANALYSIS_FAILURES" "$static_analysis_fix_attempt"; then
+ log_warn "Static analysis fix incomplete, re-running gate..."
+ fi
+ done
+ fi
+
# ARCHITECTURE COMPLIANCE CHECK (Context 2) - Per Story
if [ "$SKIP_ARCH" = false ]; then
while true; do
@@ -1642,6 +1998,16 @@ execute_story_with_fix_loop() {
done
fi
+ # REGRESSION GATE (if module loaded and not skipped)
+ if [ "$SKIP_REGRESSION" = false ] && type execute_regression_gate >/dev/null 2>&1; then
+ if ! execute_regression_gate "$story_id"; then
+ log_error "Regression detected in $story_id"
+ add_metrics_issue "$story_id" "regression_detected" "Test count decreased after implementation"
+ # Don't fail the story - regression is a warning that should be investigated
+ log_warn "Proceeding despite regression - investigate manually"
+ fi
+ fi
+
return 0
}