bigpowers 2.73.4 → 2.73.5
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.
- package/.pi/package.json +1 -1
- package/CHANGELOG.md +7 -0
- package/package.json +1 -1
- package/scripts/lib/golden-suite-agent.sh +80 -0
- package/scripts/lib/golden-suite-gates.sh +105 -0
- package/scripts/lib/golden-suite-report.sh +157 -0
- package/scripts/lib/golden-suite-run.sh +79 -0
- package/scripts/lib/validate-okf-kinds.sh +272 -0
- package/scripts/run-golden-suite.sh +4 -488
- package/scripts/run-verification-gates.sh +4 -488
- package/scripts/validate-okf.sh +2 -318
|
@@ -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
|
+
}
|