bigpowers 2.73.4 → 2.73.6

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,272 @@
1
+ #!/usr/bin/env bash
2
+ # story: e38s09 e41s04 e45s02
3
+ # OKF kind-specific validators — sourced by validate-okf.sh
4
+
5
+ if [ -n "${VALIDATE_OKF_KINDS_LOADED:-}" ]; then return 0; fi
6
+ VALIDATE_OKF_KINDS_LOADED=1
7
+
8
+ val_nullish() { [ -z "$1" ] || [ "$1" = "null" ] || [ "$1" = "None" ]; }
9
+ val_nullish_or_empty() { val_nullish "$1" || [ "$1" = "[]" ]; }
10
+
11
+ okf_parse_frontmatter() {
12
+ local file="$1"
13
+ $PYTHON -c "
14
+ import yaml, sys, json
15
+
16
+ class SafeEncoder(json.JSONEncoder):
17
+ def default(self, obj):
18
+ try:
19
+ return super().default(obj)
20
+ except TypeError:
21
+ return str(obj)
22
+
23
+ content = open('$file').read()
24
+ parts = content.split('---')
25
+ if len(parts) < 3:
26
+ sys.exit(1)
27
+ try:
28
+ fm = yaml.safe_load(parts[1])
29
+ if fm is None:
30
+ sys.exit(1)
31
+ print(json.dumps(fm, cls=SafeEncoder))
32
+ except Exception as e:
33
+ print(f'YAML parse error: {e}', file=sys.stderr)
34
+ sys.exit(1)
35
+ " 2>/dev/null
36
+ }
37
+
38
+ validate_story_metrics() {
39
+ local file="$1" fm="$2" name="$3" errs=0 key val source generator commit_range eff
40
+ for key in id epic bcps commit_range source generated_at generator; do
41
+ val="$(echo "$fm" | $PYTHON -c "import json,sys; d=json.load(sys.stdin); print(d.get('$key',''))" 2>/dev/null)"
42
+ if val_nullish "$val"; then
43
+ printf "${RED}FAIL${NC} %s: missing required key '%s'\n" "$name" "$key"
44
+ errs=$((errs + 1))
45
+ fi
46
+ done
47
+ [ "$errs" -gt 0 ] && { EXIT_CODE=1; return; }
48
+
49
+ source="$(echo "$fm" | $PYTHON -c "import json,sys; d=json.load(sys.stdin); print(d.get('source',''))" 2>/dev/null)"
50
+ if ! echo "measured estimated backfilled" | grep -qw "$source"; then
51
+ printf "${RED}FAIL${NC} %s: invalid source '%s' — must be measured|estimated|backfilled\n" "$name" "$source"
52
+ EXIT_CODE=1; return
53
+ fi
54
+
55
+ generator="$(echo "$fm" | $PYTHON -c "import json,sys; d=json.load(sys.stdin); print(d.get('generator',''))" 2>/dev/null)"
56
+ if [ "$generator" != "scripts/record-cycle-time.sh" ]; then
57
+ printf "${RED}FAIL${NC} %s: wrong generator '%s' — expected scripts/record-cycle-time.sh\n" "$name" "$generator"
58
+ EXIT_CODE=1; return
59
+ fi
60
+
61
+ commit_range="$(echo "$fm" | $PYTHON -c "import json,sys; d=json.load(sys.stdin); print(d.get('commit_range',''))" 2>/dev/null)"
62
+ if ! git -C "$ROOT" log --oneline -1 "$commit_range" >/dev/null 2>&1; then
63
+ printf "${RED}FAIL${NC} %s: commit_range '%s' does not resolve\n" "$name" "$commit_range"
64
+ EXIT_CODE=1; return
65
+ fi
66
+
67
+ eff="$(echo "$fm" | $PYTHON -c "
68
+ import json,sys
69
+ d=json.load(sys.stdin)
70
+ e=d.get('effort',{}).get('effort_hours')
71
+ if e is None: sys.exit(1)
72
+ if e < 0: sys.exit(2)
73
+ print(e)
74
+ " 2>/dev/null)" || {
75
+ local rc=$?
76
+ if [ "$rc" -eq 1 ]; then printf "${RED}FAIL${NC} %s: effort.effort_hours missing or null\n" "$name"
77
+ else printf "${RED}FAIL${NC} %s: effort_hours out of bounds\n" "$name"; fi
78
+ EXIT_CODE=1; return
79
+ }
80
+ printf "${GREEN}PASS${NC} %s (source=%s, generator=%s, commit_range resolves)\n" "$name" "$source" "$generator"
81
+ }
82
+
83
+ validate_spec_migration() {
84
+ local file="$1" fm="$2" name="$3" errs=0 key val fp_count
85
+ for key in id title since_version order actions_needed; do
86
+ val="$(echo "$fm" | $PYTHON -c "import json,sys; d=json.load(sys.stdin); print(d.get('$key',''))" 2>/dev/null)"
87
+ if val_nullish_or_empty "$val"; then
88
+ printf "${RED}FAIL${NC} %s: missing required key '%s'\n" "$name" "$key"
89
+ errs=$((errs + 1))
90
+ fi
91
+ done
92
+ [ "$errs" -gt 0 ] && { EXIT_CODE=1; return; }
93
+
94
+ fp_count="$(echo "$fm" | $PYTHON -c "
95
+ import json,sys
96
+ d=json.load(sys.stdin)
97
+ fp=d.get('fingerprint',{}).get('any',[])
98
+ print(len(fp))
99
+ " 2>/dev/null)"
100
+ if [ "${fp_count:-0}" -eq 0 ]; then
101
+ printf "${RED}FAIL${NC} %s: fingerprint.any is empty — no detection criteria\n" "$name"
102
+ EXIT_CODE=1; return
103
+ fi
104
+
105
+ printf "${GREEN}PASS${NC} %s (actions=%s, fingerprint has %s check(s))\n" \
106
+ "$name" \
107
+ "$(echo "$fm" | $PYTHON -c "import json,sys; d=json.load(sys.stdin); print(len(d.get('actions_needed',[])))" 2>/dev/null)" \
108
+ "$fp_count"
109
+ }
110
+
111
+ validate_migration_registry() {
112
+ local file="$1" fm="$2" name="$3" count bad
113
+ count="$(echo "$fm" | $PYTHON -c "
114
+ import json,sys
115
+ d=json.load(sys.stdin)
116
+ print(len(d.get('migrations',[])))
117
+ " 2>/dev/null)"
118
+ if [ "${count:-0}" -eq 0 ]; then
119
+ printf "${RED}FAIL${NC} %s: migrations list is empty\n" "$name"
120
+ EXIT_CODE=1; return
121
+ fi
122
+
123
+ bad="$(echo "$fm" | $PYTHON -c "
124
+ import json,sys
125
+ d=json.load(sys.stdin)
126
+ for m in d.get('migrations',[]):
127
+ if not all(k in m for k in ('id','file','status')):
128
+ print(m.get('id','?')); sys.exit(1)
129
+ " 2>/dev/null)" || {
130
+ printf "${RED}FAIL${NC} %s: migration '%s' missing id/file/status\n" "$name" "${bad:-?}"
131
+ EXIT_CODE=1; return
132
+ }
133
+ printf "${GREEN}PASS${NC} %s (%s migrations indexed)\n" "$name" "$count"
134
+ }
135
+
136
+ validate_concept() {
137
+ local file="$1" fm="$2" name="$3" errs=0 key val ref_count cat
138
+ for key in id title category; do
139
+ val="$(echo "$fm" | $PYTHON -c "import json,sys; d=json.load(sys.stdin); print(d.get('$key',''))" 2>/dev/null)"
140
+ if val_nullish "$val"; then
141
+ printf "${RED}FAIL${NC} %s: missing required key '%s'\n" "$name" "$key"
142
+ errs=$((errs + 1))
143
+ fi
144
+ done
145
+ ref_count="$(echo "$fm" | $PYTHON -c "
146
+ import json,sys
147
+ d=json.load(sys.stdin)
148
+ r=d.get('references',[])
149
+ print(len(r) if isinstance(r,list) else 0)
150
+ " 2>/dev/null)"
151
+ if [ "${ref_count:-0}" -eq 0 ]; then
152
+ printf "${RED}FAIL${NC} %s: references[] is empty or missing\n" "$name"
153
+ errs=$((errs + 1))
154
+ fi
155
+ [ "$errs" -gt 0 ] && { EXIT_CODE=1; return; }
156
+ cat="$(echo "$fm" | $PYTHON -c "import json,sys; d=json.load(sys.stdin); print(d.get('category',''))" 2>/dev/null)"
157
+ printf "${GREEN}PASS${NC} %s (category=%s, %s reference(s))\n" "$name" "$cat" "$ref_count"
158
+ }
159
+
160
+ validate_verification_report() {
161
+ local file="$1" fm="$2" name="$3" errs=0 key val gs score rc
162
+ for key in score gate_status threshold total_pass total_fail generated_by; do
163
+ val="$(echo "$fm" | $PYTHON -c "import json,sys; d=json.load(sys.stdin); print(d.get('$key',''))" 2>/dev/null)"
164
+ if val_nullish "$val"; then
165
+ printf "${RED}FAIL${NC} %s: missing required key '%s'\n" "$name" "$key"
166
+ errs=$((errs + 1))
167
+ fi
168
+ done
169
+ [ "$errs" -gt 0 ] && { EXIT_CODE=1; return; }
170
+
171
+ gs="$(echo "$fm" | $PYTHON -c "import json,sys; d=json.load(sys.stdin); print(d.get('gate_status',''))" 2>/dev/null)"
172
+ if ! echo "pass fail concerns waived" | grep -qw "$gs"; then
173
+ printf "${RED}FAIL${NC} %s: invalid gate_status '%s' — must be pass|fail|concerns|waived\n" "$name" "$gs"
174
+ EXIT_CODE=1; return
175
+ fi
176
+
177
+ score="$(echo "$fm" | $PYTHON -c "
178
+ import json,sys
179
+ d=json.load(sys.stdin)
180
+ s=d.get('score')
181
+ if s is None or not isinstance(s,(int,float)):
182
+ sys.exit(1)
183
+ if s < 0 or s > 100:
184
+ sys.exit(2)
185
+ print(s)
186
+ " 2>/dev/null)" || {
187
+ rc=$?
188
+ if [ "$rc" -eq 1 ]; then printf "${RED}FAIL${NC} %s: score missing or not numeric\n" "$name"
189
+ else printf "${RED}FAIL${NC} %s: score out of bounds (0-100)\n" "$name"; fi
190
+ EXIT_CODE=1; return
191
+ }
192
+
193
+ printf "${GREEN}PASS${NC} %s (score=%.1f, gate=%s, threshold=%.1f)\n" \
194
+ "$name" "$score" "$gs" \
195
+ "$(echo "$fm" | $PYTHON -c "import json,sys; d=json.load(sys.stdin); print(d.get('threshold',0))" 2>/dev/null)"
196
+ }
197
+
198
+ validate_receipts() {
199
+ local f="$ROOT/specs/receipts.json"
200
+ if [ ! -f "$f" ]; then printf "${YELLOW}SKIP${NC} receipts.json not found\n"; return; fi
201
+ $PYTHON -c "
202
+ import json, sys
203
+ d=json.load(open('$f'))
204
+ vs={'measured','estimated','backfilled','absent'}
205
+ ss=('compliance','golden_suite','metrics','traceability')
206
+ errs=0
207
+ for sec in ss:
208
+ data=d.get(sec)
209
+ if data is None: continue
210
+ src=data.get('source','')
211
+ if not src: print(f'FAIL: receipts.json — {sec}: missing source tag'); errs+=1; continue
212
+ if src not in vs: print(f'FAIL: receipts.json — {sec}: unknown source \"{src}\"'); errs+=1; continue
213
+ hv='value' in data
214
+ if src=='absent' and hv: print(f'FAIL: receipts.json — {sec}: tagged absent but carries value'); errs+=1
215
+ elif src!='absent' and not hv: print(f'FAIL: receipts.json — {sec}: tagged {src} but missing value'); errs+=1
216
+ if errs==0: print('PASS receipts.json')
217
+ else: print(f'{errs} violation(s) in receipts.json')
218
+ sys.exit(errs)
219
+ " || { EXIT_CODE=1; return; }
220
+ }
221
+
222
+ validate_bundle() {
223
+ local file="$1" name fm fm_type kind
224
+ name="$(basename "$file")"
225
+ fm="$(okf_parse_frontmatter "$file")" || {
226
+ printf "${RED}FAIL${NC} %s: cannot parse YAML frontmatter\n" "$name"
227
+ EXIT_CODE=1
228
+ return
229
+ }
230
+
231
+ fm_type="$(echo "$fm" | $PYTHON -c "import json,sys; d=json.load(sys.stdin); print(d.get('type',''))" 2>/dev/null)"
232
+ if [ -z "$fm_type" ]; then
233
+ printf "${RED}FAIL${NC} %s: OKF v0.1 — missing required 'type' field\n" "$name"
234
+ EXIT_CODE=1
235
+ return
236
+ fi
237
+
238
+ if [ "$name" = "index.md" ] && [ "$fm_type" != "Index" ]; then
239
+ printf "${RED}FAIL${NC} %s: OKF v0.1 — index.md must have type: Index (got '%s')\n" "$name" "$fm_type"
240
+ EXIT_CODE=1
241
+ return
242
+ fi
243
+
244
+ kind="$(echo "$fm" | $PYTHON -c "import json,sys; d=json.load(sys.stdin); print(d.get('okf_kind',''))" 2>/dev/null)"
245
+ case "$kind" in
246
+ story-metrics) validate_story_metrics "$file" "$fm" "$name" ;;
247
+ spec-migration) validate_spec_migration "$file" "$fm" "$name" ;;
248
+ migration-registry) validate_migration_registry "$file" "$fm" "$name" ;;
249
+ concept) validate_concept "$file" "$fm" "$name" ;;
250
+ verification-report) validate_verification_report "$file" "$fm" "$name" ;;
251
+ "") printf "${YELLOW}SKIP${NC} %s: no okf_kind — not an OKF bundle\n" "$name" ;;
252
+ *) printf "${YELLOW}SKIP${NC} %s: unknown okf_kind '%s'\n" "$name" "$kind" ;;
253
+ esac
254
+ }
255
+
256
+ okf_scan_dir() {
257
+ local dir="$1" count=0 f
258
+ if [ ! -d "$dir" ]; then
259
+ printf "${RED}FAIL${NC} directory not found: %s\n" "$dir"
260
+ EXIT_CODE=1
261
+ return
262
+ fi
263
+ while IFS= read -r -d '' f; do
264
+ if head -1 "$f" 2>/dev/null | grep -q '^---$'; then
265
+ validate_bundle "$f"
266
+ count=$((count + 1))
267
+ fi
268
+ done < <(find "$dir" -maxdepth 1 -name '*.md' -print0 2>/dev/null || true)
269
+ if [ "$count" -eq 0 ]; then
270
+ printf "${YELLOW}SKIP${NC} no OKF bundles found in %s\n" "$dir"
271
+ fi
272
+ }