@sdd-method/verify-cli 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,199 @@
1
+ #!/bin/bash
2
+ #
3
+ # validate-app-docs.sh - Validate app repo documentation structure
4
+ #
5
+ # Usage: ./validate-app-docs.sh [--app-root <path>]
6
+ #
7
+ # Checks:
8
+ # 1. service-architecture.md is present
9
+ # 2. feature-tree.md is present (may be at docs/ or repo root)
10
+ # 3. app-platform-dependencies.yaml is present
11
+ # 4. Feature spec files under features/*/ have required sections:
12
+ # - ## Overview
13
+ # - ## Acceptance Criteria
14
+ # - ## Platform Contract Dependencies
15
+ #
16
+ # Does NOT check:
17
+ # - Glossary alignment (no domain glossary in app repos)
18
+ # - Cascade completeness (no domain model or capability tier)
19
+ # - DDD artifacts (no bounded contexts or aggregates)
20
+ # - Traceability manifests (no capability traceability in app repos)
21
+
22
+ set -euo pipefail
23
+
24
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
25
+ ROOT_DIR="$(cd "$SCRIPT_DIR/../.." && pwd)"
26
+
27
+ RED='\033[0;31m'
28
+ GREEN='\033[0;32m'
29
+ YELLOW='\033[1;33m'
30
+ BLUE='\033[0;34m'
31
+ NC='\033[0m'
32
+
33
+ log_info() { echo -e "${GREEN}[INFO]${NC} $1"; }
34
+ log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
35
+ log_fail() { echo -e "${RED}[FAIL]${NC} $1"; }
36
+ log_pass() { echo -e "${GREEN}[PASS]${NC} $1"; }
37
+ # R1: an empty population is "measured nothing", not a pass — the [N/A] tag
38
+ # must start the line (no leading colour escape) so callers can detect it.
39
+ log_na() { echo -e "[N/A] $1"; }
40
+ log_section() {
41
+ echo -e "\n${BLUE}═══════════════════════════════════════════════════════════${NC}"
42
+ echo -e "${BLUE} $1${NC}"
43
+ echo -e "${BLUE}═══════════════════════════════════════════════════════════${NC}\n"
44
+ }
45
+
46
+ APP_ROOT="$ROOT_DIR"
47
+
48
+ while [[ $# -gt 0 ]]; do
49
+ case "$1" in
50
+ --app-root)
51
+ APP_ROOT="${2:-}"
52
+ shift 2
53
+ ;;
54
+ -h|--help)
55
+ echo "Usage: $(basename "$0") [--app-root <path>]"
56
+ exit 0
57
+ ;;
58
+ *)
59
+ log_fail "Unknown argument: $1"
60
+ exit 1
61
+ ;;
62
+ esac
63
+ done
64
+
65
+ if [[ ! -d "$APP_ROOT" ]]; then
66
+ log_fail "App root not found: $APP_ROOT"
67
+ exit 1
68
+ fi
69
+
70
+ FAILURES=0
71
+
72
+ # R1: the feature specs are the population this validator measures. With none,
73
+ # the Group 1 file-presence checks are reported as [INFO] rather than [PASS] so
74
+ # the run reads as "measured nothing", not as a pass.
75
+ FEATURE_SPEC_COUNT="$( (find "$APP_ROOT/features" -type f -name "*.md" 2>/dev/null || true) | wc -l | tr -d ' ')"
76
+ log_found() {
77
+ if [[ "$FEATURE_SPEC_COUNT" -gt 0 ]]; then log_pass "$1"; else log_info "$1"; fi
78
+ }
79
+
80
+ # ──────────────────────────────────────────────────────────────────────────────
81
+ check_required_files() {
82
+ log_section "Group 1: Required App Repo Files"
83
+
84
+ local checks_passed=0
85
+ local checks_failed=0
86
+
87
+ # 1a. service-architecture.md
88
+ local service_arch=""
89
+ if [[ -f "$APP_ROOT/docs/service-architecture.md" ]]; then
90
+ service_arch="$APP_ROOT/docs/service-architecture.md"
91
+ elif [[ -f "$APP_ROOT/service-architecture.md" ]]; then
92
+ service_arch="$APP_ROOT/service-architecture.md"
93
+ fi
94
+
95
+ if [[ -z "$service_arch" ]]; then
96
+ log_fail "service-architecture.md not found (checked docs/service-architecture.md and service-architecture.md)"
97
+ checks_failed=$((checks_failed + 1))
98
+ else
99
+ log_found "service-architecture.md found: ${service_arch#$APP_ROOT/}"
100
+ checks_passed=$((checks_passed + 1))
101
+ fi
102
+
103
+ # 1b. feature-tree.md
104
+ local feature_tree=""
105
+ if [[ -f "$APP_ROOT/docs/platform/feature-tree.md" ]]; then
106
+ feature_tree="$APP_ROOT/docs/platform/feature-tree.md"
107
+ elif [[ -f "$APP_ROOT/docs/feature-tree.md" ]]; then
108
+ feature_tree="$APP_ROOT/docs/feature-tree.md"
109
+ elif [[ -f "$APP_ROOT/feature-tree.md" ]]; then
110
+ feature_tree="$APP_ROOT/feature-tree.md"
111
+ fi
112
+
113
+ if [[ -z "$feature_tree" ]]; then
114
+ log_warn "feature-tree.md not found (checked docs/platform/, docs/, and repo root) — create when features are classified"
115
+ # warn only; not a hard fail at exploration stage
116
+ else
117
+ log_found "feature-tree.md found: ${feature_tree#$APP_ROOT/}"
118
+ checks_passed=$((checks_passed + 1))
119
+ fi
120
+
121
+ # 1c. app-platform-dependencies.yaml
122
+ if [[ -f "$APP_ROOT/app-platform-dependencies.yaml" ]]; then
123
+ log_found "app-platform-dependencies.yaml found"
124
+ checks_passed=$((checks_passed + 1))
125
+ else
126
+ log_fail "app-platform-dependencies.yaml not found in repo root"
127
+ checks_failed=$((checks_failed + 1))
128
+ fi
129
+
130
+ if [[ "$checks_failed" -gt 0 ]]; then
131
+ FAILURES=$((FAILURES + checks_failed))
132
+ fi
133
+
134
+ log_info "Group 1: $checks_passed passed, $checks_failed failed"
135
+ }
136
+
137
+ # ──────────────────────────────────────────────────────────────────────────────
138
+ check_feature_specs() {
139
+ log_section "Group 2: Feature Spec Section Validation"
140
+
141
+ local required_sections=("## Overview" "## Acceptance Criteria" "## Platform Contract Dependencies")
142
+ local specs_found=0
143
+ local specs_failed=0
144
+ local specs_passed=0
145
+
146
+ # Discover feature specs under features/*/
147
+ local feature_spec
148
+ while IFS= read -r feature_spec; do
149
+ specs_found=$((specs_found + 1))
150
+ local rel="${feature_spec#$APP_ROOT/}"
151
+ local file_failures=0
152
+
153
+ for section in "${required_sections[@]}"; do
154
+ if ! grep -q "^${section}" "$feature_spec" 2>/dev/null; then
155
+ log_fail "$rel: missing required section '$section'"
156
+ file_failures=$((file_failures + 1))
157
+ fi
158
+ done
159
+
160
+ if [[ "$file_failures" -eq 0 ]]; then
161
+ log_pass "$rel: all required sections present"
162
+ specs_passed=$((specs_passed + 1))
163
+ else
164
+ specs_failed=$((specs_failed + 1))
165
+ FAILURES=$((FAILURES + file_failures))
166
+ fi
167
+ done < <(find "$APP_ROOT/features" -type f -name "*.md" 2>/dev/null | sort)
168
+
169
+ if [[ "$specs_found" -eq 0 ]]; then
170
+ log_info "No feature specs found under features/ — skipping section validation"
171
+ return 0
172
+ fi
173
+
174
+ log_info "Group 2: $specs_passed/$specs_found feature specs passed, $specs_failed failed"
175
+ }
176
+
177
+ # ──────────────────────────────────────────────────────────────────────────────
178
+ main() {
179
+ log_section "App Documentation Validation"
180
+ log_info "App root: $APP_ROOT"
181
+
182
+ check_required_files
183
+ check_feature_specs
184
+
185
+ log_section "Summary"
186
+
187
+ if [[ "$FAILURES" -eq 0 && "$FEATURE_SPEC_COUNT" -eq 0 ]]; then
188
+ log_na "App documentation: required files present, but 0 feature specs found under features/ — nothing to check"
189
+ exit 0
190
+ elif [[ "$FAILURES" -eq 0 ]]; then
191
+ log_pass "All app documentation checks passed."
192
+ exit 0
193
+ else
194
+ log_fail "$FAILURES check(s) failed."
195
+ exit 1
196
+ fi
197
+ }
198
+
199
+ main "$@"