#!/usr/bin/env bash set -euo pipefail # # prdy-validate.sh — Check PRD completeness against Mosaic guide requirements # # Usage: # prdy-validate.sh [--project ] # # Performs static analysis of docs/PRD.md to verify it meets the minimum # content requirements defined in the Mosaic PRD guide. SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" source "$SCRIPT_DIR/_lib.sh" # ─── Parse arguments ───────────────────────────────────────────────────────── PROJECT="." while [[ $# -gt 0 ]]; do case "$1" in --project) PROJECT="$2"; shift 2 ;; -h|--help) cat <<'USAGE' prdy-validate.sh — Check PRD completeness against Mosaic guide Usage: prdy-validate.sh [--project ] Options: --project Project directory (default: CWD) Checks: - docs/PRD.md exists - All 10 required sections present - Metadata block (Owner, Date, Status) - Functional requirements (FR-N) count - User stories (US-NNN) count - Acceptance criteria checklist count - ASSUMPTION markers (informational) Exit codes: 0 All required checks pass 1 One or more required checks failed USAGE exit 0 ;; *) echo "Unknown option: $1" >&2; exit 1 ;; esac done # Expand tilde if passed literally (e.g., --project ~/src/foo) PROJECT="${PROJECT/#\~/$HOME}" # ─── Find PRD ──────────────────────────────────────────────────────────────── step "Validating PRD" PRD_PATH="$(find_prd "$PROJECT")" PASS=0 FAIL_COUNT=0 WARN_COUNT=0 check_pass() { ok "$1" PASS=$((PASS + 1)) } check_fail() { fail "$1" FAIL_COUNT=$((FAIL_COUNT + 1)) } check_warn() { warn "$1" WARN_COUNT=$((WARN_COUNT + 1)) } if [[ -z "$PRD_PATH" ]]; then check_fail "No PRD found. Expected $PROJECT/$PRD_CANONICAL or $PROJECT/$PRD_JSON_ALT" echo "" echo -e "${C_RED}PRD validation failed.${C_RESET} Run ${C_CYAN}mosaic prdy init${C_RESET} to create one." exit 1 fi check_pass "PRD found: $PRD_PATH" # JSON PRDs get a limited check if [[ "$PRD_PATH" == *.json ]]; then info "JSON PRD detected — section checks skipped (markdown-only)" echo "" echo -e "${C_GREEN}PRD file exists.${C_RESET} JSON validation is limited." exit 0 fi PRD_CONTENT="$(cat "$PRD_PATH")" # ─── Section checks ───────────────────────────────────────────────────────── for entry in "${PRDY_REQUIRED_SECTIONS[@]}"; do label="${entry%%|*}" pattern="${entry#*|}" if echo "$PRD_CONTENT" | grep -qiE "$pattern"; then check_pass "$label section present" else check_fail "$label section MISSING" fi done # ─── Metadata checks ──────────────────────────────────────────────────────── META_PASS=true for field in "Owner" "Date" "Status"; do if ! echo "$PRD_CONTENT" | grep -qiE "^- \*\*${field}:\*\*|^- ${field}:"; then META_PASS=false fi done if $META_PASS; then check_pass "Metadata block present (Owner, Date, Status)" else check_fail "Metadata block incomplete (need Owner, Date, Status)" fi # ─── Content counts ───────────────────────────────────────────────────────── FR_COUNT=$(echo "$PRD_CONTENT" | grep -cE '^\- FR-[0-9]+:|^FR-[0-9]+:' || true) US_COUNT=$(echo "$PRD_CONTENT" | grep -cE '^#{1,4} US-[0-9]+' || true) AC_COUNT=$(echo "$PRD_CONTENT" | grep -cE '^\s*- \[ \]' || true) ASSUMPTION_COUNT=$(echo "$PRD_CONTENT" | grep -c 'ASSUMPTION:' || true) if [[ "$FR_COUNT" -gt 0 ]]; then check_pass "Functional requirements: $FR_COUNT FR items" else check_warn "No FR-N items found (expected numbered functional requirements)" fi if [[ "$US_COUNT" -gt 0 ]]; then check_pass "User stories: $US_COUNT US items" else check_warn "No US-NNN items found (expected user stories)" fi if [[ "$AC_COUNT" -gt 0 ]]; then check_pass "Acceptance criteria: $AC_COUNT checklist items" else check_warn "No acceptance criteria checkboxes found" fi if [[ "$ASSUMPTION_COUNT" -gt 0 ]]; then info "ASSUMPTION markers: $ASSUMPTION_COUNT (informational)" fi # ─── Summary ───────────────────────────────────────────────────────────────── TOTAL=$((PASS + FAIL_COUNT)) echo "" if [[ "$FAIL_COUNT" -eq 0 ]]; then echo -e "${C_GREEN}PRD validation passed.${C_RESET} ${PASS}/${TOTAL} checks OK." if [[ "$WARN_COUNT" -gt 0 ]]; then echo -e "${C_YELLOW}${WARN_COUNT} warning(s)${C_RESET} — review recommended." fi exit 0 else echo -e "${C_RED}PRD validation failed.${C_RESET} ${FAIL_COUNT}/${TOTAL} checks failed." if [[ "$WARN_COUNT" -gt 0 ]]; then echo -e "${C_YELLOW}${WARN_COUNT} warning(s)${C_RESET}" fi exit 1 fi