invarlock 0.2.0__py3-none-any.whl

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.
Files changed (132) hide show
  1. invarlock/__init__.py +33 -0
  2. invarlock/__main__.py +10 -0
  3. invarlock/_data/runtime/profiles/ci_cpu.yaml +15 -0
  4. invarlock/_data/runtime/profiles/release.yaml +23 -0
  5. invarlock/_data/runtime/tiers.yaml +76 -0
  6. invarlock/adapters/__init__.py +102 -0
  7. invarlock/adapters/_capabilities.py +45 -0
  8. invarlock/adapters/auto.py +99 -0
  9. invarlock/adapters/base.py +530 -0
  10. invarlock/adapters/base_types.py +85 -0
  11. invarlock/adapters/hf_bert.py +852 -0
  12. invarlock/adapters/hf_gpt2.py +403 -0
  13. invarlock/adapters/hf_llama.py +485 -0
  14. invarlock/adapters/hf_mixin.py +383 -0
  15. invarlock/adapters/hf_onnx.py +112 -0
  16. invarlock/adapters/hf_t5.py +137 -0
  17. invarlock/adapters/py.typed +1 -0
  18. invarlock/assurance/__init__.py +43 -0
  19. invarlock/cli/__init__.py +8 -0
  20. invarlock/cli/__main__.py +8 -0
  21. invarlock/cli/_evidence.py +25 -0
  22. invarlock/cli/_json.py +75 -0
  23. invarlock/cli/adapter_auto.py +162 -0
  24. invarlock/cli/app.py +287 -0
  25. invarlock/cli/commands/__init__.py +26 -0
  26. invarlock/cli/commands/certify.py +403 -0
  27. invarlock/cli/commands/doctor.py +1358 -0
  28. invarlock/cli/commands/explain_gates.py +151 -0
  29. invarlock/cli/commands/export_html.py +100 -0
  30. invarlock/cli/commands/plugins.py +1331 -0
  31. invarlock/cli/commands/report.py +354 -0
  32. invarlock/cli/commands/run.py +4146 -0
  33. invarlock/cli/commands/verify.py +1040 -0
  34. invarlock/cli/config.py +396 -0
  35. invarlock/cli/constants.py +68 -0
  36. invarlock/cli/device.py +92 -0
  37. invarlock/cli/doctor_helpers.py +74 -0
  38. invarlock/cli/errors.py +6 -0
  39. invarlock/cli/overhead_utils.py +60 -0
  40. invarlock/cli/provenance.py +66 -0
  41. invarlock/cli/utils.py +41 -0
  42. invarlock/config.py +56 -0
  43. invarlock/core/__init__.py +62 -0
  44. invarlock/core/abi.py +15 -0
  45. invarlock/core/api.py +274 -0
  46. invarlock/core/auto_tuning.py +317 -0
  47. invarlock/core/bootstrap.py +226 -0
  48. invarlock/core/checkpoint.py +221 -0
  49. invarlock/core/contracts.py +73 -0
  50. invarlock/core/error_utils.py +64 -0
  51. invarlock/core/events.py +298 -0
  52. invarlock/core/exceptions.py +95 -0
  53. invarlock/core/registry.py +481 -0
  54. invarlock/core/retry.py +146 -0
  55. invarlock/core/runner.py +2041 -0
  56. invarlock/core/types.py +154 -0
  57. invarlock/edits/__init__.py +12 -0
  58. invarlock/edits/_edit_utils.py +249 -0
  59. invarlock/edits/_external_utils.py +268 -0
  60. invarlock/edits/noop.py +47 -0
  61. invarlock/edits/py.typed +1 -0
  62. invarlock/edits/quant_rtn.py +801 -0
  63. invarlock/edits/registry.py +166 -0
  64. invarlock/eval/__init__.py +23 -0
  65. invarlock/eval/bench.py +1207 -0
  66. invarlock/eval/bootstrap.py +50 -0
  67. invarlock/eval/data.py +2052 -0
  68. invarlock/eval/metrics.py +2167 -0
  69. invarlock/eval/primary_metric.py +767 -0
  70. invarlock/eval/probes/__init__.py +24 -0
  71. invarlock/eval/probes/fft.py +139 -0
  72. invarlock/eval/probes/mi.py +213 -0
  73. invarlock/eval/probes/post_attention.py +323 -0
  74. invarlock/eval/providers/base.py +67 -0
  75. invarlock/eval/providers/seq2seq.py +111 -0
  76. invarlock/eval/providers/text_lm.py +113 -0
  77. invarlock/eval/providers/vision_text.py +93 -0
  78. invarlock/eval/py.typed +1 -0
  79. invarlock/guards/__init__.py +18 -0
  80. invarlock/guards/_contracts.py +9 -0
  81. invarlock/guards/invariants.py +640 -0
  82. invarlock/guards/policies.py +805 -0
  83. invarlock/guards/py.typed +1 -0
  84. invarlock/guards/rmt.py +2097 -0
  85. invarlock/guards/spectral.py +1419 -0
  86. invarlock/guards/tier_config.py +354 -0
  87. invarlock/guards/variance.py +3298 -0
  88. invarlock/guards_ref/__init__.py +15 -0
  89. invarlock/guards_ref/rmt_ref.py +40 -0
  90. invarlock/guards_ref/spectral_ref.py +135 -0
  91. invarlock/guards_ref/variance_ref.py +60 -0
  92. invarlock/model_profile.py +353 -0
  93. invarlock/model_utils.py +221 -0
  94. invarlock/observability/__init__.py +10 -0
  95. invarlock/observability/alerting.py +535 -0
  96. invarlock/observability/core.py +546 -0
  97. invarlock/observability/exporters.py +565 -0
  98. invarlock/observability/health.py +588 -0
  99. invarlock/observability/metrics.py +457 -0
  100. invarlock/observability/py.typed +1 -0
  101. invarlock/observability/utils.py +553 -0
  102. invarlock/plugins/__init__.py +12 -0
  103. invarlock/plugins/hello_guard.py +33 -0
  104. invarlock/plugins/hf_awq_adapter.py +82 -0
  105. invarlock/plugins/hf_bnb_adapter.py +79 -0
  106. invarlock/plugins/hf_gptq_adapter.py +78 -0
  107. invarlock/plugins/py.typed +1 -0
  108. invarlock/py.typed +1 -0
  109. invarlock/reporting/__init__.py +7 -0
  110. invarlock/reporting/certificate.py +3221 -0
  111. invarlock/reporting/certificate_schema.py +244 -0
  112. invarlock/reporting/dataset_hashing.py +215 -0
  113. invarlock/reporting/guards_analysis.py +948 -0
  114. invarlock/reporting/html.py +32 -0
  115. invarlock/reporting/normalizer.py +235 -0
  116. invarlock/reporting/policy_utils.py +517 -0
  117. invarlock/reporting/primary_metric_utils.py +265 -0
  118. invarlock/reporting/render.py +1442 -0
  119. invarlock/reporting/report.py +903 -0
  120. invarlock/reporting/report_types.py +278 -0
  121. invarlock/reporting/utils.py +175 -0
  122. invarlock/reporting/validate.py +631 -0
  123. invarlock/security.py +176 -0
  124. invarlock/sparsity_utils.py +323 -0
  125. invarlock/utils/__init__.py +150 -0
  126. invarlock/utils/digest.py +45 -0
  127. invarlock-0.2.0.dist-info/METADATA +586 -0
  128. invarlock-0.2.0.dist-info/RECORD +132 -0
  129. invarlock-0.2.0.dist-info/WHEEL +5 -0
  130. invarlock-0.2.0.dist-info/entry_points.txt +20 -0
  131. invarlock-0.2.0.dist-info/licenses/LICENSE +201 -0
  132. invarlock-0.2.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,517 @@
1
+ # mypy: ignore-errors
2
+ from __future__ import annotations
3
+
4
+ import copy
5
+ import hashlib
6
+ import json
7
+ from typing import Any
8
+
9
+ from invarlock.core.auto_tuning import TIER_POLICIES
10
+
11
+ from .report_types import RunReport
12
+
13
+
14
+ def _compute_variance_policy_digest(policy: dict[str, Any]) -> str:
15
+ """Compute a stable digest for the variance guard policy knobs."""
16
+ canonical_keys = [
17
+ "deadband",
18
+ "min_abs_adjust",
19
+ "max_scale_step",
20
+ "min_effect_lognll",
21
+ "predictive_one_sided",
22
+ "topk_backstop",
23
+ "max_adjusted_modules",
24
+ ]
25
+ canonical_payload = {
26
+ key: policy.get(key) for key in canonical_keys if key in policy
27
+ }
28
+ if not canonical_payload:
29
+ return ""
30
+ serialized = json.dumps(canonical_payload, sort_keys=True, separators=(",", ":"))
31
+ return hashlib.sha256(serialized.encode("utf-8")).hexdigest()[:16]
32
+
33
+
34
+ def _compute_thresholds_payload(
35
+ tier: str, resolved_policy: dict[str, Any]
36
+ ) -> dict[str, Any]:
37
+ """Build canonical thresholds payload for digest stability."""
38
+ from .certificate import TIER_RATIO_LIMITS # local to avoid cycles
39
+
40
+ tier_lc = (tier or "balanced").lower()
41
+ ratio_limit_base = float(TIER_RATIO_LIMITS.get(tier_lc, 1.10))
42
+ tier_policy = TIER_POLICIES.get(tier_lc, {}) if isinstance(tier_lc, str) else {}
43
+ metrics_policy = (
44
+ tier_policy.get("metrics", {}) if isinstance(tier_policy, dict) else {}
45
+ )
46
+ pm_policy = (
47
+ metrics_policy.get("pm_ratio", {}) if isinstance(metrics_policy, dict) else {}
48
+ )
49
+ acc_policy = (
50
+ metrics_policy.get("accuracy", {}) if isinstance(metrics_policy, dict) else {}
51
+ )
52
+ variance_policy = (
53
+ resolved_policy.get("variance", {}) if isinstance(resolved_policy, dict) else {}
54
+ )
55
+
56
+ payload = {
57
+ "tier": tier_lc,
58
+ "pm_ratio": {
59
+ "ratio_limit_base": ratio_limit_base,
60
+ "min_tokens": int(pm_policy.get("min_tokens", 0) or 0),
61
+ "min_token_fraction": float(
62
+ pm_policy.get("min_token_fraction", 0.0) or 0.0
63
+ ),
64
+ "hysteresis_ratio": float(pm_policy.get("hysteresis_ratio", 0.0) or 0.0),
65
+ },
66
+ "accuracy": {
67
+ "delta_min_pp": float(acc_policy.get("delta_min_pp", -1.0) or -1.0),
68
+ "min_examples": int(acc_policy.get("min_examples", 200) or 200),
69
+ "min_examples_fraction": float(
70
+ acc_policy.get("min_examples_fraction", 0.0) or 0.0
71
+ ),
72
+ "hysteresis_delta_pp": float(
73
+ acc_policy.get("hysteresis_delta_pp", 0.0) or 0.0
74
+ ),
75
+ },
76
+ "variance": {
77
+ "min_effect_lognll": float(
78
+ variance_policy.get("min_effect_lognll", 0.0) or 0.0
79
+ )
80
+ },
81
+ }
82
+ return payload
83
+
84
+
85
+ def _compute_thresholds_hash(payload: dict[str, Any]) -> str:
86
+ canonical = json.dumps(payload, sort_keys=True, separators=(",", ":"))
87
+ return hashlib.sha256(canonical.encode("utf-8")).hexdigest()[:16]
88
+
89
+
90
+ def _promote_legacy_multiple_testing_key(payload: dict[str, Any]) -> None:
91
+ """Promote legacy 'multipletesting' to 'multiple_testing' in-place if present."""
92
+ try:
93
+ legacy_mt = payload.pop("multipletesting", None)
94
+ if legacy_mt is not None and "multiple_testing" not in payload:
95
+ payload["multiple_testing"] = legacy_mt
96
+ except Exception:
97
+ pass
98
+
99
+
100
+ def _resolve_policy_tier(report: RunReport) -> str:
101
+ """Resolve the policy tier from report metadata or context."""
102
+ tier: Any = None
103
+ try:
104
+ meta = report.get("meta", {}) if isinstance(report, dict) else {}
105
+ auto_cfg: dict[str, Any] | None = (
106
+ meta.get("auto", {}) if isinstance(meta, dict) else {}
107
+ )
108
+ tier = auto_cfg.get("tier") or meta.get("policy_tier")
109
+ if not tier:
110
+ context = report.get("context", {}) if isinstance(report, dict) else {}
111
+ if isinstance(context, dict):
112
+ tier = context.get("policy_tier") or (
113
+ context.get("auto", {})
114
+ if isinstance(context.get("auto"), dict)
115
+ else {}
116
+ ).get("tier")
117
+ if not tier:
118
+ tier = "balanced"
119
+ return str(tier).lower()
120
+ except Exception:
121
+ return "balanced"
122
+
123
+
124
+ def _format_family_caps(caps: Any) -> dict[str, dict[str, float]]:
125
+ formatted: dict[str, dict[str, float]] = {}
126
+ if isinstance(caps, dict):
127
+ for family, data in caps.items():
128
+ family_name = str(family)
129
+ if isinstance(data, dict):
130
+ kappa_val = data.get("kappa")
131
+ if isinstance(kappa_val, int | float):
132
+ try:
133
+ formatted[family_name] = {"kappa": float(kappa_val)}
134
+ except Exception:
135
+ pass
136
+ elif isinstance(data, int | float):
137
+ try:
138
+ formatted[family_name] = {"kappa": float(data)}
139
+ except Exception:
140
+ pass
141
+ return formatted
142
+
143
+
144
+ def _format_epsilon_map(epsilon_map: Any) -> dict[str, float]:
145
+ formatted: dict[str, float] = {}
146
+ if isinstance(epsilon_map, dict):
147
+ for family, value in epsilon_map.items():
148
+ if isinstance(value, int | float):
149
+ try:
150
+ formatted[str(family)] = float(value)
151
+ except Exception:
152
+ pass
153
+ return formatted
154
+
155
+
156
+ def _build_resolved_policies(
157
+ tier: str, spectral: dict[str, Any], rmt: dict[str, Any], variance: dict[str, Any]
158
+ ) -> dict[str, Any]:
159
+ """Merge tier defaults with observed policies to surface the resolved configuration."""
160
+ tier_key = (tier or "balanced").lower()
161
+ base = copy.deepcopy(TIER_POLICIES.get(tier_key, TIER_POLICIES.get("balanced", {})))
162
+
163
+ resolved: dict[str, Any] = {}
164
+
165
+ def _safe_float(value: Any, default: float) -> float:
166
+ try:
167
+ return float(value)
168
+ except (TypeError, ValueError):
169
+ return float(default)
170
+
171
+ def _safe_int(value: Any, default: int) -> int:
172
+ try:
173
+ return int(value)
174
+ except (TypeError, ValueError):
175
+ return int(default)
176
+
177
+ # Spectral guard
178
+ base_spectral = (
179
+ copy.deepcopy(base.get("spectral", {})) if isinstance(base, dict) else {}
180
+ )
181
+ spectral_resolved: dict[str, Any] = {}
182
+ if isinstance(base_spectral, dict):
183
+ spectral_resolved.update(base_spectral)
184
+ spectral_caps = spectral.get("family_caps") or spectral_resolved.get("family_caps")
185
+ from .policy_utils import _format_family_caps as _ffc # self import safe
186
+
187
+ spectral_resolved["family_caps"] = _ffc(spectral_caps)
188
+ # Prefer observed policy sigma_quantile (accepting legacy aliases), then fallback
189
+ pol_sq = None
190
+ try:
191
+ pol_sq = (spectral.get("policy", {}) or {}).get("sigma_quantile")
192
+ if pol_sq is None:
193
+ # Legacy aliases
194
+ pol_sq = (spectral.get("policy", {}) or {}).get("contraction") or (
195
+ spectral.get("policy", {}) or {}
196
+ ).get("kappa")
197
+ except Exception:
198
+ pol_sq = None
199
+ spectral_resolved["sigma_quantile"] = _safe_float(
200
+ pol_sq
201
+ if pol_sq is not None
202
+ else spectral.get(
203
+ "sigma_quantile", spectral_resolved.get("sigma_quantile", 0.95)
204
+ ),
205
+ 0.95,
206
+ )
207
+ spectral_resolved["deadband"] = _safe_float(
208
+ spectral.get("deadband", spectral_resolved.get("deadband", 0.1)), 0.1
209
+ )
210
+ max_caps_val = spectral.get("max_caps", spectral_resolved.get("max_caps", 5))
211
+ spectral_resolved["max_caps"] = _safe_int(
212
+ max_caps_val, base_spectral.get("max_caps", 5) or 5
213
+ )
214
+ if "ignore_preview_inflation" in base_spectral:
215
+ spectral_resolved["ignore_preview_inflation"] = base_spectral[
216
+ "ignore_preview_inflation"
217
+ ]
218
+ spectral_resolved["scope"] = (
219
+ spectral.get("policy", {}).get("scope")
220
+ or spectral_resolved.get("scope")
221
+ or "all"
222
+ )
223
+ mt_source = spectral.get("multiple_testing") or spectral_resolved.get(
224
+ "multiple_testing"
225
+ )
226
+ if isinstance(mt_source, dict):
227
+ mt_entry = {
228
+ "method": mt_source.get("method"),
229
+ "alpha": _safe_float(mt_source.get("alpha", 0.05), 0.05),
230
+ }
231
+ m_val = mt_source.get("m")
232
+ mt_entry["m"] = _safe_int(m_val, len(spectral_resolved["family_caps"] or {}))
233
+ spectral_resolved["multiple_testing"] = mt_entry
234
+ correction_flag = spectral.get("policy", {}).get(
235
+ "correction_enabled",
236
+ spectral_resolved.get("correction_enabled", False),
237
+ )
238
+ spectral_resolved["correction_enabled"] = bool(correction_flag)
239
+ if tier_key == "balanced":
240
+ spectral_resolved["correction_enabled"] = False
241
+ spectral_resolved["max_spectral_norm"] = None
242
+ else:
243
+ spectral_resolved["max_spectral_norm"] = spectral.get("policy", {}).get(
244
+ "max_spectral_norm", spectral_resolved.get("max_spectral_norm")
245
+ )
246
+ resolved["spectral"] = spectral_resolved
247
+
248
+ # RMT guard
249
+ base_rmt = copy.deepcopy(base.get("rmt", {})) if isinstance(base, dict) else {}
250
+ rmt_resolved: dict[str, Any] = {}
251
+ if isinstance(base_rmt, dict):
252
+ rmt_resolved.update(base_rmt)
253
+ rmt_resolved["margin"] = _safe_float(
254
+ rmt.get("margin", rmt_resolved.get("margin", 1.5)), 1.5
255
+ )
256
+ rmt_resolved["deadband"] = _safe_float(
257
+ rmt.get("deadband", rmt_resolved.get("deadband", 0.1)), 0.1
258
+ )
259
+ epsilon_default_val = rmt.get(
260
+ "epsilon_default", rmt_resolved.get("epsilon_default", 0.1)
261
+ )
262
+ rmt_resolved["epsilon_default"] = _safe_float(epsilon_default_val, 0.1)
263
+ from .policy_utils import _format_epsilon_map as _fem
264
+
265
+ epsilon_map = _fem(rmt.get("epsilon_by_family") or rmt_resolved.pop("epsilon", {}))
266
+ if epsilon_map:
267
+ rmt_resolved["epsilon_by_family"] = epsilon_map
268
+ else:
269
+ rmt_resolved.pop("epsilon", None)
270
+ if "epsilon" in rmt_resolved:
271
+ rmt_resolved.pop("epsilon", None)
272
+ if "correct" in rmt_resolved:
273
+ rmt_resolved["correct"] = bool(rmt_resolved["correct"])
274
+ resolved["rmt"] = rmt_resolved
275
+
276
+ # Variance guard
277
+ base_variance = (
278
+ copy.deepcopy(base.get("variance", {})) if isinstance(base, dict) else {}
279
+ )
280
+ variance_resolved: dict[str, Any] = {}
281
+ if isinstance(base_variance, dict):
282
+ variance_resolved.update(base_variance)
283
+ predictive_gate = variance.get("predictive_gate", {})
284
+ predictive_one_sided = variance_resolved.get("predictive_one_sided")
285
+ if isinstance(predictive_gate, dict) and "sided" in predictive_gate:
286
+ predictive_one_sided = predictive_gate.get("sided") in (True, "one_sided")
287
+ variance_resolved["predictive_one_sided"] = bool(
288
+ predictive_one_sided if predictive_one_sided is not None else True
289
+ )
290
+ variance_resolved["min_effect_lognll"] = _safe_float(
291
+ variance_resolved.get("min_effect_lognll", 0.0), 0.0
292
+ )
293
+ variance_resolved["max_adjusted_modules"] = _safe_int(
294
+ variance_resolved.get("max_adjusted_modules", 0), 0
295
+ )
296
+ if "deadband" in variance_resolved:
297
+ variance_resolved["deadband"] = _safe_float(
298
+ variance_resolved.get("deadband", 0.0), 0.0
299
+ )
300
+ if "min_abs_adjust" in variance_resolved:
301
+ variance_resolved["min_abs_adjust"] = _safe_float(
302
+ variance_resolved.get("min_abs_adjust", 0.0), 0.0
303
+ )
304
+ if "max_scale_step" in variance_resolved:
305
+ variance_resolved["max_scale_step"] = _safe_float(
306
+ variance_resolved.get("max_scale_step", 0.0), 0.0
307
+ )
308
+ resolved["variance"] = variance_resolved
309
+
310
+ # Confidence thresholds (optional policy knobs)
311
+ try:
312
+ metrics = base.get("metrics", {}) if isinstance(base, dict) else {}
313
+ conf = metrics.get("confidence") if isinstance(metrics, dict) else None
314
+ if isinstance(conf, dict) and conf:
315
+ resolved["confidence"] = {}
316
+ if "ppl_ratio_width_max" in conf:
317
+ try:
318
+ resolved["confidence"]["ppl_ratio_width_max"] = float(
319
+ conf.get("ppl_ratio_width_max")
320
+ )
321
+ except Exception:
322
+ pass
323
+ if "accuracy_delta_pp_width_max" in conf:
324
+ try:
325
+ resolved["confidence"]["accuracy_delta_pp_width_max"] = float(
326
+ conf.get("accuracy_delta_pp_width_max")
327
+ )
328
+ except Exception:
329
+ pass
330
+ except Exception:
331
+ pass
332
+
333
+ return resolved
334
+
335
+
336
+ def _extract_effective_policies(report: RunReport) -> dict[str, Any]:
337
+ """Extract the effective policies that were applied during the run."""
338
+ policies: dict[str, Any] = {}
339
+
340
+ guard_entries: list[dict[str, Any]] = report.get("guards", [])
341
+ for guard in guard_entries:
342
+ guard_name = guard.get("name", "").lower()
343
+ guard_policy = guard.get("policy", {})
344
+ original_policy = dict(guard_policy) if isinstance(guard_policy, dict) else {}
345
+ guard_metrics = guard.get("metrics", {})
346
+
347
+ if not guard_policy and guard_metrics:
348
+ if guard_name == "rmt":
349
+ guard_policy = {
350
+ "deadband": guard_metrics.get("deadband_used", 0.1),
351
+ "margin": guard_metrics.get("margin_used", 1.5),
352
+ "detection_threshold": guard_metrics.get(
353
+ "detection_threshold", 1.65
354
+ ),
355
+ "q_method": guard_metrics.get("q_used", "auto"),
356
+ "epsilon_default": guard_metrics.get("epsilon_default"),
357
+ "epsilon_by_family": guard_metrics.get("epsilon_by_family"),
358
+ }
359
+ elif guard_name == "spectral":
360
+ sigma_quantile = guard_metrics.get(
361
+ "sigma_quantile",
362
+ guard_metrics.get("contraction", guard_metrics.get("kappa", 0.95)),
363
+ )
364
+ multiple_testing = guard_metrics.get("multiple_testing") or (
365
+ guard_metrics.get("multipletesting")
366
+ if isinstance(guard_metrics.get("multipletesting"), dict)
367
+ else None
368
+ )
369
+ guard_policy = {
370
+ "max_spectral_norm": guard_metrics.get("max_spectral_norm"),
371
+ "stability_score": guard_metrics.get("stability_score", 0.95),
372
+ "caps_applied": guard_metrics.get("caps_applied", 0),
373
+ "sigma_quantile": sigma_quantile,
374
+ "deadband": guard_metrics.get("deadband", 0.1),
375
+ "max_caps": guard_metrics.get("max_caps", 5),
376
+ }
377
+ if isinstance(multiple_testing, dict) and multiple_testing:
378
+ guard_policy["multiple_testing"] = multiple_testing
379
+ elif guard_name == "variance":
380
+ guard_policy = {
381
+ "scope": guard_metrics.get("scope", "both"),
382
+ "min_gain_threshold": guard_metrics.get("min_gain_threshold", 0.3),
383
+ "target_modules": guard_metrics.get("target_modules", 0),
384
+ "ve_enabled": guard_metrics.get("ve_enabled", False),
385
+ }
386
+ elif guard_name == "invariants":
387
+ guard_policy = {
388
+ "checks_performed": guard_metrics.get("checks_performed", 0),
389
+ "violations_found": guard_metrics.get("violations_found", 0),
390
+ }
391
+
392
+ if guard_policy:
393
+ if guard_name == "spectral":
394
+ sigma_quantile = guard_policy.get("sigma_quantile")
395
+ if sigma_quantile is None:
396
+ sigma_quantile = guard_policy.get("contraction")
397
+ if sigma_quantile is None and "kappa" in guard_policy:
398
+ sigma_quantile = guard_policy["kappa"]
399
+ sanitized_policy = dict(guard_policy)
400
+ if sigma_quantile is not None:
401
+ try:
402
+ sanitized_policy["sigma_quantile"] = float(sigma_quantile)
403
+ except (TypeError, ValueError):
404
+ pass
405
+ _promote_legacy_multiple_testing_key(sanitized_policy)
406
+ sanitized_policy.pop("contraction", None)
407
+ sanitized_policy.pop("kappa", None)
408
+ if sanitized_policy.get("max_spectral_norm") in (None, 0):
409
+ sanitized_policy["max_spectral_norm"] = None
410
+ guard_policy = sanitized_policy
411
+ elif guard_name == "rmt" and isinstance(guard_metrics, dict):
412
+ eps_default = guard_metrics.get("epsilon_default")
413
+ if eps_default is not None and "epsilon_default" not in guard_policy:
414
+ guard_policy = dict(guard_policy)
415
+ guard_policy["epsilon_default"] = eps_default
416
+ elif guard_name == "variance" and original_policy:
417
+ guard_policy = dict(guard_policy)
418
+ for key in (
419
+ "deadband",
420
+ "min_abs_adjust",
421
+ "max_scale_step",
422
+ "min_effect_lognll",
423
+ "predictive_one_sided",
424
+ "topk_backstop",
425
+ "max_adjusted_modules",
426
+ ):
427
+ if key in original_policy and key not in guard_policy:
428
+ guard_policy[key] = original_policy[key]
429
+ policies[guard_name] = dict(guard_policy)
430
+
431
+ tier_defaults = TIER_POLICIES.get(_resolve_policy_tier(report), {})
432
+
433
+ def _merge_defaults(target: dict[str, Any], defaults: dict[str, Any]) -> None:
434
+ for key, value in defaults.items():
435
+ if isinstance(value, dict):
436
+ target_value = target.get(key)
437
+ if not isinstance(target_value, dict):
438
+ target[key] = copy.deepcopy(value)
439
+ else:
440
+ _merge_defaults(target_value, value)
441
+ else:
442
+ if target.get(key) in (None, "", [], {}):
443
+ target[key] = copy.deepcopy(value)
444
+
445
+ for guard_name, defaults in tier_defaults.items():
446
+ if not isinstance(defaults, dict):
447
+ continue
448
+ if guard_name not in policies:
449
+ policies[guard_name] = copy.deepcopy(defaults)
450
+ else:
451
+ _merge_defaults(policies[guard_name], defaults)
452
+
453
+ if not policies:
454
+ metrics = report.get("metrics", {}) if isinstance(report, dict) else {}
455
+ if "spectral" in metrics:
456
+ policies["spectral"] = {"status": "default_config"}
457
+ if "rmt" in metrics:
458
+ policies["rmt"] = {"status": "default_config"}
459
+
460
+ return policies
461
+
462
+
463
+ def _normalize_override_entry(value: Any) -> list[str]:
464
+ if value is None:
465
+ return []
466
+ if isinstance(value, str):
467
+ return [value]
468
+ if isinstance(value, list | tuple | set):
469
+ return [str(item) for item in value if item is not None]
470
+ return []
471
+
472
+
473
+ def _extract_policy_overrides(report: RunReport) -> list[str]:
474
+ """Return ordered list of policy override paths referenced by the run."""
475
+ overrides: list[str] = []
476
+ meta = report.get("meta", {})
477
+ config = report.get("config", {})
478
+ candidate_sources: list[str] = []
479
+ if isinstance(meta, dict):
480
+ candidate_sources.extend(
481
+ _normalize_override_entry(meta.get("policy_overrides"))
482
+ )
483
+ candidate_sources.extend(_normalize_override_entry(meta.get("overrides")))
484
+ auto_meta = meta.get("auto")
485
+ if isinstance(auto_meta, dict):
486
+ candidate_sources.extend(
487
+ _normalize_override_entry(auto_meta.get("overrides"))
488
+ )
489
+ if isinstance(config, dict):
490
+ candidate_sources.extend(_normalize_override_entry(config.get("overrides")))
491
+ seen: set[str] = set()
492
+ for entry in candidate_sources:
493
+ if entry and entry not in seen:
494
+ overrides.append(entry)
495
+ seen.add(entry)
496
+ return overrides
497
+
498
+
499
+ def _compute_policy_digest(policy: dict[str, Any]) -> str:
500
+ canonical = json.dumps(policy, sort_keys=True, default=str)
501
+ return hashlib.sha256(canonical.encode()).hexdigest()[:16]
502
+
503
+
504
+ __all__ = [
505
+ "_compute_variance_policy_digest",
506
+ "_compute_thresholds_payload",
507
+ "_compute_thresholds_hash",
508
+ "_promote_legacy_multiple_testing_key",
509
+ "_resolve_policy_tier",
510
+ "_build_resolved_policies",
511
+ "_extract_effective_policies",
512
+ "_normalize_override_entry",
513
+ "_extract_policy_overrides",
514
+ "_format_family_caps",
515
+ "_format_epsilon_map",
516
+ "_compute_policy_digest",
517
+ ]