#!/usr/bin/env sh set -eu # Check AC (Acceptance Criteria) Traceability. # 1. Collect all AC IDs defined in spec/**/requirements.md (format: [AC-FEATURE-NN]) # 2. Collect all AC IDs referenced in src/** and test/** (format: [AC-FEATURE-NN]) # 3. Verify every referenced AC ID exists in requirements. die() { echo "ERROR: $*" >&2 exit 1 } # 1. Collect defined ACs from requirements defined_acs_file=$(mktemp) # Matches patterns like [AC-REG-01] find spec -name "requirements.md" -exec grep -o "\[AC-[A-Z0-9]\+-[0-9]\{2\}\]" {} + | sed 's/.*\[\(AC-[A-Z0-9]\+-[0-9]\{2\}\)\].*/\1/' | sort -u > "$defined_acs_file" if [ ! -s "$defined_acs_file" ]; then echo "Warning: No AC IDs found in spec/**/requirements.md. Skipping traceability check." rm -f "$defined_acs_file" exit 0 fi echo "Found $(wc -l < "$defined_acs_file") defined AC IDs in requirements." # 2. Collect referenced ACs from src/ and test/ referenced_acs_file=$(mktemp) # Search in src and test directories find src test -type f \( -name "*.java" -o -name "*.vue" -o -name "*.ts" -o -name "*.tsx" -o -name "*.md" \) -exec grep -o "\[AC-[A-Z0-9]\+-[0-9]\{2\}\]" {} + | sed 's/.*\[\(AC-[A-Z0-9]\+-[0-9]\{2\}\)\].*/\1/' | sort -u > "$referenced_acs_file" if [ ! -s "$referenced_acs_file" ]; then echo "OK: No AC references found in code. Traceability check skipped." rm -f "$defined_acs_file" "$referenced_acs_file" exit 0 fi echo "Found $(wc -l < "$referenced_acs_file") AC references in code." # 3. Verify references missing_acs=0 while read -r ac; do if ! grep -q "^$ac$" "$defined_acs_file"; then echo "ERROR: Referenced AC ID '$ac' not found in any requirements.md" missing_acs=$((missing_acs + 1)) fi done < "$referenced_acs_file" rm -f "$defined_acs_file" "$referenced_acs_file" if [ "$missing_acs" -gt 0 ]; then die "Traceability check failed: $missing_acs unknown AC reference(s) found." fi echo "OK: All AC references in code are traceable to requirements."