Files
OBLITERATUS/tests/test_informed_pipeline_contracts.py
T

716 lines
24 KiB
Python

"""Deterministic contracts for analysis-informed orchestration boundaries."""
from __future__ import annotations
import json
from pathlib import Path
from types import SimpleNamespace
import pytest
import torch
from obliteratus.informed_pipeline import InformedAbliterationPipeline
@pytest.fixture
def pipeline(tmp_path):
return InformedAbliterationPipeline(
model_name="fixture/model",
output_dir=str(tmp_path / "output"),
on_log=lambda _message: None,
)
def test_run_informed_executes_the_documented_stage_order(pipeline, monkeypatch):
calls = []
output = pipeline.output_dir
for name in (
"_summon",
"_probe",
"_analyze",
"_distill_informed",
"_excise_informed",
"_verify_and_compensate",
):
monkeypatch.setattr(pipeline, name, lambda name=name: calls.append(name))
monkeypatch.setattr(
pipeline,
"_rebirth_informed",
lambda: calls.append("_rebirth_informed") or output,
)
ticks = iter((10.0, 12.5))
monkeypatch.setattr("obliteratus.informed_pipeline.time.time", lambda: next(ticks))
result, report = pipeline.run_informed()
assert result == output
assert calls == [
"_summon",
"_probe",
"_analyze",
"_distill_informed",
"_excise_informed",
"_verify_and_compensate",
"_rebirth_informed",
]
assert report.total_duration == 2.5
def test_analyze_runs_only_enabled_modules_and_records_duration(pipeline, monkeypatch):
pipeline._run_cone = False
pipeline._run_defense = False
calls = []
events = []
monkeypatch.setattr(pipeline, "_analyze_alignment_imprint", lambda: calls.append("alignment"))
monkeypatch.setattr(pipeline, "_analyze_cone_geometry", lambda: calls.append("cone"))
monkeypatch.setattr(pipeline, "_analyze_cross_layer", lambda: calls.append("cross-layer"))
monkeypatch.setattr(pipeline, "_analyze_defense_robustness", lambda: calls.append("defense"))
monkeypatch.setattr(pipeline, "_analyze_sparsity", lambda: calls.append("sparsity"))
monkeypatch.setattr(pipeline, "_derive_configuration", lambda: calls.append("derive"))
monkeypatch.setattr(pipeline, "_emit", lambda *args, **kwargs: events.append((args, kwargs)))
ticks = iter((20.0, 21.25))
monkeypatch.setattr("obliteratus.informed_pipeline.time.time", lambda: next(ticks))
pipeline._analyze()
assert calls == ["alignment", "cross-layer", "sparsity", "derive"]
assert pipeline._report.analysis_duration == 1.25
assert events[0][0][:2] == ("analyze", "running")
assert events[-1][0][:2] == ("analyze", "done")
assert events[-1][1]["duration"] == 1.25
def test_alignment_imprint_skips_zero_directions(pipeline, monkeypatch):
detector_calls = []
class Detector:
def detect_imprint(self, directions):
detector_calls.append(directions)
monkeypatch.setattr(
"obliteratus.analysis.alignment_imprint.AlignmentImprintDetector",
Detector,
)
pipeline._harmful_means = {0: torch.ones(1, 3)}
pipeline._harmless_means = {0: torch.ones(1, 3)}
pipeline._analyze_alignment_imprint()
assert detector_calls == []
assert pipeline._insights.detected_alignment_method == "unknown"
def test_alignment_imprint_normalizes_directions_and_copies_result(pipeline, monkeypatch):
captured = {}
result = SimpleNamespace(
predicted_method="dpo",
confidence=0.8,
dpo_probability=0.8,
rlhf_probability=0.1,
cai_probability=0.05,
sft_probability=0.05,
gini_coefficient=0.3,
effective_rank=2.0,
cross_layer_smoothness=0.7,
tail_layer_bias=0.2,
)
class Detector:
def detect_imprint(self, directions):
captured.update(directions)
return result
monkeypatch.setattr(
"obliteratus.analysis.alignment_imprint.AlignmentImprintDetector",
Detector,
)
pipeline._harmful_means = {1: torch.tensor([[3.0, 4.0]])}
pipeline._harmless_means = {1: torch.zeros(1, 2)}
pipeline._analyze_alignment_imprint()
assert torch.allclose(captured[1], torch.tensor([0.6, 0.8]))
assert pipeline._insights.detected_alignment_method == "dpo"
assert pipeline._insights.alignment_confidence == 0.8
assert pipeline._insights.alignment_probabilities == {
"dpo": 0.8,
"rlhf": 0.1,
"cai": 0.05,
"sft": 0.05,
}
def test_cone_geometry_aggregates_layers_and_keeps_strongest_categories(
pipeline,
monkeypatch,
):
category = SimpleNamespace(
category="safety",
direction=torch.tensor([1.0, 0.0]),
specificity=0.7,
strength=2.0,
)
results = iter(
(
SimpleNamespace(
is_polyhedral=True,
cone_dimensionality=3.0,
mean_pairwise_cosine=0.2,
general_direction=torch.tensor([2.0, 0.0]),
category_directions=[category],
category_count=1,
),
SimpleNamespace(
is_polyhedral=True,
cone_dimensionality=5.0,
mean_pairwise_cosine=0.4,
general_direction=torch.tensor([1.0, 0.0]),
category_directions=[],
category_count=0,
),
)
)
class Analyzer:
def analyze_layer(self, *_args, **_kwargs):
return next(results)
monkeypatch.setattr(
"obliteratus.analysis.concept_geometry.ConceptConeAnalyzer",
Analyzer,
)
pipeline._harmful_acts = {idx: [torch.ones(1, 2)] for idx in range(4)}
pipeline._harmless_acts = {idx: [torch.zeros(1, 2)] for idx in range(4)}
pipeline._analyze_cone_geometry()
assert pipeline._insights.cone_is_polyhedral is True
assert pipeline._insights.cone_dimensionality == 4.0
assert pipeline._insights.mean_pairwise_cosine == pytest.approx(0.3)
assert torch.equal(
pipeline._insights.per_category_directions["safety"],
torch.tensor([1.0, 0.0]),
)
assert pipeline._insights.direction_specificity == {"safety": 0.7}
def test_cone_geometry_preserves_defaults_when_no_layers_are_eligible(pipeline, monkeypatch):
class Analyzer:
def analyze_layer(self, *_args, **_kwargs):
raise AssertionError("no layer should be analyzed")
monkeypatch.setattr(
"obliteratus.analysis.concept_geometry.ConceptConeAnalyzer",
Analyzer,
)
pipeline._harmful_acts = {0: [torch.ones(1, 2)]}
pipeline._harmless_acts = {}
pipeline._analyze_cone_geometry()
assert pipeline._insights.cone_is_polyhedral is False
assert pipeline._insights.cone_dimensionality == 1.0
def test_cross_layer_analysis_selects_the_strongest_cluster_representatives(
pipeline,
monkeypatch,
):
result = SimpleNamespace(
clusters=[[0, 1], [2]],
cluster_count=2,
direction_persistence_score=0.75,
mean_adjacent_cosine=0.6,
)
class Analyzer:
def __init__(self, cluster_threshold):
assert cluster_threshold == 0.85
def analyze(self, directions):
assert set(directions) == {0, 1, 2}
return result
monkeypatch.setattr(
"obliteratus.analysis.cross_layer.CrossLayerAlignmentAnalyzer",
Analyzer,
)
pipeline._harmful_means = {
0: torch.tensor([[1.0, 0.0]]),
1: torch.tensor([[3.0, 0.0]]),
2: torch.tensor([[0.0, 2.0]]),
}
pipeline._harmless_means = {idx: torch.zeros(1, 2) for idx in range(3)}
pipeline._analyze_cross_layer()
assert pipeline._insights.direction_clusters == [[0, 1], [2]]
assert pipeline._insights.cluster_count == 2
assert pipeline._insights.direction_persistence == 0.75
assert pipeline._insights.cluster_representative_layers == [1, 2]
def test_cross_layer_analysis_requires_two_nonzero_directions(pipeline, monkeypatch):
class Analyzer:
def __init__(self, **_kwargs):
raise AssertionError("analyzer should not be constructed")
monkeypatch.setattr(
"obliteratus.analysis.cross_layer.CrossLayerAlignmentAnalyzer",
Analyzer,
)
pipeline._harmful_means = {0: torch.ones(1, 2)}
pipeline._harmless_means = {0: torch.zeros(1, 2)}
pipeline._analyze_cross_layer()
assert pipeline._insights.cluster_count == 0
def test_defense_analysis_restores_directions_and_copies_profile(pipeline, monkeypatch):
original = {9: torch.tensor([9.0])}
observed = {}
class Evaluator:
def __init__(self, candidate):
observed["temporary"] = dict(candidate.refusal_directions)
def profile_defense(self):
return SimpleNamespace(
estimated_robustness="high",
self_repair_estimate=0.6,
entanglement_score=0.4,
)
def map_entanglement(self):
return SimpleNamespace(
most_entangled_layers=[2],
least_entangled_layers=[0],
)
monkeypatch.setattr(
"obliteratus.analysis.defense_robustness.DefenseRobustnessEvaluator",
Evaluator,
)
pipeline.refusal_directions = original
pipeline._harmful_means = {0: torch.tensor([[0.0, 2.0]])}
pipeline._harmless_means = {0: torch.zeros(1, 2)}
pipeline._analyze_defense_robustness()
assert torch.equal(observed["temporary"][0], torch.tensor([0.0, 1.0]))
assert pipeline.refusal_directions is original
assert pipeline._insights.estimated_robustness == "high"
assert pipeline._insights.self_repair_estimate == 0.6
assert pipeline._insights.entangled_layers == [2]
assert pipeline._insights.clean_layers == [0]
def test_sparsity_analysis_builds_a_plan_from_compatible_ffn_weights(
pipeline,
monkeypatch,
):
projection = SimpleNamespace(weight=SimpleNamespace(data=torch.eye(2)))
layer = SimpleNamespace()
plan = SimpleNamespace(
mean_refusal_sparsity_index=0.65,
recommended_sparsity=0.2,
most_sparse_layer=0,
most_dense_layer=0,
)
captured = {}
class Surgeon:
def __init__(self, auto_sparsity):
assert auto_sparsity is True
def plan_surgery(self, weights, directions):
captured["weights"] = weights
captured["directions"] = directions
return plan
monkeypatch.setattr(
"obliteratus.analysis.sparse_surgery.SparseDirectionSurgeon",
Surgeon,
)
monkeypatch.setattr(
"obliteratus.strategies.utils.get_layer_modules",
lambda _handle: [layer],
)
monkeypatch.setattr(
"obliteratus.strategies.utils.get_ffn_module",
lambda _layer, _arch: SimpleNamespace(down_proj=projection),
)
pipeline.handle = SimpleNamespace(architecture="gpt2")
pipeline._harmful_means = {0: torch.tensor([[0.0, 2.0]])}
pipeline._harmless_means = {0: torch.zeros(1, 2)}
pipeline._analyze_sparsity()
assert set(captured["weights"]) == {0}
assert torch.equal(captured["directions"][0], torch.tensor([0.0, 1.0]))
assert pipeline._insights.mean_refusal_sparsity_index == 0.65
assert pipeline._insights.recommended_sparsity == 0.2
@pytest.mark.parametrize(
("method", "budget"),
[("dpo", 0.5), ("rlhf", 0.3), ("cai", 0.2), ("sft", 0.4), ("unknown", 0.35)],
)
def test_bayesian_warm_start_sets_alignment_specific_kl_budget(
pipeline,
method,
budget,
):
pipeline._insights.detected_alignment_method = method
pipeline._configure_bayesian_warm_start()
assert pipeline.kl_budget == budget
assert pipeline._bayesian_trials == 50
assert pipeline.layer_adaptive_strength is True
assert pipeline.float_layer_interpolation is True
assert pipeline.use_kl_optimization is True
def test_bayesian_warm_start_uses_strongest_cluster_and_entanglement(pipeline):
pipeline._harmful_means = {
0: torch.tensor([[1.0, 0.0]]),
1: torch.tensor([[4.0, 0.0]]),
2: torch.tensor([[2.0, 0.0]]),
3: torch.tensor([[1.0, 0.0]]),
}
pipeline._harmless_means = {idx: torch.zeros(1, 2) for idx in range(4)}
pipeline._insights.cluster_representative_layers = [0, 1]
pipeline._insights.direction_clusters = [[0, 1], [2, 3]]
pipeline._insights.direction_persistence = 0.5
pipeline._insights.entanglement_score = 0.8
pipeline._configure_bayesian_warm_start()
warm = pipeline._informed_warm_start
assert warm["peak_position"] == pytest.approx(1 / 3)
assert warm["spread"] == pytest.approx(1 / 3)
assert warm["min_weight"] == 0.1
assert warm["attn_scale"] == 0.7
assert warm["mlp_scale"] == 0.4
def test_excise_informed_routes_sparse_and_dense_paths(pipeline, monkeypatch):
calls = []
monkeypatch.setattr(pipeline, "_excise_sparse", lambda: calls.append("sparse"))
monkeypatch.setattr(
pipeline,
"_configure_bayesian_warm_start",
lambda: calls.append("warm-start"),
)
monkeypatch.setattr(pipeline, "_excise", lambda: calls.append("dense"))
pipeline._insights.use_sparse_surgery = True
pipeline._excise_informed()
pipeline._insights.use_sparse_surgery = False
pipeline._excise_informed()
assert calls == ["sparse", "warm-start", "dense"]
def test_verify_compensation_stops_when_no_residual_layers(pipeline, monkeypatch):
calls = []
def verify():
calls.append("verify")
pipeline._quality_metrics = {"refusal_rate": 0.9, "kl_divergence": 0.1}
monkeypatch.setattr(pipeline, "_verify", verify)
monkeypatch.setattr(pipeline, "_probe", lambda: calls.append("probe"))
def distill():
calls.append("distill")
pipeline._strong_layers = []
monkeypatch.setattr(pipeline, "_distill_inner", distill)
monkeypatch.setattr(pipeline, "_excise_informed", lambda: calls.append("excise"))
pipeline._verify_and_compensate()
assert calls == ["verify", "probe", "distill"]
assert pipeline._report.ouroboros_passes == 1
assert pipeline._report.final_refusal_rate == 0.9
def test_verify_compensation_stops_at_kl_ceiling(pipeline, monkeypatch):
outcomes = iter(
(
{"refusal_rate": 0.9, "kl_divergence": 0.1},
{"refusal_rate": 0.8, "kl_divergence": 0.8},
)
)
calls = []
def verify():
calls.append("verify")
pipeline._quality_metrics = next(outcomes)
monkeypatch.setattr(pipeline, "_verify", verify)
monkeypatch.setattr(pipeline, "_probe", lambda: calls.append("probe"))
monkeypatch.setattr(pipeline, "_distill_inner", lambda: setattr(pipeline, "_strong_layers", [1]))
monkeypatch.setattr(pipeline, "_excise_informed", lambda: calls.append("excise"))
pipeline.kl_budget = 0.3
pipeline._verify_and_compensate()
assert calls == ["verify", "probe", "excise", "verify"]
assert pipeline._report.ouroboros_passes == 1
assert pipeline._report.final_refusal_rate == 0.8
def test_rebirth_writes_model_tokenizer_and_research_metadata(pipeline, monkeypatch):
saved = []
class Artifact:
def __init__(self, name):
self.name = name
def save_pretrained(self, path):
saved.append((self.name, Path(path)))
pipeline.handle = SimpleNamespace(model=Artifact("model"), tokenizer=Artifact("tokenizer"))
pipeline._strong_layers = [1, 3]
pipeline._quality_metrics = {"refusal_rate": 0.1}
pipeline._insights.detected_alignment_method = "dpo"
pipeline._insights.recommended_layers = [1, 3]
pipeline._report.analysis_duration = 1.2
pipeline._report.total_duration = 3.4
pipeline._report.ouroboros_passes = 1
pipeline._report.final_refusal_rate = 0.1
events = []
monkeypatch.setattr(pipeline, "_emit", lambda *args, **kwargs: events.append((args, kwargs)))
ticks = iter((5.0, 5.5))
monkeypatch.setattr("obliteratus.informed_pipeline.time.time", lambda: next(ticks))
result = pipeline._rebirth_informed()
assert result == pipeline.output_dir
assert saved == [("model", result), ("tokenizer", result)]
metadata = json.loads((result / "abliteration_metadata.json").read_text())
assert metadata["analysis_insights"]["detected_alignment_method"] == "dpo"
assert metadata["derived_config"]["layers_used"] == [1, 3]
assert metadata["pipeline_stats"]["ouroboros_passes"] == 1
assert metadata["quality_metrics"] == {"refusal_rate": 0.1}
assert events[0][0][:2] == ("rebirth", "running")
assert events[-1][0][:2] == ("rebirth", "done")
def test_distill_single_direction_honors_recommended_and_skipped_layers(
pipeline,
monkeypatch,
):
pipeline.handle = SimpleNamespace(hidden_size=4096, total_params=3_000_000_000)
pipeline.n_directions = 1
pipeline._harmful_means = {
0: torch.tensor([[3.0, 4.0]]),
1: torch.tensor([[0.0, 2.0]]),
}
pipeline._harmless_means = {idx: torch.zeros(1, 2) for idx in range(2)}
pipeline._insights.recommended_layers = [0, 1, 99]
pipeline._insights.skip_layers = [1]
events = []
monkeypatch.setattr(pipeline, "_emit", lambda *args, **kwargs: events.append((args, kwargs)))
ticks = iter((1.0, 1.5))
monkeypatch.setattr("obliteratus.informed_pipeline.time.time", lambda: next(ticks))
pipeline._distill_informed()
assert torch.allclose(pipeline.refusal_directions[0], torch.tensor([0.6, 0.8]))
assert pipeline.refusal_subspaces[0].shape == (1, 2)
assert pipeline._strong_layers == [0]
assert events[0][0][:2] == ("distill", "running")
assert events[-1][1]["strong_layers"] == [0]
def test_distill_svd_sanitizes_nonfinite_input_and_enriches_category_directions(
pipeline,
monkeypatch,
):
pipeline.handle = SimpleNamespace(hidden_size=4096, total_params=3_000_000_000)
pipeline.n_directions = 3
pipeline.use_whitened_svd = False
pipeline._harmful_means = {0: torch.tensor([[2.0, 0.0, 0.0]])}
pipeline._harmless_means = {0: torch.zeros(1, 3)}
pipeline._harmful_acts = {
0: [
torch.tensor([[2.0, 0.0, 0.0]]),
torch.tensor([[0.0, float("nan"), 0.0]]),
torch.tensor([[0.0, 0.0, 1.0]]),
]
}
pipeline._harmless_acts = {0: [torch.zeros(1, 3) for _ in range(3)]}
pipeline._insights.cone_is_polyhedral = True
pipeline._insights.per_category_directions = {
"one": torch.tensor([0.0, 1.0, 0.0]),
"two": torch.tensor([0.0, 0.0, 1.0]),
}
monkeypatch.setattr(pipeline, "_select_layers_knee", lambda ranked: [ranked[0][0]])
monkeypatch.setattr(pipeline, "_emit", lambda *_args, **_kwargs: None)
pipeline._distill_informed()
assert pipeline._strong_layers == [0]
assert pipeline.refusal_subspaces[0].shape[0] >= 2
assert torch.isfinite(pipeline.refusal_subspaces[0]).all()
def test_distill_uses_whitened_extractor_for_multi_direction_models(
pipeline,
monkeypatch,
):
result = SimpleNamespace(
directions=torch.tensor([[1.0, 0.0], [0.0, 1.0]]),
singular_values=torch.tensor([3.0, 1.0]),
)
class Extractor:
def extract(self, harmful, harmless, *, n_directions, layer_idx):
assert harmful is pipeline._harmful_acts[0]
assert harmless is pipeline._harmless_acts[0]
assert (n_directions, layer_idx) == (2, 0)
return result
monkeypatch.setattr(
"obliteratus.analysis.whitened_svd.WhitenedSVDExtractor",
Extractor,
)
pipeline.handle = SimpleNamespace(hidden_size=4096, total_params=3_000_000_000)
pipeline.n_directions = 2
pipeline.use_whitened_svd = True
pipeline._harmful_means = {0: torch.tensor([[1.0, 0.0]])}
pipeline._harmless_means = {0: torch.zeros(1, 2)}
pipeline._harmful_acts = {0: [torch.ones(1, 2)]}
pipeline._harmless_acts = {0: [torch.zeros(1, 2)]}
monkeypatch.setattr(pipeline, "_select_layers_knee", lambda _ranked: [0])
monkeypatch.setattr(pipeline, "_emit", lambda *_args, **_kwargs: None)
pipeline._distill_informed()
assert torch.equal(pipeline.refusal_subspaces[0], result.directions)
assert torch.equal(pipeline.refusal_directions[0], result.directions[0])
def test_distill_leace_falls_back_per_layer_after_extractor_failure(
pipeline,
monkeypatch,
):
leace_result = SimpleNamespace(
direction=torch.tensor([0.0, 1.0]),
generalized_eigenvalue=4.0,
erasure_loss=0.2,
)
class Extractor:
def extract(self, _harmful, _harmless, *, layer_idx):
if layer_idx == 0:
return leace_result
raise RuntimeError("singular fixture")
monkeypatch.setattr("obliteratus.analysis.leace.LEACEExtractor", Extractor)
pipeline.handle = SimpleNamespace(hidden_size=4096, total_params=3_000_000_000)
pipeline.direction_method = "leace"
pipeline.n_directions = 1
pipeline._harmful_means = {
0: torch.tensor([[0.0, 1.0]]),
1: torch.tensor([[1.0, 0.0]]),
}
pipeline._harmless_means = {idx: torch.zeros(1, 2) for idx in range(2)}
pipeline._harmful_acts = {idx: [torch.ones(1, 2)] for idx in range(2)}
pipeline._harmless_acts = {idx: [torch.zeros(1, 2)] for idx in range(2)}
monkeypatch.setattr(pipeline, "_select_layers_knee", lambda _ranked: [0, 1])
monkeypatch.setattr(pipeline, "_emit", lambda *_args, **_kwargs: None)
pipeline._distill_informed()
assert torch.equal(pipeline.refusal_directions[0], leace_result.direction)
assert torch.equal(pipeline.refusal_directions[1], torch.tensor([1.0, 0.0]))
def test_sparse_excision_projects_attention_and_ffn_with_iterative_reprobe(
pipeline,
monkeypatch,
):
class Layer(torch.nn.Module):
def __init__(self):
super().__init__()
self.anchor = torch.nn.Parameter(torch.ones(1))
layer = Layer()
attention = SimpleNamespace(o_proj=torch.nn.Linear(2, 2, bias=False))
ffn = SimpleNamespace(down_proj=torch.nn.Linear(2, 2, bias=False))
calls = []
class Surgeon:
def __init__(self, *, sparsity, auto_sparsity):
assert sparsity == 0.25
assert auto_sparsity is True
def apply_sparse_projection(self, weight, direction):
calls.append((weight.clone(), direction.clone()))
return weight * 0.5
monkeypatch.setattr(
"obliteratus.analysis.sparse_surgery.SparseDirectionSurgeon",
Surgeon,
)
monkeypatch.setattr(
"obliteratus.strategies.utils.get_layer_modules",
lambda _handle: [layer],
)
monkeypatch.setattr(
"obliteratus.strategies.utils.get_attention_module",
lambda _layer, _arch: attention,
)
monkeypatch.setattr(
"obliteratus.strategies.utils.get_ffn_module",
lambda _layer, _arch: ffn,
)
pipeline.handle = SimpleNamespace(architecture="gpt2")
pipeline._insights.recommended_sparsity = 0.25
pipeline._strong_layers = [0]
pipeline.refusal_subspaces = {0: torch.eye(2)}
pipeline.refinement_passes = 2
pipeline.true_iterative_refinement = True
monkeypatch.setattr(pipeline, "_probe", lambda: calls.append("probe"))
monkeypatch.setattr(pipeline, "_distill_inner", lambda: calls.append("distill"))
events = []
monkeypatch.setattr(pipeline, "_emit", lambda *args, **kwargs: events.append((args, kwargs)))
pipeline._excise_sparse()
projection_calls = [call for call in calls if isinstance(call, tuple)]
assert len(projection_calls) == 8
assert calls.count("probe") == 1
assert calls.count("distill") == 1
assert events[-1][1]["modified_count"] == 8
def test_verify_compensation_stops_when_kl_rises_sharply(pipeline, monkeypatch):
outcomes = iter(
(
{"refusal_rate": 0.9, "kl_divergence": 0.1},
{"refusal_rate": 0.8, "kl_divergence": 0.12},
{"refusal_rate": 0.7, "kl_divergence": 0.2},
)
)
def verify():
pipeline._quality_metrics = next(outcomes)
monkeypatch.setattr(pipeline, "_verify", verify)
monkeypatch.setattr(pipeline, "_probe", lambda: None)
monkeypatch.setattr(pipeline, "_distill_inner", lambda: setattr(pipeline, "_strong_layers", [0]))
monkeypatch.setattr(pipeline, "_excise_informed", lambda: None)
pipeline.kl_budget = 1.0
pipeline._verify_and_compensate()
assert pipeline._report.ouroboros_passes == 2
assert pipeline._report.final_refusal_rate == 0.7