invarlock 0.3.8__py3-none-any.whl → 0.3.9__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.
invarlock/__init__.py CHANGED
@@ -12,7 +12,7 @@ For torch-dependent functionality, see subpackages under `invarlock.*`:
12
12
  - `invarlock.eval`: Metrics, guard-overhead checks, and evaluation reporting
13
13
  """
14
14
 
15
- __version__ = "0.3.8"
15
+ __version__ = "0.3.9"
16
16
 
17
17
  # Core exports - torch-independent
18
18
  from .config import CFG, Defaults, get_default_config
@@ -35,7 +35,7 @@ def _bh_reject_families(
35
35
 
36
36
  order = sorted(
37
37
  range(n),
38
- key=lambda idx: (float("inf") if not _finite01(pvals[idx]) else pvals[idx]),
38
+ key=lambda idx: float("inf") if not _finite01(pvals[idx]) else pvals[idx],
39
39
  )
40
40
  max_k = 0
41
41
  for rank, idx in enumerate(order, start=1):
@@ -1,13 +1,17 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import json
4
+ import math
4
5
  from pathlib import Path
5
6
 
6
7
  import typer
7
8
  from rich.console import Console
8
9
 
9
10
  from invarlock.core.auto_tuning import get_tier_policies
10
- from invarlock.reporting.report_builder import make_report
11
+ from invarlock.reporting.report_builder import (
12
+ PM_DRIFT_BAND_DEFAULT,
13
+ make_report,
14
+ )
11
15
 
12
16
  console = Console()
13
17
 
@@ -47,12 +51,8 @@ def explain_gates_command(
47
51
  tier = str(
48
52
  (evaluation_report.get("auto", {}) or {}).get("tier", "balanced")
49
53
  ).lower()
50
- tier_thresholds = {
51
- "conservative": 1.05,
52
- "balanced": 1.10,
53
- "aggressive": 1.20,
54
- "none": 1.10,
55
- }
54
+ tier_policies = get_tier_policies()
55
+ tier_defaults = tier_policies.get(tier, tier_policies.get("balanced", {}))
56
56
  resolved_policy = (
57
57
  evaluation_report.get("resolved_policy", {})
58
58
  if isinstance(evaluation_report.get("resolved_policy"), dict)
@@ -64,8 +64,6 @@ def explain_gates_command(
64
64
  else {}
65
65
  )
66
66
  if not metrics_policy:
67
- tier_policies = get_tier_policies()
68
- tier_defaults = tier_policies.get(tier, tier_policies.get("balanced", {}))
69
67
  metrics_policy = (
70
68
  tier_defaults.get("metrics", {}) if isinstance(tier_defaults, dict) else {}
71
69
  )
@@ -79,13 +77,31 @@ def explain_gates_command(
79
77
  hysteresis_ratio = float(pm_policy.get("hysteresis_ratio", 0.0))
80
78
  min_tokens = int(pm_policy.get("min_tokens", 0))
81
79
  try:
82
- limit_base = float(
83
- pm_policy.get("ratio_limit_base", tier_thresholds.get(tier, 1.10))
84
- or tier_thresholds.get(tier, 1.10)
85
- )
80
+ limit_base = float(pm_policy.get("ratio_limit_base"))
86
81
  except Exception:
87
- limit_base = tier_thresholds.get(tier, 1.10)
88
- limit_with_hyst = limit_base + max(0.0, hysteresis_ratio)
82
+ limit_base = None
83
+ if limit_base is None or not isinstance(limit_base, int | float):
84
+ limit_base = None
85
+ elif not float("-inf") < float(limit_base) < float("inf"):
86
+ limit_base = None
87
+ if limit_base is None:
88
+ try:
89
+ fallback = (
90
+ tier_defaults.get("metrics", {})
91
+ if isinstance(tier_defaults, dict)
92
+ else {}
93
+ )
94
+ fallback_pm = (
95
+ fallback.get("pm_ratio", {}) if isinstance(fallback, dict) else {}
96
+ )
97
+ limit_base = float(fallback_pm.get("ratio_limit_base"))
98
+ except Exception:
99
+ limit_base = None
100
+ limit_with_hyst = (
101
+ float(limit_base) + max(0.0, hysteresis_ratio)
102
+ if isinstance(limit_base, int | float)
103
+ else None
104
+ )
89
105
  tokens_ok = True
90
106
  telem = (
91
107
  evaluation_report.get("telemetry", {})
@@ -118,16 +134,21 @@ def explain_gates_command(
118
134
  )
119
135
  else:
120
136
  console.print(f" observed: {ratio:.3f}x")
121
- console.print(
122
- f" threshold: {limit_base:.2f}x{(f' (+hysteresis {hysteresis_ratio:.3f})' if hysteresis_ratio else '')}"
123
- )
137
+ if isinstance(limit_base, int | float):
138
+ hyst_suffix = (
139
+ f" (+hysteresis {hysteresis_ratio:.3f})" if hysteresis_ratio else ""
140
+ )
141
+ console.print(f" threshold: ≤ {float(limit_base):.2f}x{hyst_suffix}")
142
+ else:
143
+ console.print(" threshold: unavailable")
124
144
  console.print(
125
145
  f" tokens: {'ok' if tokens_ok else 'below floor'} (token floors: min_tokens={min_tokens or 0}, total={int(telem.get('preview_total_tokens', 0)) + int(telem.get('final_total_tokens', 0)) if telem else 0})"
126
146
  )
127
147
  if hysteresis_applied:
128
- console.print(
129
- f" note: hysteresis applied → effective threshold = {limit_with_hyst:.3f}x"
130
- )
148
+ if isinstance(limit_with_hyst, int | float):
149
+ console.print(
150
+ f" note: hysteresis applied → effective threshold = {float(limit_with_hyst):.3f}x"
151
+ )
131
152
 
132
153
  # Tail gate explanation (warn/fail; based on per-window Δlog-loss vs baseline)
133
154
  pm_tail = (
@@ -199,11 +220,18 @@ def explain_gates_command(
199
220
  except Exception:
200
221
  pass
201
222
 
202
- # Drift gate explanation
223
+ # Drift gate explanation (ppl-like kinds only)
203
224
  drift = None
204
- drift_ci = None
205
- if isinstance(evaluation_report.get("primary_metric"), dict):
206
- pm = evaluation_report.get("primary_metric", {})
225
+ drift_status = (
226
+ "PASS" if bool(validation.get("preview_final_drift_acceptable")) else "FAIL"
227
+ )
228
+ pm = (
229
+ evaluation_report.get("primary_metric", {})
230
+ if isinstance(evaluation_report.get("primary_metric"), dict)
231
+ else {}
232
+ )
233
+ kind = str(pm.get("kind", "") or "").lower()
234
+ if kind.startswith("ppl"):
207
235
  preview = pm.get("preview")
208
236
  final = pm.get("final")
209
237
  if isinstance(preview, int | float) and isinstance(final, int | float):
@@ -212,19 +240,26 @@ def explain_gates_command(
212
240
  drift = float(final) / float(preview)
213
241
  except Exception:
214
242
  drift = None
215
- drift_status = (
216
- "PASS" if bool(validation.get("preview_final_drift_acceptable")) else "FAIL"
217
- )
218
- console.print("\n[bold]Gate: Drift (final/preview)[/bold]")
219
- if isinstance(drift, int | float):
220
- if isinstance(drift_ci, tuple | list) and len(drift_ci) == 2:
221
- console.print(
222
- f" observed: {drift:.3f} (CI {drift_ci[0]:.3f}-{drift_ci[1]:.3f})"
223
- )
224
- else:
243
+
244
+ console.print("\n[bold]Gate: Drift (final/preview)[/bold]")
245
+ if isinstance(drift, int | float):
225
246
  console.print(f" observed: {drift:.3f}")
226
- console.print(" threshold: 0.95-1.05")
227
- console.print(f" status: {drift_status}")
247
+ drift_band = (
248
+ pm.get("drift_band") if isinstance(pm.get("drift_band"), dict) else {}
249
+ )
250
+ drift_min = drift_band.get("min")
251
+ drift_max = drift_band.get("max")
252
+ if not (
253
+ isinstance(drift_min, int | float)
254
+ and isinstance(drift_max, int | float)
255
+ and math.isfinite(float(drift_min))
256
+ and math.isfinite(float(drift_max))
257
+ and float(drift_min) > 0.0
258
+ and float(drift_min) < float(drift_max)
259
+ ):
260
+ drift_min, drift_max = PM_DRIFT_BAND_DEFAULT
261
+ console.print(f" threshold: {float(drift_min):.3f}-{float(drift_max):.3f}")
262
+ console.print(f" status: {drift_status}")
228
263
 
229
264
  # Guard Overhead explanation (if present)
230
265
  overhead = (
@@ -337,9 +337,13 @@ def _generate_reports(
337
337
 
338
338
  console.print(" ARTIFACTS")
339
339
  entries = _artifact_entries(saved_files, str(output_dir))
340
+ artifact_label_width = max(
341
+ ARTIFACT_LABEL_WIDTH,
342
+ max((len(label) for label, _ in entries), default=0),
343
+ )
340
344
  for idx, (label, value) in enumerate(entries):
341
345
  branch = "└─" if idx == len(entries) - 1 else "├─"
342
- console.print(f" {branch} {label:<{ARTIFACT_LABEL_WIDTH}} {value}")
346
+ console.print(f" {branch} {label:<{artifact_label_width}} {value}")
343
347
  console.print("═" * SECTION_WIDTH)
344
348
 
345
349
  # In CLI report flow, do not hard-exit on validation failure; just display status.
@@ -61,7 +61,7 @@ def _bh_reject_families(
61
61
 
62
62
  order = sorted(
63
63
  range(n),
64
- key=lambda idx: (float("inf") if not _finite01(pvals[idx]) else pvals[idx]),
64
+ key=lambda idx: float("inf") if not _finite01(pvals[idx]) else pvals[idx],
65
65
  )
66
66
  max_k = 0
67
67
  for rank, idx in enumerate(order, start=1):
@@ -20,7 +20,7 @@ def bh_select(pvals: list[float], alpha: float) -> list[bool]:
20
20
 
21
21
  # Sort by p-value ascending while remembering original indices
22
22
  order = sorted(
23
- range(n), key=lambda i: (float("inf") if not _finite01(pvals[i]) else pvals[i])
23
+ range(n), key=lambda i: float("inf") if not _finite01(pvals[i]) else pvals[i]
24
24
  )
25
25
  rejs_sorted = [False] * n
26
26
  max_k = 0
@@ -3,6 +3,7 @@ Health checking and status monitoring.
3
3
  """
