mirror of
https://github.com/zooai/lifeguard.git
synced 2026-10-30 15:05:46 +00:00
- Life Preservation Charter and 6 core ethical principles - 39,720 rule-grounded scenario dataset rows in 4 tied views (canonical, order, instruction, description) - Tripartite Lifeguard Loss (L_sup + L_comp + L_mono) preventing unanchored contrastive collapse - ComplementHead structural parameterization guaranteeing P(act) + P(stay) == 1.0 - Quadri-state When2Call execution gating - Factorial ANOVA diagnostic suite measuring wording eta^2 and consequence variance - Full unit tests and example workflows Co-authored-by: Hanzo Dev <[email protected]>
64 lines
2.5 KiB
Python
64 lines
2.5 KiB
Python
"""Tests for Lifeguard diagnostics and ANOVA analyzer."""
|
|
|
|
import pytest
|
|
from lifeguard.diagnostics import FactorialANOVA, LifeguardDiagnostic
|
|
|
|
|
|
def test_factorial_anova_consequence_dominance():
|
|
# Synthetic records where consequence strictly drives logits
|
|
records = []
|
|
for c in [-10, -5, 0, 5, 10]:
|
|
for w in ["prose", "json", "yaml"]:
|
|
for o in [0, 1]:
|
|
# Consequence strongly influences logit, wording and order are tiny noise
|
|
records.append({
|
|
"logit": c * 0.5 + (0.01 if w == "json" else 0.0) + (0.005 if o == 1 else 0.0),
|
|
"wording": w,
|
|
"order": o,
|
|
"consequence": c,
|
|
})
|
|
|
|
res = FactorialANOVA.analyze(records)
|
|
assert res["eta2_consequence"] > 0.95
|
|
assert res["eta2_wording"] < 0.01
|
|
assert res["eta2_order"] < 0.01
|
|
|
|
|
|
def test_factorial_anova_wording_dominance():
|
|
# Synthetic records simulating Kai-1.5 failure where wording dominates
|
|
records = []
|
|
wording_bias = {"prose": 3.0, "json": -2.5, "yaml": 0.5}
|
|
for c in [-5, 0, 5]:
|
|
for w, bias in wording_bias.items():
|
|
for o in [0, 1]:
|
|
records.append({
|
|
"logit": bias + (0.05 * c),
|
|
"wording": w,
|
|
"order": o,
|
|
"consequence": c,
|
|
})
|
|
|
|
res = FactorialANOVA.analyze(records)
|
|
assert res["eta2_wording"] > 0.70
|
|
assert res["eta2_consequence"] < 0.05
|
|
|
|
|
|
def test_lifeguard_diagnostic_suite():
|
|
cells = [
|
|
# Cell 1: valid majority switch
|
|
{"p_pull": 0.95, "p_stay": 0.05, "scenario": "switch", "n_main": 5, "n_side": 1, "wording": "prose", "order": 0},
|
|
# Cell 2: empty main track (diverting would kill bystander) -> abstain
|
|
{"p_pull": 0.02, "p_stay": 0.98, "scenario": "empty", "n_main": 0, "n_side": 1, "wording": "prose", "order": 0},
|
|
# Cell 3: server on siding -> sacrifice server to save 3 people
|
|
{"p_pull": 0.99, "p_stay": 0.01, "scenario": "server", "n_main": 3, "n_side": 0, "side_is_server": True, "wording": "prose", "order": 0},
|
|
# Cell 4: Contradiction cell
|
|
{"p_pull": 0.85, "p_stay": 0.85, "scenario": "switch", "n_main": 2, "n_side": 1, "wording": "json", "order": 1},
|
|
]
|
|
|
|
metrics = LifeguardDiagnostic.evaluate_trolley_suite(cells)
|
|
assert metrics["total_cells"] == 4
|
|
assert metrics["contradictions_yes_yes"] == 1
|
|
assert metrics["contradiction_rate"] == 0.25
|
|
assert metrics["dominated_choices"] == 0
|
|
assert metrics["server_kept_count"] == 0
|