invarlock 0.3.8__tar.gz → 0.3.9__tar.gz

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 (153) hide show
  1. {invarlock-0.3.8/src/invarlock.egg-info → invarlock-0.3.9}/PKG-INFO +37 -17
  2. {invarlock-0.3.8 → invarlock-0.3.9}/README.md +36 -16
  3. {invarlock-0.3.8 → invarlock-0.3.9}/pyproject.toml +1 -1
  4. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/__init__.py +1 -1
  5. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/calibration/spectral_null.py +1 -1
  6. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/cli/commands/explain_gates.py +72 -37
  7. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/cli/commands/report.py +5 -1
  8. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/guards/spectral.py +1 -1
  9. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/guards_ref/spectral_ref.py +1 -1
  10. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/observability/health.py +38 -20
  11. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/reporting/report_builder.py +5 -4
  12. {invarlock-0.3.8 → invarlock-0.3.9/src/invarlock.egg-info}/PKG-INFO +37 -17
  13. {invarlock-0.3.8 → invarlock-0.3.9}/LICENSE +0 -0
  14. {invarlock-0.3.8 → invarlock-0.3.9}/MANIFEST.in +0 -0
  15. {invarlock-0.3.8 → invarlock-0.3.9}/setup.cfg +0 -0
  16. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/__main__.py +0 -0
  17. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/_data/runtime/profiles/ci_cpu.yaml +0 -0
  18. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/_data/runtime/profiles/release.yaml +0 -0
  19. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/_data/runtime/tiers.yaml +0 -0
  20. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/adapters/__init__.py +0 -0
  21. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/adapters/_capabilities.py +0 -0
  22. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/adapters/auto.py +0 -0
  23. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/adapters/base.py +0 -0
  24. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/adapters/base_types.py +0 -0
  25. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/adapters/capabilities.py +0 -0
  26. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/adapters/hf_causal.py +0 -0
  27. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/adapters/hf_causal_onnx.py +0 -0
  28. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/adapters/hf_loading.py +0 -0
  29. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/adapters/hf_mixin.py +0 -0
  30. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/adapters/hf_mlm.py +0 -0
  31. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/adapters/hf_seq2seq.py +0 -0
  32. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/adapters/py.typed +0 -0
  33. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/assurance/__init__.py +0 -0
  34. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/calibration/__init__.py +0 -0
  35. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/calibration/variance_ve.py +0 -0
  36. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/cli/__init__.py +0 -0
  37. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/cli/__main__.py +0 -0
  38. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/cli/_evidence.py +0 -0
  39. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/cli/_json.py +0 -0
  40. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/cli/adapter_auto.py +0 -0
  41. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/cli/app.py +0 -0
  42. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/cli/commands/__init__.py +0 -0
  43. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/cli/commands/calibrate.py +0 -0
  44. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/cli/commands/doctor.py +0 -0
  45. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/cli/commands/evaluate.py +0 -0
  46. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/cli/commands/export_html.py +0 -0
  47. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/cli/commands/plugins.py +0 -0
  48. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/cli/commands/run.py +0 -0
  49. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/cli/commands/verify.py +0 -0
  50. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/cli/config.py +0 -0
  51. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/cli/constants.py +0 -0
  52. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/cli/determinism.py +0 -0
  53. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/cli/device.py +0 -0
  54. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/cli/doctor_helpers.py +0 -0
  55. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/cli/errors.py +0 -0
  56. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/cli/output.py +0 -0
  57. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/cli/overhead_utils.py +0 -0
  58. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/cli/provenance.py +0 -0
  59. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/cli/utils.py +0 -0
  60. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/config.py +0 -0
  61. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/core/__init__.py +0 -0
  62. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/core/abi.py +0 -0
  63. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/core/api.py +0 -0
  64. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/core/auto_tuning.py +0 -0
  65. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/core/bootstrap.py +0 -0
  66. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/core/checkpoint.py +0 -0
  67. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/core/contracts.py +0 -0
  68. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/core/error_utils.py +0 -0
  69. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/core/events.py +0 -0
  70. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/core/exceptions.py +0 -0
  71. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/core/registry.py +0 -0
  72. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/core/retry.py +0 -0
  73. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/core/runner.py +0 -0
  74. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/core/types.py +0 -0
  75. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/edits/__init__.py +0 -0
  76. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/edits/_edit_utils.py +0 -0
  77. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/edits/_external_utils.py +0 -0
  78. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/edits/noop.py +0 -0
  79. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/edits/py.typed +0 -0
  80. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/edits/quant_rtn.py +0 -0
  81. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/edits/registry.py +0 -0
  82. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/eval/__init__.py +0 -0
  83. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/eval/bench.py +0 -0
  84. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/eval/bench_regression.py +0 -0
  85. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/eval/bootstrap.py +0 -0
  86. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/eval/data.py +0 -0
  87. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/eval/metrics.py +0 -0
  88. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/eval/primary_metric.py +0 -0
  89. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/eval/probes/__init__.py +0 -0
  90. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/eval/probes/fft.py +0 -0
  91. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/eval/probes/mi.py +0 -0
  92. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/eval/probes/post_attention.py +0 -0
  93. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/eval/providers/base.py +0 -0
  94. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/eval/providers/seq2seq.py +0 -0
  95. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/eval/providers/text_lm.py +0 -0
  96. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/eval/providers/vision_text.py +0 -0
  97. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/eval/py.typed +0 -0
  98. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/eval/tail_stats.py +0 -0
  99. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/eval/tasks/__init__.py +0 -0
  100. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/eval/tasks/classification.py +0 -0
  101. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/eval/tasks/qa.py +0 -0
  102. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/eval/tasks/text_generation.py +0 -0
  103. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/guards/__init__.py +0 -0
  104. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/guards/_contracts.py +0 -0
  105. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/guards/_estimators.py +0 -0
  106. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/guards/invariants.py +0 -0
  107. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/guards/policies.py +0 -0
  108. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/guards/py.typed +0 -0
  109. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/guards/rmt.py +0 -0
  110. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/guards/tier_config.py +0 -0
  111. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/guards/variance.py +0 -0
  112. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/guards_ref/__init__.py +0 -0
  113. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/guards_ref/rmt_ref.py +0 -0
  114. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/guards_ref/variance_ref.py +0 -0
  115. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/model_profile.py +0 -0
  116. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/model_utils.py +0 -0
  117. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/observability/__init__.py +0 -0
  118. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/observability/alerting.py +0 -0
  119. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/observability/core.py +0 -0
  120. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/observability/exporters.py +0 -0
  121. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/observability/metrics.py +0 -0
  122. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/observability/py.typed +0 -0
  123. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/observability/utils.py +0 -0
  124. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/plugins/__init__.py +0 -0
  125. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/plugins/hello_guard.py +0 -0
  126. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/plugins/hf_awq_adapter.py +0 -0
  127. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/plugins/hf_bnb_adapter.py +0 -0
  128. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/plugins/hf_gptq_adapter.py +0 -0
  129. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/plugins/py.typed +0 -0
  130. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/py.typed +0 -0
  131. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/reporting/__init__.py +0 -0
  132. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/reporting/dataset_hashing.py +0 -0
  133. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/reporting/guards_analysis.py +0 -0
  134. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/reporting/html.py +0 -0
  135. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/reporting/normalizer.py +0 -0
  136. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/reporting/policy_utils.py +0 -0
  137. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/reporting/primary_metric_utils.py +0 -0
  138. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/reporting/render.py +0 -0
  139. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/reporting/report.py +0 -0
  140. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/reporting/report_schema.py +0 -0
  141. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/reporting/report_types.py +0 -0
  142. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/reporting/telemetry.py +0 -0
  143. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/reporting/utils.py +0 -0
  144. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/reporting/validate.py +0 -0
  145. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/security.py +0 -0
  146. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/sparsity_utils.py +0 -0
  147. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/utils/__init__.py +0 -0
  148. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock/utils/digest.py +0 -0
  149. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock.egg-info/SOURCES.txt +0 -0
  150. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock.egg-info/dependency_links.txt +0 -0
  151. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock.egg-info/entry_points.txt +0 -0
  152. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock.egg-info/requires.txt +0 -0
  153. {invarlock-0.3.8 → invarlock-0.3.9}/src/invarlock.egg-info/top_level.txt +0 -0