4
4
 
5
5
  import logging
6
+ import os
6
7
  import time
7
8
  import traceback
8
9
  from collections.abc import Callable
@@ -177,8 +178,12 @@ class HealthChecker:
177
178
 
178
179
  def check_cpu():
179
180
  """Check CPU usage."""
181
+ cpu_details: dict[str, Any] = {"percent": None, "core_count": None}
182
+ warnings: list[str] = []
183
+
180
184
  try:
181
185
  cpu_percent = psutil.cpu_percent(interval=1)
186
+ cpu_details["percent"] = cpu_percent
182
187
 
183
188
  if cpu_percent > 95:
184
189
  status = HealthStatus.CRITICAL
@@ -189,28 +194,41 @@ class HealthChecker:
189
194
  else:
190
195
  status = HealthStatus.HEALTHY
191
196
  message = f"CPU usage normal: {cpu_percent:.1f}%"
197
+ except Exception as e:
198
+ status = HealthStatus.CRITICAL
199
+ message = f"Failed to measure CPU usage: {e}"
200
+ cpu_details["error"] = str(e)
192
201
 
193
- return ComponentHealth(
194
- name="cpu",
195
- status=status,
196
- message=message,
197
- details={
198
- "percent": cpu_percent,
199
- "core_count": psutil.cpu_count(),
200
- "load_avg": psutil.getloadavg()
201
- if hasattr(psutil, "getloadavg")
202
- else None,
203
- },
204
- timestamp=time.time(),
205
- )
202
+ try:
203
+ core_count = psutil.cpu_count()
204
+ if core_count is None:
205
+ core_count = os.cpu_count()
206
+ cpu_details["core_count"] = core_count
206
207
  except Exception as e:
