feat(epic-execute): add phase 2+3 improvements with modular architecture
- Add decision log module for context preservation across phases - Add regression gate module for test baseline tracking - Add design phase module for pre-implementation planning - Enhance fix phase to include real tooling output - Pass design and decision context to dev phase - Add --skip-design and --skip-regression CLI flags - Modularize into epic-execute-lib/ for maintainability Implements improvements from bmad_improvements_v2.md: - Phase 2.1: Real test output in fix loops - Phase 2.2: Cumulative decision log - Phase 2.3: Regression test gate - Phase 3.1: Pre-implementation design phase Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
3bad076884
commit
fd744c96f3
|
|
@ -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
|
||||||
|
}
|
||||||
|
|
@ -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>
|
||||||
|
$story_contents
|
||||||
|
</story>
|
||||||
|
|
||||||
|
## Architecture Reference
|
||||||
|
|
||||||
|
<architecture>
|
||||||
|
$arch_contents
|
||||||
|
</architecture>
|
||||||
|
|
||||||
|
## Previous Decisions in This Epic
|
||||||
|
|
||||||
|
<decision-context>
|
||||||
|
$decision_context
|
||||||
|
</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: <file path>
|
||||||
|
action: create|modify
|
||||||
|
purpose: <why this file needs changes>
|
||||||
|
|
||||||
|
patterns_to_use:
|
||||||
|
- <pattern name>: <how it will be applied>
|
||||||
|
|
||||||
|
dependencies:
|
||||||
|
- <package>: <installed|needs-install>
|
||||||
|
|
||||||
|
acceptance_criteria_mapping:
|
||||||
|
- AC1: <which files/functions will implement this>
|
||||||
|
- AC2: <which files/functions will implement this>
|
||||||
|
|
||||||
|
risks:
|
||||||
|
- <potential issue and mitigation>
|
||||||
|
|
||||||
|
estimated_test_files:
|
||||||
|
- <test file path>: <what it will test>
|
||||||
|
|
||||||
|
implementation_order:
|
||||||
|
1. <first step>
|
||||||
|
2. <second step>
|
||||||
|
...
|
||||||
|
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:
|
||||||
|
|
||||||
|
<design-plan>
|
||||||
|
$LAST_DESIGN
|
||||||
|
</design-plan>
|
||||||
|
|
||||||
|
### 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
|
||||||
|
}
|
||||||
|
|
@ -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
|
||||||
|
}
|
||||||
|
|
@ -15,6 +15,7 @@
|
||||||
# --skip-arch Skip architecture compliance check
|
# --skip-arch Skip architecture compliance check
|
||||||
# --skip-test-quality Skip test quality review
|
# --skip-test-quality Skip test quality review
|
||||||
# --skip-traceability Skip traceability check (not recommended)
|
# --skip-traceability Skip traceability check (not recommended)
|
||||||
|
# --skip-static-analysis Skip static analysis gate (runs real tooling)
|
||||||
#
|
#
|
||||||
|
|
||||||
set -e
|
set -e
|
||||||
|
|
@ -27,6 +28,15 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
||||||
BMAD_DIR="$PROJECT_ROOT/bmad"
|
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"
|
STORIES_DIR="$PROJECT_ROOT/docs/stories"
|
||||||
SPRINT_ARTIFACTS_DIR="$PROJECT_ROOT/docs/sprint-artifacts"
|
SPRINT_ARTIFACTS_DIR="$PROJECT_ROOT/docs/sprint-artifacts"
|
||||||
SPRINTS_DIR="$PROJECT_ROOT/docs/sprints"
|
SPRINTS_DIR="$PROJECT_ROOT/docs/sprints"
|
||||||
|
|
@ -369,6 +379,9 @@ SKIP_DONE=false
|
||||||
SKIP_ARCH=false
|
SKIP_ARCH=false
|
||||||
SKIP_TEST_QUALITY=false
|
SKIP_TEST_QUALITY=false
|
||||||
SKIP_TRACEABILITY=false
|
SKIP_TRACEABILITY=false
|
||||||
|
SKIP_STATIC_ANALYSIS=false
|
||||||
|
SKIP_DESIGN=false
|
||||||
|
SKIP_REGRESSION=false
|
||||||
|
|
||||||
while [[ $# -gt 0 ]]; do
|
while [[ $# -gt 0 ]]; do
|
||||||
case $1 in
|
case $1 in
|
||||||
|
|
@ -412,6 +425,18 @@ while [[ $# -gt 0 ]]; do
|
||||||
SKIP_TRACEABILITY=true
|
SKIP_TRACEABILITY=true
|
||||||
shift
|
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"
|
echo "Unknown option: $1"
|
||||||
exit 1
|
exit 1
|
||||||
|
|
@ -437,6 +462,9 @@ if [ -z "$EPIC_ID" ]; then
|
||||||
echo " --skip-arch Skip architecture compliance check"
|
echo " --skip-arch Skip architecture compliance check"
|
||||||
echo " --skip-test-quality Skip test quality review"
|
echo " --skip-test-quality Skip test quality review"
|
||||||
echo " --skip-traceability Skip traceability check (not recommended)"
|
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
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
@ -509,6 +537,16 @@ EPIC_START_TIME=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
||||||
EPIC_START_SECONDS=$(date +%s)
|
EPIC_START_SECONDS=$(date +%s)
|
||||||
init_metrics
|
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)
|
# Find epic file (supports both epic-39-*.md and epic-039-*.md formats)
|
||||||
EPIC_FILE=""
|
EPIC_FILE=""
|
||||||
# Pad epic ID with leading zero for 3-digit format (e.g., 40 -> 040)
|
# 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 workflow_executor=$(cat "$WORKFLOW_EXECUTOR")
|
||||||
local story_contents=$(cat "$story_file")
|
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
|
# Build the dev prompt using BMAD workflow
|
||||||
local dev_prompt="You are executing a BMAD dev-story workflow in automated mode.
|
local dev_prompt="You are executing a BMAD dev-story workflow in automated mode.
|
||||||
|
|
||||||
|
|
@ -670,6 +720,12 @@ $workflow_checklist
|
||||||
<story-contents>
|
<story-contents>
|
||||||
$story_contents
|
$story_contents
|
||||||
</story-contents>
|
</story-contents>
|
||||||
|
$design_context
|
||||||
|
## Previous Implementation Context
|
||||||
|
|
||||||
|
<decision-log>
|
||||||
|
$decision_context
|
||||||
|
</decision-log>
|
||||||
|
|
||||||
## Execution Variables (Pre-resolved)
|
## Execution Variables (Pre-resolved)
|
||||||
|
|
||||||
|
|
@ -886,6 +942,7 @@ execute_fix_phase() {
|
||||||
local story_file="$1"
|
local story_file="$1"
|
||||||
local review_findings="$2"
|
local review_findings="$2"
|
||||||
local attempt_num="$3"
|
local attempt_num="$3"
|
||||||
|
local static_analysis_context="${4:-}" # Optional: real tooling output
|
||||||
local story_id=$(basename "$story_file" .md)
|
local story_id=$(basename "$story_file" .md)
|
||||||
|
|
||||||
log ">>> FIX PHASE: $story_id (attempt $attempt_num, using BMAD dev-story workflow)"
|
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 workflow_executor=$(cat "$WORKFLOW_EXECUTOR")
|
||||||
local story_contents=$(cat "$story_file")
|
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
|
# 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.
|
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>
|
<review-findings>
|
||||||
$review_findings
|
$review_findings
|
||||||
</review-findings>
|
</review-findings>
|
||||||
|
$tooling_section
|
||||||
## Workflow Executor Engine
|
## Workflow Executor Engine
|
||||||
|
|
||||||
<workflow-executor>
|
<workflow-executor>
|
||||||
|
|
@ -1028,11 +1098,255 @@ Address all review findings now. This is attempt $attempt_num of 3."
|
||||||
fi
|
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
|
# Maximum number of fix attempts before giving up
|
||||||
MAX_FIX_ATTEMPTS=3
|
MAX_FIX_ATTEMPTS=3
|
||||||
MAX_ARCH_FIX_ATTEMPTS=2
|
MAX_ARCH_FIX_ATTEMPTS=2
|
||||||
MAX_TEST_QUALITY_FIX_ATTEMPTS=2
|
MAX_TEST_QUALITY_FIX_ATTEMPTS=2
|
||||||
MAX_TRACEABILITY_FIX_ATTEMPTS=3
|
MAX_TRACEABILITY_FIX_ATTEMPTS=3
|
||||||
|
MAX_STATIC_ANALYSIS_FIX_ATTEMPTS=3
|
||||||
|
|
||||||
# Global variable to store arch violations for fix loop
|
# Global variable to store arch violations for fix loop
|
||||||
LAST_ARCH_VIOLATIONS=""
|
LAST_ARCH_VIOLATIONS=""
|
||||||
|
|
@ -1043,6 +1357,9 @@ LAST_TEST_QUALITY_ISSUES=""
|
||||||
# Global variable to store traceability gaps for fix loop
|
# Global variable to store traceability gaps for fix loop
|
||||||
LAST_TRACEABILITY_GAPS=""
|
LAST_TRACEABILITY_GAPS=""
|
||||||
|
|
||||||
|
# Global variable to store static analysis failures for fix loop
|
||||||
|
LAST_STATIC_ANALYSIS_FAILURES=""
|
||||||
|
|
||||||
execute_arch_compliance_phase() {
|
execute_arch_compliance_phase() {
|
||||||
local story_file="$1"
|
local story_file="$1"
|
||||||
local story_id=$(basename "$story_file" .md)
|
local story_id=$(basename "$story_file" .md)
|
||||||
|
|
@ -1534,12 +1851,51 @@ execute_story_with_fix_loop() {
|
||||||
local test_quality_fix_attempt=0
|
local test_quality_fix_attempt=0
|
||||||
local needs_fixes=false
|
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)
|
# DEV PHASE (Context 1)
|
||||||
if ! execute_dev_phase "$story_file"; then
|
if ! execute_dev_phase "$story_file"; then
|
||||||
log_error "Dev phase failed for $story_id"
|
log_error "Dev phase failed for $story_id"
|
||||||
return 1
|
return 1
|
||||||
fi
|
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
|
# ARCHITECTURE COMPLIANCE CHECK (Context 2) - Per Story
|
||||||
if [ "$SKIP_ARCH" = false ]; then
|
if [ "$SKIP_ARCH" = false ]; then
|
||||||
while true; do
|
while true; do
|
||||||
|
|
@ -1642,6 +1998,16 @@ execute_story_with_fix_loop() {
|
||||||
done
|
done
|
||||||
fi
|
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
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue