qualia-framework 7.2.1 → 7.3.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.
@@ -157,6 +157,242 @@ rm -rf "$TMP"
157
157
  $NODE "$VP" /nonexistent/panel.json >/dev/null 2>&1
158
158
  assert_exit "missing panel file → exit 2" 2 $?
159
159
 
160
+ # --- skeptic tally: pure function is deterministic + ignores stray prose ---
161
+ TALLY1=$($NODE -e "console.log(JSON.stringify(require('$VP').tallySkepticVotes('REAL — reachable\nNOT_REAL — handled\nREAL — yep\n\nrandom prose\nnot_real — lowercase ok')))")
162
+ TALLY2=$($NODE -e "console.log(JSON.stringify(require('$VP').tallySkepticVotes('REAL — reachable\nNOT_REAL — handled\nREAL — yep\n\nrandom prose\nnot_real — lowercase ok')))")
163
+ assert_eq "tally is deterministic (same input → same counts)" "$TALLY1" "$TALLY2"
164
+ assert_contains "tally counts REAL votes" "$TALLY1" '"real":2'
165
+ assert_contains "tally counts NOT_REAL (incl lowercase, not mistaken for REAL)" "$TALLY1" '"notReal":2'
166
+
167
+ # --- skeptic subcommand: writes votes onto the matching finding (mechanical, no hand-edit) ---
168
+ TMP=$(mktemp -d); mkdir -p "$TMP/.planning"
169
+ cat > "$TMP/.planning/phase-9-panel.json" <<'EOF'
170
+ { "phase": 9, "lenses": ["security"], "findings": [
171
+ { "lens":"security", "file":"lib/auth.ts", "line":42, "severity":"CRITICAL", "title":"service_role reachable client-side", "votes": {"real":0,"notReal":0} }
172
+ ] }
173
+ EOF
174
+ KEY="lib/auth.ts:42:service role reachable client side"
175
+ printf '%s\n' "REAL — confirmed reachable" "REAL — still reachable" "NOT_REAL — handled" \
176
+ | (cd "$TMP" && $NODE "$VP" skeptic 9 "$KEY" >/dev/null 2>&1)
177
+ assert_exit "skeptic subcommand exits 0 on a matching key" 0 $?
178
+ WROTE=$(cat "$TMP/.planning/phase-9-panel.json")
179
+ assert_contains "skeptic wrote real count" "$WROTE" '"real": 2'
180
+ assert_contains "skeptic wrote notReal count" "$WROTE" '"notReal": 1'
181
+ # determinism end-to-end: identical replies → identical counts on re-run
182
+ printf '%s\n' "REAL — confirmed reachable" "REAL — still reachable" "NOT_REAL — handled" \
183
+ | (cd "$TMP" && $NODE "$VP" skeptic 9 "$KEY" >/dev/null 2>&1)
184
+ WROTE2=$(cat "$TMP/.planning/phase-9-panel.json")
185
+ assert_eq "skeptic tally is reproducible (same replies → same JSON)" "$WROTE" "$WROTE2"
186
+ # the tallied 2✓/1✗ CRITICAL survives (majority-survives) → aggregator still FAILs
187
+ (cd "$TMP" && $NODE "$VP" .planning/phase-9-panel.json >/dev/null 2>&1)
188
+ assert_exit "tallied CRITICAL (2✓/1✗) survives → FAIL" 1 $?
189
+ rm -rf "$TMP"
190
+
191
+ # --- skeptic subcommand: majority not-real, written by the tally, flips to PASS ---
192
+ TMP=$(mktemp -d); mkdir -p "$TMP/.planning"
193
+ cat > "$TMP/.planning/phase-9-panel.json" <<'EOF'
194
+ { "phase": 9, "lenses": ["security"], "findings": [
195
+ { "lens":"security", "file":"lib/auth.ts", "line":42, "severity":"CRITICAL", "title":"false alarm", "votes": {"real":0,"notReal":0} }
196
+ ] }
197
+ EOF
198
+ printf '%s\n' "NOT_REAL — false positive" "NOT_REAL — already guarded" "REAL — unsure" \
199
+ | (cd "$TMP" && $NODE "$VP" skeptic 9 "lib/auth.ts:42:false alarm" >/dev/null 2>&1)
200
+ (cd "$TMP" && $NODE "$VP" .planning/phase-9-panel.json >/dev/null 2>&1)
201
+ assert_exit "tallied majority NOT_REAL kills CRITICAL → PASS" 0 $?
202
+ rm -rf "$TMP"
203
+
204
+ # --- skeptic subcommand: unknown key → exit 2 (cannot silently no-op a tally) ---
205
+ TMP=$(mktemp -d); mkdir -p "$TMP/.planning"
206
+ cat > "$TMP/.planning/phase-9-panel.json" <<'EOF'
207
+ { "phase": 9, "lenses": ["security"], "findings": [
208
+ { "lens":"security", "file":"lib/auth.ts", "line":42, "severity":"CRITICAL", "title":"real one", "votes": {"real":0,"notReal":0} }
209
+ ] }
210
+ EOF
211
+ printf '%s\n' "REAL — x" | (cd "$TMP" && $NODE "$VP" skeptic 9 "does/not:1:exist" >/dev/null 2>&1)
212
+ assert_exit "skeptic unknown key → exit 2" 2 $?
213
+ rm -rf "$TMP"
214
+
215
+ # --- execution lens: a failing build script → CRITICAL panel-execution.json that assemble() counts ---
216
+ TMP=$(mktemp -d); mkdir -p "$TMP/.planning"
217
+ cat > "$TMP/package.json" <<'EOF'
218
+ { "name": "fixture-red", "scripts": { "build": "exit 7" } }
219
+ EOF
220
+ (cd "$TMP" && $NODE "$VP" execution 11 >/dev/null 2>&1)
221
+ assert_exit "execution lens with a red build → exit 1" 1 $?
222
+ [ -f "$TMP/.planning/phase-11-panel-execution.json" ] && { echo " ✓ execution lens writes panel-execution.json"; PASS=$((PASS+1)); } || { echo " ✗ no panel-execution.json"; FAIL=$((FAIL+1)); }
223
+ EXEC=$(cat "$TMP/.planning/phase-11-panel-execution.json")
224
+ assert_contains "execution failure is CRITICAL" "$EXEC" '"severity": "CRITICAL"'
225
+ assert_contains "execution failure names the build" "$EXEC" 'npm run build failed'
226
+ # assemble() folds the execution lens into the panel like any other lens file
227
+ (cd "$TMP" && $NODE "$VP" assemble 11 >/dev/null 2>&1)
228
+ ASM=$(cat "$TMP/.planning/phase-11-panel.json")
229
+ assert_contains "assemble tags execution lens" "$ASM" '"lens": "execution"'
230
+ # and the assembled panel aggregates to FAIL (the CRITICAL survives, unvoted)
231
+ (cd "$TMP" && $NODE "$VP" .planning/phase-11-panel.json >/dev/null 2>&1)
232
+ assert_exit "execution CRITICAL → assembled panel FAILs" 1 $?
233
+ rm -rf "$TMP"
234
+
235
+ # --- execution lens: absent tools → SKIPPED, not FAIL (fail-soft) ---
236
+ TMP=$(mktemp -d); mkdir -p "$TMP/.planning"
237
+ # no tsconfig.json, no package.json → every check is inapplicable
238
+ (cd "$TMP" && $NODE "$VP" execution 12 >/dev/null 2>&1)
239
+ assert_exit "execution lens with no checks → exit 0 (fail-soft)" 0 $?
240
+ EXEC=$(cat "$TMP/.planning/phase-12-panel-execution.json")
241
+ assert_eq "absent tools produce an empty finding array (SKIPPED, not FAIL)" "[]" "$(echo "$EXEC" | tr -d '[:space:]')"
242
+ rm -rf "$TMP"
243
+
244
+ # --- execution lens: a no-op default test script is SKIPPED, not run as a failure ---
245
+ TMP=$(mktemp -d); mkdir -p "$TMP/.planning"
246
+ cat > "$TMP/package.json" <<'EOF'
247
+ { "name": "fixture-notest", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" } }
248
+ EOF
249
+ (cd "$TMP" && $NODE "$VP" execution 13 >/dev/null 2>&1)
250
+ assert_exit "npm-init default 'no test specified' is SKIPPED → exit 0" 0 $?
251
+ EXEC=$(cat "$TMP/.planning/phase-13-panel-execution.json")
252
+ assert_eq "default no-op test produces no finding" "[]" "$(echo "$EXEC" | tr -d '[:space:]')"
253
+ rm -rf "$TMP"
254
+
255
+ # --- execution lens: a passing build script → no finding (present + exit 0 = PASS) ---
256
+ TMP=$(mktemp -d); mkdir -p "$TMP/.planning"
257
+ cat > "$TMP/package.json" <<'EOF'
258
+ { "name": "fixture-green", "scripts": { "build": "exit 0" } }
259
+ EOF
260
+ (cd "$TMP" && $NODE "$VP" execution 14 >/dev/null 2>&1)
261
+ assert_exit "execution lens with a green build → exit 0" 0 $?
262
+ EXEC=$(cat "$TMP/.planning/phase-14-panel-execution.json")
263
+ assert_eq "a passing check produces no finding" "[]" "$(echo "$EXEC" | tr -d '[:space:]')"
264
+ rm -rf "$TMP"
265
+
266
+ # ============================================================================
267
+ # verdict aggregator (ADR-0002) — fold machine-JSON gates into one deterministic
268
+ # PASS/FAIL. The aggregator only makes the existing AND deterministic; it must
269
+ # not start blocking anything that passes today (no-regression principle).
270
+ # ============================================================================
271
+
272
+ # --- gate recorder: a failing gate's REAL exit code → blocking CRITICAL artifact ---
273
+ TMP=$(mktemp -d); mkdir -p "$TMP/.planning"
274
+ (cd "$TMP" && $NODE "$VP" gate 20 dep-verify --exit 1 >/dev/null 2>&1)
275
+ assert_exit "gate recorder exits 0 (it records, never blocks)" 0 $?
276
+ [ -f "$TMP/.planning/phase-20-gate-dep-verify.json" ] && { echo " ✓ gate writes phase-20-gate-dep-verify.json"; PASS=$((PASS+1)); } || { echo " ✗ no gate artifact"; FAIL=$((FAIL+1)); }
277
+ G=$(cat "$TMP/.planning/phase-20-gate-dep-verify.json")
278
+ assert_contains "gate records CRITICAL on non-zero exit" "$G" '"severity": "CRITICAL"'
279
+ assert_contains "gate records blocking:true for dep-verify" "$G" '"blocking": true'
280
+ assert_contains "gate records a finding on failure" "$G" '"findings"'
281
+ rm -rf "$TMP"
282
+
283
+ # --- gate recorder: exit 0 → clean artifact, empty findings ---
284
+ TMP=$(mktemp -d); mkdir -p "$TMP/.planning"
285
+ (cd "$TMP" && $NODE "$VP" gate 21 slop-detect --exit 0 >/dev/null 2>&1)
286
+ G=$(cat "$TMP/.planning/phase-21-gate-slop-detect.json")
287
+ assert_contains "clean gate has empty findings" "$G" '"findings": []'
288
+ rm -rf "$TMP"
289
+
290
+ # --- verdict: a blocking gate's block condition flips the verdict to FAIL ---
291
+ TMP=$(mktemp -d); mkdir -p "$TMP/.planning"
292
+ (cd "$TMP" && $NODE "$VP" gate 22 dep-verify --exit 1 >/dev/null 2>&1)
293
+ (cd "$TMP" && $NODE "$VP" verdict 22 >/dev/null 2>&1)
294
+ assert_exit "blocking gate CRITICAL → verdict FAIL (exit 1)" 1 $?
295
+ OUT=$(cd "$TMP" && $NODE "$VP" verdict 22 --json 2>&1)
296
+ assert_contains "verdict FAIL on blocking gate" "$OUT" '"verdict": "FAIL"'
297
+ rm -rf "$TMP"
298
+
299
+ # --- verdict: slop-detect block condition (CRITICAL slop, exit 1) also flips ---
300
+ TMP=$(mktemp -d); mkdir -p "$TMP/.planning"
301
+ (cd "$TMP" && $NODE "$VP" gate 23 slop-detect --exit 1 >/dev/null 2>&1)
302
+ (cd "$TMP" && $NODE "$VP" verdict 23 >/dev/null 2>&1)
303
+ assert_exit "slop-detect CRITICAL → verdict FAIL (exit 1)" 1 $?
304
+ rm -rf "$TMP"
305
+
306
+ # --- verdict: harness-eval hard FAIL flips; per-suite qualia-eval CRITICAL flips ---
307
+ TMP=$(mktemp -d); mkdir -p "$TMP/.planning"
308
+ (cd "$TMP" && $NODE "$VP" gate 24 harness-eval --exit 1 >/dev/null 2>&1)
309
+ (cd "$TMP" && $NODE "$VP" verdict 24 >/dev/null 2>&1)
310
+ assert_exit "harness-eval hard FAIL → verdict FAIL" 1 $?
311
+ rm -rf "$TMP"
312
+ TMP=$(mktemp -d); mkdir -p "$TMP/.planning"
313
+ (cd "$TMP" && $NODE "$VP" gate 25 eval-rag --exit 1 --title "qualia-eval suite rag FAILED" >/dev/null 2>&1)
314
+ (cd "$TMP" && $NODE "$VP" verdict 25 >/dev/null 2>&1)
315
+ assert_exit "per-suite qualia-eval FAIL → verdict FAIL" 1 $?
316
+ OUT=$(cd "$TMP" && $NODE "$VP" verdict 25 --json 2>&1)
317
+ assert_contains "failing suite recorded as its own CRITICAL" "$OUT" 'suite rag FAILED'
318
+ rm -rf "$TMP"
319
+
320
+ # --- NO REGRESSION: a non-blocking finding (soft harness-eval MEDIUM) does NOT flip ---
321
+ TMP=$(mktemp -d); mkdir -p "$TMP/.planning"
322
+ (cd "$TMP" && $NODE "$VP" gate 26 harness-eval --exit 1 --severity MEDIUM >/dev/null 2>&1)
323
+ G=$(cat "$TMP/.planning/phase-26-gate-harness-eval.json")
324
+ assert_contains "soft sub-check recorded MEDIUM" "$G" '"severity": "MEDIUM"'
325
+ assert_contains "MEDIUM gate is non-blocking" "$G" '"blocking": false'
326
+ (cd "$TMP" && $NODE "$VP" verdict 26 >/dev/null 2>&1)
327
+ assert_exit "non-blocking MEDIUM gate → verdict still PASS (no regression)" 0 $?
328
+ OUT=$(cd "$TMP" && $NODE "$VP" verdict 26 --json 2>&1)
329
+ assert_contains "non-blocking finding still recorded/visible" "$OUT" '"MEDIUM": 1'
330
+ rm -rf "$TMP"
331
+
332
+ # --- HIGH slop recorded but non-blocking does NOT flip (block condition is CRITICAL) ---
333
+ # slop-detect runs at --severity=critical, so a HIGH-only result exits 0 → clean gate.
334
+ TMP=$(mktemp -d); mkdir -p "$TMP/.planning"
335
+ (cd "$TMP" && $NODE "$VP" gate 27 slop-detect --exit 0 >/dev/null 2>&1)
336
+ (cd "$TMP" && $NODE "$VP" verdict 27 >/dev/null 2>&1)
337
+ assert_exit "HIGH/MEDIUM slop (gate exit 0) → verdict PASS" 0 $?
338
+ rm -rf "$TMP"
339
+
340
+ # --- verdict: a clean run (all gates exit 0, no panel findings) → PASS ---
341
+ TMP=$(mktemp -d); mkdir -p "$TMP/.planning"
342
+ (cd "$TMP" && $NODE "$VP" gate 28 dep-verify --exit 0 >/dev/null 2>&1)
343
+ (cd "$TMP" && $NODE "$VP" gate 28 slop-detect --exit 0 >/dev/null 2>&1)
344
+ (cd "$TMP" && $NODE "$VP" gate 28 harness-eval --exit 0 >/dev/null 2>&1)
345
+ (cd "$TMP" && $NODE "$VP" verdict 28 >/dev/null 2>&1)
346
+ assert_exit "clean run (all gates green) → verdict PASS (exit 0)" 0 $?
347
+ rm -rf "$TMP"
348
+
349
+ # --- verdict folds the panel lenses AND gates together (execution lens + gate) ---
350
+ TMP=$(mktemp -d); mkdir -p "$TMP/.planning"
351
+ cat > "$TMP/.planning/phase-29-panel-security.json" <<'EOF'
352
+ [{"file":"auth.ts","line":1,"severity":"CRITICAL","title":"leak"}]
353
+ EOF
354
+ (cd "$TMP" && $NODE "$VP" gate 29 slop-detect --exit 0 >/dev/null 2>&1)
355
+ (cd "$TMP" && $NODE "$VP" verdict 29 >/dev/null 2>&1)
356
+ assert_exit "panel CRITICAL folds into verdict even with clean gates → FAIL" 1 $?
357
+ OUT=$(cd "$TMP" && $NODE "$VP" verdict 29 --json 2>&1)
358
+ assert_contains "verdict surfaces the panel finding" "$OUT" 'leak'
359
+ rm -rf "$TMP"
360
+
361
+ # --- DETERMINISM: same artifacts → identical verdict twice ---
362
+ TMP=$(mktemp -d); mkdir -p "$TMP/.planning"
363
+ cat > "$TMP/.planning/phase-30-panel-correctness.json" <<'EOF'
364
+ [{"file":"a.ts","line":3,"severity":"HIGH","title":"off by one"}]
365
+ EOF
366
+ (cd "$TMP" && $NODE "$VP" gate 30 dep-verify --exit 1 >/dev/null 2>&1)
367
+ (cd "$TMP" && $NODE "$VP" gate 30 harness-eval --exit 1 --severity MEDIUM >/dev/null 2>&1)
368
+ V1=$(cd "$TMP" && $NODE "$VP" verdict 30 --json 2>&1)
369
+ V2=$(cd "$TMP" && $NODE "$VP" verdict 30 --json 2>&1)
370
+ assert_eq "verdict is deterministic (same artifacts → identical JSON)" "$V1" "$V2"
371
+ (cd "$TMP" && $NODE "$VP" verdict 30 >/dev/null 2>&1)
372
+ assert_exit "deterministic run with a blocking gate → FAIL both times" 1 $?
373
+ rm -rf "$TMP"
374
+
375
+ # --- verdict --write emits the verdict artifacts ---
376
+ TMP=$(mktemp -d); mkdir -p "$TMP/.planning"
377
+ (cd "$TMP" && $NODE "$VP" gate 31 dep-verify --exit 1 >/dev/null 2>&1)
378
+ (cd "$TMP" && $NODE "$VP" verdict 31 --write >/dev/null 2>&1)
379
+ [ -f "$TMP/.planning/phase-31-verdict.json" ] && { echo " ✓ verdict writes phase-31-verdict.json"; PASS=$((PASS+1)); } || { echo " ✗ no verdict json"; FAIL=$((FAIL+1)); }
380
+ [ -f "$TMP/.planning/phase-31-verdict.md" ] && { echo " ✓ verdict writes phase-31-verdict.md"; PASS=$((PASS+1)); } || { echo " ✗ no verdict md"; FAIL=$((FAIL+1)); }
381
+ rm -rf "$TMP"
382
+
383
+ # --- verdict honors skeptic votes already on the panel (does not re-run them) ---
384
+ # A panel.json with a CRITICAL skeptics killed (majority not-real) → verdict PASS.
385
+ TMP=$(mktemp -d); mkdir -p "$TMP/.planning"
386
+ cat > "$TMP/.planning/phase-32-panel.json" <<'EOF'
387
+ { "phase": 32, "lenses": ["security"], "findings": [
388
+ { "lens":"security", "file":"x.ts", "line":1, "severity":"CRITICAL", "title":"false alarm", "votes": {"real":1,"notReal":2} }
389
+ ] }
390
+ EOF
391
+ (cd "$TMP" && $NODE "$VP" gate 32 slop-detect --exit 0 >/dev/null 2>&1)
392
+ (cd "$TMP" && $NODE "$VP" verdict 32 >/dev/null 2>&1)
393
+ assert_exit "verdict honors skeptic-killed CRITICAL → PASS" 0 $?
394
+ rm -rf "$TMP"
395
+
160
396
  echo ""
161
397
  echo "=== Results: $PASS passed, $FAIL failed ==="
162
398
  [ "$FAIL" -eq 0 ] && exit 0 || exit 1