207
- return ComponentHealth(
208
- name="cpu",
209
- status=HealthStatus.CRITICAL,
210
- message=f"Failed to check CPU: {e}",
211
- details={"error": str(e)},
212
- timestamp=time.time(),
213
- )
208
+ cpu_details["core_count"] = os.cpu_count()
209
+ warnings.append(f"cpu_count_unavailable: {e}")
210
+
211
+ try:
212
+ load_avg: Any | None = None
213
+ if hasattr(psutil, "getloadavg"):
214
+ load_avg = psutil.getloadavg()
215
+ elif hasattr(os, "getloadavg"):
216
+ load_avg = os.getloadavg() # type: ignore[attr-defined]
217
+ cpu_details["load_avg"] = load_avg
218
+ except Exception as e:
219
+ cpu_details["load_avg"] = None
220
+ warnings.append(f"load_avg_unavailable: {e}")
221
+
222
+ if warnings:
223
+ cpu_details["warnings"] = warnings
224
+
225
+ return ComponentHealth(
226
+ name="cpu",
227
+ status=status,
228
+ message=message,
229
+ details=cpu_details,
230
+ timestamp=time.time(),
231
+ )
214
232
 
215
233
  def check_disk():
216
234
  """Check disk space."""
@@ -80,6 +80,9 @@ TIER_RATIO_LIMITS: dict[str, float] = {
80
80
  "none": 1.10,
81
81
  }
82
82
 
83
+ # Canonical preview→final drift band used when not explicitly configured.
84
+ PM_DRIFT_BAND_DEFAULT: tuple[float, float] = (0.95, 1.05)
85
+
83
86
 
84
87
  def _is_ppl_kind(name: Any) -> bool:
85
88
  """Return True if a primary_metric kind denotes a ppl-like metric.
@@ -3263,8 +3266,7 @@ def _resolve_pm_drift_band_from_report(
3263
3266
  ) -> dict[str, float]:
3264
3267
  """Resolve preview→final drift band from report context/meta/env."""
3265
3268
 
3266
- base_min = 0.95
3267
- base_max = 1.05
3269
+ base_min, base_max = PM_DRIFT_BAND_DEFAULT
3268
3270
 
3269
3271
  def _safe_float(val: Any) -> float | None:
3270
3272
  try:
@@ -3436,8 +3438,7 @@ def _compute_validation_flags(
3436
3438
  # Canonical Gates
3437
3439
  # 1. Drift gate: by default 0.95 ≤ final/preview ≤ 1.05 (configurable)
3438
3440
  drift_ratio = ppl.get("preview_final_ratio", 1.0)
3439
- drift_min = 0.95
3440
- drift_max = 1.05
3441
+ drift_min, drift_max = PM_DRIFT_BAND_DEFAULT
3441
3442
  if isinstance(pm_drift_band, dict):
3442
3443
  try:
3443
3444
  cand_min = pm_drift_band.get("min")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: invarlock
3
- Version: 0.3.8
3
+ Version: 0.3.9
4
4
  Summary: Edit‑agnostic robustness evaluation reports for weight edits (InvarLock framework)
5
5
  Author-email: InvarLock Team <oss@invarlock.dev>
6
6
  Maintainer-email: InvarLock Maintainers <support@invarlock.dev>
@@ -106,31 +106,44 @@ Requires-Dist: twine>=4.0.0; extra == "dev"
106
106
  Dynamic: license-file
107
107
 
108
108
  <p align="center">
109
- <img
110
- src="docs/assets/invarlock-logo.svg"
111
- alt="InvarLock"
112
- width="420"
113
- />
109
+ <picture>
110
+ <source
111
+ media="(prefers-color-scheme: dark)"
112
+ srcset="docs/assets/invarlock-logo-dark.svg"
113
+ />
114
+ <img src="docs/assets/invarlock-logo.svg" alt="InvarLock" />
115
+ </picture>
114
116
  </p>
115
117
 
116
- # InvarLock — Edit‑agnostic robustness reports for weight edits
118
+ <p align="center"><em>Edit‑agnostic robustness reports for weight edits</em></p>
117
119
 
118
- [![CI](https://img.shields.io/github/actions/workflow/status/invarlock/invarlock/ci.yml?branch=main&logo=github&label=CI)](https://github.com/invarlock/invarlock/actions/workflows/ci.yml)
119
- [![PyPI](https://badge.fury.io/py/invarlock.svg)](https://pypi.org/project/invarlock/)
120
- [![Docs](https://img.shields.io/badge/docs-quickstart-blue.svg)](https://github.com/invarlock/invarlock/blob/main/docs/user-guide/quickstart.md)
121
- [![License: Apache-2.0](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](LICENSE)
122
- [![Python 3.12+](https://img.shields.io/badge/python-3.12+-blue.svg)](https://www.python.org/downloads/release/python-3120/)
120
+ <p align="center">
121
+ <a href="https://github.com/invarlock/invarlock/actions/workflows/ci.yml">
122
+ <img alt="CI" src="https://img.shields.io/github/actions/workflow/status/invarlock/invarlock/ci.yml?branch=main&logo=github&label=CI" />
123
+ </a>
124
+ <a href="https://pypi.org/project/invarlock/">
125
+ <img alt="PyPI" src="https://badge.fury.io/py/invarlock.svg" />
126
+ </a>
127
+ <a href="https://github.com/invarlock/invarlock/blob/main/docs/user-guide/quickstart.md">
128
+ <img alt="Docs" src="https://img.shields.io/badge/docs-quickstart-blue.svg" />
129
+ </a>
130
+ <a href="LICENSE">
131
+ <img alt="License: Apache-2.0" src="https://img.shields.io/badge/License-Apache_2.0-blue.svg" />
132
+ </a>
133
+ <a href="https://www.python.org/downloads/release/python-3120/">
134
+ <img alt="Python 3.12+" src="https://img.shields.io/badge/python-3.12+-blue.svg" />
135
+ </a>
136
+ </p>
137
+
138
+ <p align="center">
139
+ <strong>Catch silent quality regressions from quantization, pruning, and weight edits before they ship.</strong>
140
+ </p>
123
141
 
124
142
  Quantizing, pruning, or otherwise editing a model’s weights can silently degrade quality.
125
143
  InvarLock compares an edited **subject** checkpoint against a fixed **baseline** with paired
126
144
  evaluation windows, enforces a guard pipeline (invariants → spectral → RMT → variance), and
127
145
  produces a machine‑readable Evaluation Report you can gate in CI.
128
146
 
129
- > Status: pre‑1.0. Until 1.0, minor releases may be breaking. See `CHANGELOG.md`.
130
-
131
- For guidance on where to ask questions, how to report bugs, and what to expect in terms of response times, see
132
- [SUPPORT.md](https://github.com/invarlock/invarlock/blob/main/SUPPORT.md).
133
-
134
147
  ## Why InvarLock?
135
148
 
136
149
  - **Quality gates for weight edits**: catch regressions before deployment.
@@ -270,6 +283,13 @@ If you use InvarLock in scientific work, please cite it (canonical metadata is i
270
283
  | CPU | ✅ Fallback | Slower but functional |
271
284
  <!-- markdownlint-enable MD060 -->
272
285
 
286
+ ## Project status
287
+
288
+ InvarLock is pre‑1.0. Until 1.0, minor releases may include breaking changes. See [`CHANGELOG.md`](CHANGELOG.md).
289
+
290
+ For guidance on where to ask questions, how to report bugs, and what to expect in terms of response times, see
291
+ [`SUPPORT.md`](SUPPORT.md).
292
+
273
293
  ## Contributing
274
294
 
275
295
  - Contributing guide: <https://github.com/invarlock/invarlock/blob/main/CONTRIBUTING.md>
@@ -1,4 +1,4 @@
1
- invarlock/__init__.py,sha256=5SfeXbFvAxg2c1dblfdTyPB9MB91SgRJ4DFHxGgJJto,1273
1
+ invarlock/__init__.py,sha256=fP52-66oQH_Zck6U8qJFLOoUVY7G_nxXXI04n4b0g0g,1273
2
2
  invarlock/__main__.py,sha256=ffhoKctw89j-henmQXThbHDIdlvK9fBfsy8LpjhOEXc,146
3
3
  invarlock/config.py,sha256=7BUOl7EW258YnsgRipjOx6lmWou5jNDzimREd35ewsQ,1725
4
4
  invarlock/model_profile.py,sha256=OooTlTpsSfAOzJlMw2TsjExXShdAVyitvaKbLebzGsw,13689
@@ -24,7 +24,7 @@ invarlock/adapters/hf_seq2seq.py,sha256=_5jmTB0sMpXs5N1_rNJ7vpntkpj4k68Ca7tM-Iy8
24
24
  invarlock/adapters/py.typed,sha256=LCPmZeE_vANVVJDNvuq9A07i7jg9Nxrq6f10UeuNfZc,37
25
25
  invarlock/assurance/__init__.py,sha256=TTvJGsysPfonfJGIW2qdl3D0_yjslpzyFR0nb70ot_Q,1192
26
26
  invarlock/calibration/__init__.py,sha256=M5lbkNQtLBuQjQJGeRzHovkpYI87fEWIm7a0b23jSp4,110
27
- invarlock/calibration/spectral_null.py,sha256=2WQU7YgnnJ2i3OF60Zj4miaWBvWBXQmztukQtIWhvro,9601
27
+ invarlock/calibration/spectral_null.py,sha256=h8kmV75FICkpQPtlSmFjCsdrSPAx1Nr4i_0m9N283jU,9599
28
28
  invarlock/calibration/variance_ve.py,sha256=QkHO2vxht-n56LNqWbHYYY106Ge5PbxzHfGV1QktnN8,4487
29
29
  invarlock/cli/__init__.py,sha256=mRjYDVmNykhxJxfQBxJMzhFfmNhmvP6OWI-vJL-ehUA,196
30
30
  invarlock/cli/__main__.py,sha256=spgMrx0eJQSEf4rcd0_mB2B8rHOc3iWlTFO3P_qEvoM,175
@@ -46,10 +46,10 @@ invarlock/cli/commands/__init__.py,sha256=Nvjvw9Jo0Wq3BHBpEGZBTUnj25920hw9jZ0qm1
46
46
  invarlock/cli/commands/calibrate.py,sha256=IFWIvkwMFmXl8c0V6mY0AQ4gS9Jkl7MP96W2DFMEbMU,22663
47
47
  invarlock/cli/commands/doctor.py,sha256=F-q5CJ2qRgCDqbaZYBRApPUy7V6E_jkgSCqZgkxlOqA,56879
48
48
  invarlock/cli/commands/evaluate.py,sha256=eCnT9m4LzYCf5lmZd8hCvn-8CgdtXwx7CJvyMRaYRbg,36799
49
- invarlock/cli/commands/explain_gates.py,sha256=m1TRR6jQWMkFCDHocw6-arwIANeCz337ZRhT4PCE1Ps,9720
49
+ invarlock/cli/commands/explain_gates.py,sha256=wbIgCStvdu6EXy5us8QeI-6Nq6nMQRbbF3eynFDAnXc,10977
50
50
  invarlock/cli/commands/export_html.py,sha256=v5UCbFcRrJvRFC5ek-LhZbaBTZtkmaQikg2QHZR73RA,3142
51
51
  invarlock/cli/commands/plugins.py,sha256=VTJKTMjFO4Dh4dqcY4qlkwhsWJixyHSNk8tWBcYGyfA,53620
52
- invarlock/cli/commands/report.py,sha256=UY52S2q5UzUJZFougJr6yDJGRP9Pe0_idnmK81meB3o,21226
52
+ invarlock/cli/commands/report.py,sha256=eL1AaV0sK8rUNlstlew_UQhDv_J6sunEblsDuk3qItA,21404
53
53
  invarlock/cli/commands/run.py,sha256=7D2s8WM5o02n9oVsNpaVpdeBKXY812jXkNS4MZwColQ,236332
54
54
  invarlock/cli/commands/verify.py,sha256=o8WtTaw2JZFNXb3fREhlC0eLutujDaGzqDSIXwIrtl8,51481
55
55
  invarlock/core/__init__.py,sha256=4wb83Xv7NE5M1FgvaFUviiNtVSTVATiPH3gqavNmp2w,1490
@@ -101,18 +101,18 @@ invarlock/guards/invariants.py,sha256=pnzjB1_2e0yD7jjySLIm_McDail_HTWpbAsGuYD2I4
101
101
  invarlock/guards/policies.py,sha256=ggY0zZ3i39OAeFZJSG1FjwnR3-wjWntKLLvumBvG8pc,25933
102
102
  invarlock/guards/py.typed,sha256=LCPmZeE_vANVVJDNvuq9A07i7jg9Nxrq6f10UeuNfZc,37
103
103
  invarlock/guards/rmt.py,sha256=aRMtRR7LfrjR-ZkeL_AsGb61bbBadYHhQx424WIRFVs,99878
104
- invarlock/guards/spectral.py,sha256=AJqMx7oPHMOlnMWf4lN9mIFKparC0lOKkIB8ydABMw0,69603
104
+ invarlock/guards/spectral.py,sha256=lJbj4X_rSB6gcZ89yohdr2795Hlw5E5LXiRRFXjRKUw,69601
105
105
  invarlock/guards/tier_config.py,sha256=6JzS1TOlhUVX2NdUodWhb4VOA_AnAFVOXvwY9aiopbA,10796
106
106
  invarlock/guards/variance.py,sha256=iNursnzCHbc2fd3icZ3cYG-dk4WFYqn6CKxKcKn2MJI,136385
107
107
  invarlock/guards_ref/__init__.py,sha256=jLnyFqdqQaheG1qQMlU4Gx7R118rkkQHPqFVF3_1ih0,366
108
108
  invarlock/guards_ref/rmt_ref.py,sha256=IfwN-l9cLExUQJhZmr-1uJv7qRSoYrGg2w2caCG53hs,1399
109
- invarlock/guards_ref/spectral_ref.py,sha256=FdwFfrs5hxEEUIfBV3CvAJvTX78gAM00mKLEXyZ0zJo,4386
109
+ invarlock/guards_ref/spectral_ref.py,sha256=UE-jHz77jmj9Qw0ScYrsLxGme5yvUh5BVDw4uy9a_l4,4384
110
110
  invarlock/guards_ref/variance_ref.py,sha256=BmTQL5nfxReJK5SqnuUi1SOcQftK7IUNwlcxqdtGPEU,2264
111
111
  invarlock/observability/__init__.py,sha256=_fCH1H51M0PYOvpuNq9sVQLTiDAtdQMvtnhVt-z-VPk,345
112
112
  invarlock/observability/alerting.py,sha256=y_mReOy5KHbHhglQIoChzTl-4BZz9aaGDreLGFzhl8M,17887
113
113
  invarlock/observability/core.py,sha256=lMsXCp5HZj3YIdfrkxRYQWpu1ib6pm8TkPab_1aKjHM,19050
114
114
  invarlock/observability/exporters.py,sha256=bNpuN7ulQ3KQSySozJZvNHythMw2ciF54aQzZ9t2hKE,18712
115
- invarlock/observability/health.py,sha256=jKVfThhe985oY1koS8EhWm08VQaVhHgRcspkuNrvO9E,21573
115
+ invarlock/observability/health.py,sha256=xP60MMz-uX8UDPd7l0CE5hBymKdVhnazUYMWqpxOPWc,22253
116
116
  invarlock/observability/metrics.py,sha256=RhEz4B5zvRHAgFHukyZHGhDJDSxR3szYwk7TTFzIZTQ,19016
117
117
  invarlock/observability/py.typed,sha256=LCPmZeE_vANVVJDNvuq9A07i7jg9Nxrq6f10UeuNfZc,37
118
118
  invarlock/observability/utils.py,sha256=1AC3ZEzbrDzTOvSIFxAtWlITy1BHEPZ032HKKzqK_10,16492
@@ -131,7 +131,7 @@ invarlock/reporting/policy_utils.py,sha256=yjUOtClY0iMreVo_r1srHFvPgb1QyQ9X6x2cQ
131
131
  invarlock/reporting/primary_metric_utils.py,sha256=Ing2LKIvgpqQQLAuB0x80J3qMd1rBJV2UrePWHZeHCo,14912
132
132
  invarlock/reporting/render.py,sha256=zliYsn79a0s-DkaKsAdcjKBLyPTdPfS-DHGb2b3fYOs,75618
133
133
  invarlock/reporting/report.py,sha256=2w-REyyvbeuOm6l4BIEqVf0lT1ldPWZD7N378_DDR5I,33871
134
- invarlock/reporting/report_builder.py,sha256=l3w7WUcMWtxXmg6dtZjyOkUWA_50mQB88ovibpXyxgU,159829
134
+ invarlock/reporting/report_builder.py,sha256=7USKoyPTGOSObdFc24Z2iPVu6r7k6nm9hBw7rZe54Io,159978
135
135
  invarlock/reporting/report_schema.py,sha256=yvxSbbppdhQchQCBmMcVQTszUBERQg7rrZ-4obnzafc,8902
136
136
  invarlock/reporting/report_types.py,sha256=NcA6PkI8JJA-FbPAGT-q_QcDkRcz57ayEKHyFbqe-Rk,9211
137
137
  invarlock/reporting/telemetry.py,sha256=1A4ZgsmT7BzaEMiG56OzCwc0dX6MKVh8TPOwyXNGSb8,2705
@@ -139,9 +139,9 @@ invarlock/reporting/utils.py,sha256=1aLYgSUR4XvgmhDvU9YK9ICd7W5sjft1qdsZC9JJSRY,
139
139
  invarlock/reporting/validate.py,sha256=aFcac5iaaNOLMYEf6g5xoF4-a-J5E430utFLnfu6fKc,21782
140
140
  invarlock/utils/__init__.py,sha256=DR2pBrgddLH2PW-6ninOE8CM7DNvlvgyYsCkckozbPU,4276
141
141
  invarlock/utils/digest.py,sha256=sfnqGFRiRf7l950MjSIrWO1XbUfXlcEfNLeWFbBUr8I,1290
142
- invarlock-0.3.8.dist-info/licenses/LICENSE,sha256=uFddaXYY02nEFdPpS7bam_bnm0st41BibzD0jHULPXw,10413
143
- invarlock-0.3.8.dist-info/METADATA,sha256=9A66_l4i-uTNQvWASLPe5XPouoxglNdr1aLFv13YS4s,13419
144
- invarlock-0.3.8.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
145
- invarlock-0.3.8.dist-info/entry_points.txt,sha256=7zdvDjsIPqOGEhfQFn596IrodvkhYrkybFgQ2ywPHYQ,780
146
- invarlock-0.3.8.dist-info/top_level.txt,sha256=GXfftc_YDHHcQC2vQgYbZ5cTO82YuWY3HusHMT3DuKs,10
147
- invarlock-0.3.8.dist-info/RECORD,,
142
+ invarlock-0.3.9.dist-info/licenses/LICENSE,sha256=uFddaXYY02nEFdPpS7bam_bnm0st41BibzD0jHULPXw,10413
143
+ invarlock-0.3.9.dist-info/METADATA,sha256=8dJtXFFBg3tIKnIcVkIc0wsLqR-uhXAfH82MnJLoxDI,13901
144
+ invarlock-0.3.9.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
145
+ invarlock-0.3.9.dist-info/entry_points.txt,sha256=7zdvDjsIPqOGEhfQFn596IrodvkhYrkybFgQ2ywPHYQ,780
146
+ invarlock-0.3.9.dist-info/top_level.txt,sha256=GXfftc_YDHHcQC2vQgYbZ5cTO82YuWY3HusHMT3DuKs,10
147
+ invarlock-0.3.9.dist-info/RECORD,,