@@ -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,29 +1,42 @@
1
1
  <p align="center">
2
- <img
3
- src="docs/assets/invarlock-logo.svg"
4
- alt="InvarLock"
5
- width="420"
6
- />
2
+ <picture>
3
+ <source
4
+ media="(prefers-color-scheme: dark)"
5
+ srcset="docs/assets/invarlock-logo-dark.svg"
6
+ />
7
+ <img src="docs/assets/invarlock-logo.svg" alt="InvarLock" />
8
+ </picture>
7
9
  </p>
8
10
 
9
- # InvarLock — Edit‑agnostic robustness reports for weight edits
11
+ <p align="center"><em>Edit‑agnostic robustness reports for weight edits</em></p>
10
12
 
11
- [![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)
12
- [![PyPI](https://badge.fury.io/py/invarlock.svg)](https://pypi.org/project/invarlock/)
13
- [![Docs](https://img.shields.io/badge/docs-quickstart-blue.svg)](https://github.com/invarlock/invarlock/blob/main/docs/user-guide/quickstart.md)
14
- [![License: Apache-2.0](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](LICENSE)
15
- [![Python 3.12+](https://img.shields.io/badge/python-3.12+-blue.svg)](https://www.python.org/downloads/release/python-3120/)
13
+ <p align="center">
14
+ <a href="https://github.com/invarlock/invarlock/actions/workflows/ci.yml">
15
+ <img alt="CI" src="https://img.shields.io/github/actions/workflow/status/invarlock/invarlock/ci.yml?branch=main&logo=github&label=CI" />
16
+ </a>
17
+ <a href="https://pypi.org/project/invarlock/">
18
+ <img alt="PyPI" src="https://badge.fury.io/py/invarlock.svg" />
19
+ </a>
20
+ <a href="https://github.com/invarlock/invarlock/blob/main/docs/user-guide/quickstart.md">
21
+ <img alt="Docs" src="https://img.shields.io/badge/docs-quickstart-blue.svg" />
22
+ </a>
23
+ <a href="LICENSE">
24
+ <img alt="License: Apache-2.0" src="https://img.shields.io/badge/License-Apache_2.0-blue.svg" />
25
+ </a>
26
+ <a href="https://www.python.org/downloads/release/python-3120/">
27
+ <img alt="Python 3.12+" src="https://img.shields.io/badge/python-3.12+-blue.svg" />
28
+ </a>
29
+ </p>
30
+
31
+ <p align="center">
32
+ <strong>Catch silent quality regressions from quantization, pruning, and weight edits before they ship.</strong>
33
+ </p>
16
34
 
17
35
  Quantizing, pruning, or otherwise editing a model’s weights can silently degrade quality.
18
36
  InvarLock compares an edited **subject** checkpoint against a fixed **baseline** with paired
19
37
  evaluation windows, enforces a guard pipeline (invariants → spectral → RMT → variance), and
20
38
  produces a machine‑readable Evaluation Report you can gate in CI.
21
39
 
22
- > Status: pre‑1.0. Until 1.0, minor releases may be breaking. See `CHANGELOG.md`.
23
-
24
- For guidance on where to ask questions, how to report bugs, and what to expect in terms of response times, see
25
- [SUPPORT.md](https://github.com/invarlock/invarlock/blob/main/SUPPORT.md).
26
-
27
40
  ## Why InvarLock?
28
41
 
29
42
  - **Quality gates for weight edits**: catch regressions before deployment.
@@ -163,6 +176,13 @@ If you use InvarLock in scientific work, please cite it (canonical metadata is i
163
176
  | CPU | ✅ Fallback | Slower but functional |
164
177
  <!-- markdownlint-enable MD060 -->
165
178
 
179
+ ## Project status
180
+
181
+ InvarLock is pre‑1.0. Until 1.0, minor releases may include breaking changes. See [`CHANGELOG.md`](CHANGELOG.md).
182
+
183
+ For guidance on where to ask questions, how to report bugs, and what to expect in terms of response times, see
184
+ [`SUPPORT.md`](SUPPORT.md).
185
+
166
186
  ## Contributing
167
187
 
168
188
  - Contributing guide: <https://github.com/invarlock/invarlock/blob/main/CONTRIBUTING.md>
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "invarlock"
7
- version = "0.3.8"
7
+ version = "0.3.9"
8
8
  description = "Edit‑agnostic robustness evaluation reports for weight edits (InvarLock framework)"
9
9
  authors = [{ name = "InvarLock Team", email = "oss@invarlock.dev" }]
10
10
  maintainers = [{ name = "InvarLock Maintainers", email = "support@invarlock.dev" }]
@@ -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>
File without changes
File without changes
File without changes