margin 0.9.31__tar.gz → 0.9.32__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.
Potentially problematic release.
This version of margin might be problematic. Click here for more details.
- {margin-0.9.31/margin.egg-info → margin-0.9.32}/PKG-INFO +55 -2
- {margin-0.9.31 → margin-0.9.32}/README.md +54 -1
- {margin-0.9.31 → margin-0.9.32/margin.egg-info}/PKG-INFO +55 -2
- {margin-0.9.31 → margin-0.9.32}/pyproject.toml +1 -1
- {margin-0.9.31 → margin-0.9.32}/LICENSE +0 -0
- {margin-0.9.31 → margin-0.9.32}/NOTICE +0 -0
- {margin-0.9.31 → margin-0.9.32}/margin/__init__.py +0 -0
- {margin-0.9.31 → margin-0.9.32}/margin/__main__.py +0 -0
- {margin-0.9.31 → margin-0.9.32}/margin/algebra.py +0 -0
- {margin-0.9.31 → margin-0.9.32}/margin/anomaly.py +0 -0
- {margin-0.9.31 → margin-0.9.32}/margin/bridge.py +0 -0
- {margin-0.9.31 → margin-0.9.32}/margin/calibrate.py +0 -0
- {margin-0.9.31 → margin-0.9.32}/margin/causal.py +0 -0
- {margin-0.9.31 → margin-0.9.32}/margin/composite.py +0 -0
- {margin-0.9.31 → margin-0.9.32}/margin/confidence.py +0 -0
- {margin-0.9.31 → margin-0.9.32}/margin/config.py +0 -0
- {margin-0.9.31 → margin-0.9.32}/margin/contract.py +0 -0
- {margin-0.9.31 → margin-0.9.32}/margin/controller.py +0 -0
- {margin-0.9.31 → margin-0.9.32}/margin/correlate.py +0 -0
- {margin-0.9.31 → margin-0.9.32}/margin/diff.py +0 -0
- {margin-0.9.31 → margin-0.9.32}/margin/drift.py +0 -0
- {margin-0.9.31 → margin-0.9.32}/margin/events.py +0 -0
- {margin-0.9.31 → margin-0.9.32}/margin/fingerprint.py +0 -0
- {margin-0.9.31 → margin-0.9.32}/margin/flow.py +0 -0
- {margin-0.9.31 → margin-0.9.32}/margin/forecast.py +0 -0
- {margin-0.9.31 → margin-0.9.32}/margin/health.py +0 -0
- {margin-0.9.31 → margin-0.9.32}/margin/intent.py +0 -0
- {margin-0.9.31 → margin-0.9.32}/margin/issue.py +0 -0
- {margin-0.9.31 → margin-0.9.32}/margin/ledger.py +0 -0
- {margin-0.9.31 → margin-0.9.32}/margin/loop.py +0 -0
- {margin-0.9.31 → margin-0.9.32}/margin/observation.py +0 -0
- {margin-0.9.31 → margin-0.9.32}/margin/persist.py +0 -0
- {margin-0.9.31 → margin-0.9.32}/margin/policy/__init__.py +0 -0
- {margin-0.9.31 → margin-0.9.32}/margin/policy/compose.py +0 -0
- {margin-0.9.31 → margin-0.9.32}/margin/policy/core.py +0 -0
- {margin-0.9.31 → margin-0.9.32}/margin/policy/temporal.py +0 -0
- {margin-0.9.31 → margin-0.9.32}/margin/policy/trace.py +0 -0
- {margin-0.9.31 → margin-0.9.32}/margin/policy/tuning.py +0 -0
- {margin-0.9.31 → margin-0.9.32}/margin/policy/validate.py +0 -0
- {margin-0.9.31 → margin-0.9.32}/margin/predicates.py +0 -0
- {margin-0.9.31 → margin-0.9.32}/margin/provenance.py +0 -0
- {margin-0.9.31 → margin-0.9.32}/margin/streaming.py +0 -0
- {margin-0.9.31 → margin-0.9.32}/margin/transitions.py +0 -0
- {margin-0.9.31 → margin-0.9.32}/margin/uncertain.py +0 -0
- {margin-0.9.31 → margin-0.9.32}/margin/validity.py +0 -0
- {margin-0.9.31 → margin-0.9.32}/margin.egg-info/SOURCES.txt +0 -0
- {margin-0.9.31 → margin-0.9.32}/margin.egg-info/dependency_links.txt +0 -0
- {margin-0.9.31 → margin-0.9.32}/margin.egg-info/top_level.txt +0 -0
- {margin-0.9.31 → margin-0.9.32}/setup.cfg +0 -0
- {margin-0.9.31 → margin-0.9.32}/tests/test_absence.py +0 -0
- {margin-0.9.31 → margin-0.9.32}/tests/test_algebra.py +0 -0
- {margin-0.9.31 → margin-0.9.32}/tests/test_anomaly.py +0 -0
- {margin-0.9.31 → margin-0.9.32}/tests/test_bridge.py +0 -0
- {margin-0.9.31 → margin-0.9.32}/tests/test_calibrate.py +0 -0
- {margin-0.9.31 → margin-0.9.32}/tests/test_causal.py +0 -0
- {margin-0.9.31 → margin-0.9.32}/tests/test_composite.py +0 -0
- {margin-0.9.31 → margin-0.9.32}/tests/test_confidence.py +0 -0
- {margin-0.9.31 → margin-0.9.32}/tests/test_config.py +0 -0
- {margin-0.9.31 → margin-0.9.32}/tests/test_contract.py +0 -0
- {margin-0.9.31 → margin-0.9.32}/tests/test_controller.py +0 -0
- {margin-0.9.31 → margin-0.9.32}/tests/test_correlate.py +0 -0
- {margin-0.9.31 → margin-0.9.32}/tests/test_diff.py +0 -0
- {margin-0.9.31 → margin-0.9.32}/tests/test_drift.py +0 -0
- {margin-0.9.31 → margin-0.9.32}/tests/test_events.py +0 -0
- {margin-0.9.31 → margin-0.9.32}/tests/test_fingerprint_ext.py +0 -0
- {margin-0.9.31 → margin-0.9.32}/tests/test_float_tolerance.py +0 -0
- {margin-0.9.31 → margin-0.9.32}/tests/test_flow.py +0 -0
- {margin-0.9.31 → margin-0.9.32}/tests/test_forecast.py +0 -0
- {margin-0.9.31 → margin-0.9.32}/tests/test_health.py +0 -0
- {margin-0.9.31 → margin-0.9.32}/tests/test_healthcare.py +0 -0
- {margin-0.9.31 → margin-0.9.32}/tests/test_intent.py +0 -0
- {margin-0.9.31 → margin-0.9.32}/tests/test_issue.py +0 -0
- {margin-0.9.31 → margin-0.9.32}/tests/test_ledger.py +0 -0
- {margin-0.9.31 → margin-0.9.32}/tests/test_loop.py +0 -0
- {margin-0.9.31 → margin-0.9.32}/tests/test_neuro.py +0 -0
- {margin-0.9.31 → margin-0.9.32}/tests/test_observation.py +0 -0
- {margin-0.9.31 → margin-0.9.32}/tests/test_persist.py +0 -0
- {margin-0.9.31 → margin-0.9.32}/tests/test_pipeline_features.py +0 -0
- {margin-0.9.31 → margin-0.9.32}/tests/test_policy.py +0 -0
- {margin-0.9.31 → margin-0.9.32}/tests/test_policy_compose.py +0 -0
- {margin-0.9.31 → margin-0.9.32}/tests/test_policy_temporal.py +0 -0
- {margin-0.9.31 → margin-0.9.32}/tests/test_policy_trace.py +0 -0
- {margin-0.9.31 → margin-0.9.32}/tests/test_policy_tuning.py +0 -0
- {margin-0.9.31 → margin-0.9.32}/tests/test_policy_validate.py +0 -0
- {margin-0.9.31 → margin-0.9.32}/tests/test_predicates.py +0 -0
- {margin-0.9.31 → margin-0.9.32}/tests/test_printer3d.py +0 -0
- {margin-0.9.31 → margin-0.9.32}/tests/test_provenance.py +0 -0
- {margin-0.9.31 → margin-0.9.32}/tests/test_reverse_bridge.py +0 -0
- {margin-0.9.31 → margin-0.9.32}/tests/test_ros2.py +0 -0
- {margin-0.9.31 → margin-0.9.32}/tests/test_staleness.py +0 -0
- {margin-0.9.31 → margin-0.9.32}/tests/test_streaming.py +0 -0
- {margin-0.9.31 → margin-0.9.32}/tests/test_transformer.py +0 -0
- {margin-0.9.31 → margin-0.9.32}/tests/test_transitions.py +0 -0
- {margin-0.9.31 → margin-0.9.32}/tests/test_uncertain.py +0 -0
- {margin-0.9.31 → margin-0.9.32}/tests/test_validity.py +0 -0
- {margin-0.9.31 → margin-0.9.32}/tests/test_windowing.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: margin
|
|
3
|
-
Version: 0.9.
|
|
3
|
+
Version: 0.9.32
|
|
4
4
|
Summary: Typed health classification, uncertainty algebra, and correction auditing for any system that measures things and needs to explain what happened.
|
|
5
5
|
Author: Cope Labs LLC
|
|
6
6
|
License: MIT
|
|
@@ -86,6 +86,8 @@ Then the correction loop:
|
|
|
86
86
|
- **Causal** — dependency graphs ("api is DEGRADED because db is ABLATED")
|
|
87
87
|
- **Auto-correlation** — discover which components move together from data, with lag detection
|
|
88
88
|
- **Streaming** — incremental trackers: `Monitor.update(values)` updates health + drift + anomaly + correlation in one call
|
|
89
|
+
- **Flow** — ordered stages with typed live guidance: ADVANCE / HOLD / RETRY / ROLLBACK / ESCALATE for multi-stage processes
|
|
90
|
+
- **Issue** — typed diagnostics: library surfaces its own misconfigurations, degraded assumptions, recovered failures
|
|
89
91
|
- **Config** — define everything in YAML/JSON: `margin.load_config("margin.yaml")`
|
|
90
92
|
- **Persistence** — save/restore Monitor state across restarts, batch replay from CSV
|
|
91
93
|
- **Intent** — goal feasibility: `intent.evaluate_monitor(monitor)` → FEASIBLE / AT_RISK / INFEASIBLE with ETA
|
|
@@ -123,6 +125,55 @@ Thresholds(intact=0.02, ablated=0.10, higher_is_better=False)
|
|
|
123
125
|
|
|
124
126
|
One flag. Threads through every comparison, every sigma calculation, every correction decision, every recovery ratio. You never think about it again.
|
|
125
127
|
|
|
128
|
+
## Multi-stage process guidance
|
|
129
|
+
|
|
130
|
+
For processes where each stage has distinct entry/quality/exit criteria — deployment pipelines, fermentation phases, clinical trials, incident response:
|
|
131
|
+
|
|
132
|
+
```python
|
|
133
|
+
from margin import Flow, Stage, Parser, Thresholds
|
|
134
|
+
|
|
135
|
+
flow = Flow(stages=[
|
|
136
|
+
Stage(
|
|
137
|
+
name="lag",
|
|
138
|
+
parser=Parser(baselines={"biomass": 0.1}, thresholds=Thresholds(intact=0.08, ablated=0.02)),
|
|
139
|
+
advance_when=lambda expr: expr.observations[0].value >= 0.15,
|
|
140
|
+
),
|
|
141
|
+
Stage(name="exponential", parser=..., advance_when=...),
|
|
142
|
+
Stage(name="stationary", parser=...),
|
|
143
|
+
], label="ferment")
|
|
144
|
+
|
|
145
|
+
result = flow.observe({"biomass": 0.12})
|
|
146
|
+
print(result.to_atom())
|
|
147
|
+
# flow:ferment:lag[1/3] → HOLD
|
|
148
|
+
|
|
149
|
+
# When the advance_when predicate fires:
|
|
150
|
+
result = flow.observe({"biomass": 0.18})
|
|
151
|
+
# result.guidance == Guidance.ADVANCE
|
|
152
|
+
flow.advance()
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
Predicate exceptions are caught as ERROR issues, guidance degrades to HOLD, the flow stays alive — safety-first semantics for safety-critical contexts.
|
|
156
|
+
|
|
157
|
+
## Typed diagnostics
|
|
158
|
+
|
|
159
|
+
The library surfaces its own operational state the same way it classifies observed state:
|
|
160
|
+
|
|
161
|
+
```python
|
|
162
|
+
monitor.update({"mystery": 1.0}) # emits WARNING: BASELINE_MISSING
|
|
163
|
+
monitor.drift("unknown") # emits WARNING: UNKNOWN_COMPONENT
|
|
164
|
+
|
|
165
|
+
# Query it programmatically
|
|
166
|
+
monitor.issues.by_code("BASELINE_MISSING") # [Issue(...)]
|
|
167
|
+
monitor.issues.at_least(IssueLevel.WARNING) # all severe enough
|
|
168
|
+
monitor.diagnose() # JSON-safe dashboard summary
|
|
169
|
+
|
|
170
|
+
# Bridge to your logger
|
|
171
|
+
import logging
|
|
172
|
+
monitor.issues.attach_logger(logging.getLogger("margin"))
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
**Harvest-safe on hard fail:** if `Monitor.update()` or `Flow.observe()` raises, a `FATAL(INTERNAL_ERROR)` issue is recorded *before* the exception propagates. `monitor.issues` survives crashes intact.
|
|
176
|
+
|
|
126
177
|
## Auto-calibrate from data
|
|
127
178
|
|
|
128
179
|
Don't guess thresholds. Derive them from healthy measurements:
|
|
@@ -147,8 +198,10 @@ parser = parser_from_calibration(
|
|
|
147
198
|
| **Contract** | Are we meeting our goals? | `HealthTarget`, `SustainHealth`, `RecoveryThreshold` |
|
|
148
199
|
| **Causal** | Why did this happen? | `CausalGraph`, `CausalLink`, `Explanation` |
|
|
149
200
|
| **Intent** | Can we still get there? | `Intent`, `Feasibility`, `IntentResult` |
|
|
201
|
+
| **Flow** | What stage are we in, and what should we do? | `Flow`, `Stage`, `Guidance`, `FlowResult` |
|
|
202
|
+
| **Issue** | What did the library notice about itself? | `Issue`, `IssueLevel`, `IssueBuffer`, `IssueCode` |
|
|
150
203
|
|
|
151
|
-
`full_step()` orchestrates
|
|
204
|
+
`full_step()` orchestrates the first seven in one call. `Flow` composes them into a sequence. `Issue` is ambient — every layer emits diagnostics into the same buffer.
|
|
152
205
|
|
|
153
206
|
## Domain adapters
|
|
154
207
|
|
|
@@ -57,6 +57,8 @@ Then the correction loop:
|
|
|
57
57
|
- **Causal** — dependency graphs ("api is DEGRADED because db is ABLATED")
|
|
58
58
|
- **Auto-correlation** — discover which components move together from data, with lag detection
|
|
59
59
|
- **Streaming** — incremental trackers: `Monitor.update(values)` updates health + drift + anomaly + correlation in one call
|
|
60
|
+
- **Flow** — ordered stages with typed live guidance: ADVANCE / HOLD / RETRY / ROLLBACK / ESCALATE for multi-stage processes
|
|
61
|
+
- **Issue** — typed diagnostics: library surfaces its own misconfigurations, degraded assumptions, recovered failures
|
|
60
62
|
- **Config** — define everything in YAML/JSON: `margin.load_config("margin.yaml")`
|
|
61
63
|
- **Persistence** — save/restore Monitor state across restarts, batch replay from CSV
|
|
62
64
|
- **Intent** — goal feasibility: `intent.evaluate_monitor(monitor)` → FEASIBLE / AT_RISK / INFEASIBLE with ETA
|
|
@@ -94,6 +96,55 @@ Thresholds(intact=0.02, ablated=0.10, higher_is_better=False)
|
|
|
94
96
|
|
|
95
97
|
One flag. Threads through every comparison, every sigma calculation, every correction decision, every recovery ratio. You never think about it again.
|
|
96
98
|
|
|
99
|
+
## Multi-stage process guidance
|
|
100
|
+
|
|
101
|
+
For processes where each stage has distinct entry/quality/exit criteria — deployment pipelines, fermentation phases, clinical trials, incident response:
|
|
102
|
+
|
|
103
|
+
```python
|
|
104
|
+
from margin import Flow, Stage, Parser, Thresholds
|
|
105
|
+
|
|
106
|
+
flow = Flow(stages=[
|
|
107
|
+
Stage(
|
|
108
|
+
name="lag",
|
|
109
|
+
parser=Parser(baselines={"biomass": 0.1}, thresholds=Thresholds(intact=0.08, ablated=0.02)),
|
|
110
|
+
advance_when=lambda expr: expr.observations[0].value >= 0.15,
|
|
111
|
+
),
|
|
112
|
+
Stage(name="exponential", parser=..., advance_when=...),
|
|
113
|
+
Stage(name="stationary", parser=...),
|
|
114
|
+
], label="ferment")
|
|
115
|
+
|
|
116
|
+
result = flow.observe({"biomass": 0.12})
|
|
117
|
+
print(result.to_atom())
|
|
118
|
+
# flow:ferment:lag[1/3] → HOLD
|
|
119
|
+
|
|
120
|
+
# When the advance_when predicate fires:
|
|
121
|
+
result = flow.observe({"biomass": 0.18})
|
|
122
|
+
# result.guidance == Guidance.ADVANCE
|
|
123
|
+
flow.advance()
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Predicate exceptions are caught as ERROR issues, guidance degrades to HOLD, the flow stays alive — safety-first semantics for safety-critical contexts.
|
|
127
|
+
|
|
128
|
+
## Typed diagnostics
|
|
129
|
+
|
|
130
|
+
The library surfaces its own operational state the same way it classifies observed state:
|
|
131
|
+
|
|
132
|
+
```python
|
|
133
|
+
monitor.update({"mystery": 1.0}) # emits WARNING: BASELINE_MISSING
|
|
134
|
+
monitor.drift("unknown") # emits WARNING: UNKNOWN_COMPONENT
|
|
135
|
+
|
|
136
|
+
# Query it programmatically
|
|
137
|
+
monitor.issues.by_code("BASELINE_MISSING") # [Issue(...)]
|
|
138
|
+
monitor.issues.at_least(IssueLevel.WARNING) # all severe enough
|
|
139
|
+
monitor.diagnose() # JSON-safe dashboard summary
|
|
140
|
+
|
|
141
|
+
# Bridge to your logger
|
|
142
|
+
import logging
|
|
143
|
+
monitor.issues.attach_logger(logging.getLogger("margin"))
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
**Harvest-safe on hard fail:** if `Monitor.update()` or `Flow.observe()` raises, a `FATAL(INTERNAL_ERROR)` issue is recorded *before* the exception propagates. `monitor.issues` survives crashes intact.
|
|
147
|
+
|
|
97
148
|
## Auto-calibrate from data
|
|
98
149
|
|
|
99
150
|
Don't guess thresholds. Derive them from healthy measurements:
|
|
@@ -118,8 +169,10 @@ parser = parser_from_calibration(
|
|
|
118
169
|
| **Contract** | Are we meeting our goals? | `HealthTarget`, `SustainHealth`, `RecoveryThreshold` |
|
|
119
170
|
| **Causal** | Why did this happen? | `CausalGraph`, `CausalLink`, `Explanation` |
|
|
120
171
|
| **Intent** | Can we still get there? | `Intent`, `Feasibility`, `IntentResult` |
|
|
172
|
+
| **Flow** | What stage are we in, and what should we do? | `Flow`, `Stage`, `Guidance`, `FlowResult` |
|
|
173
|
+
| **Issue** | What did the library notice about itself? | `Issue`, `IssueLevel`, `IssueBuffer`, `IssueCode` |
|
|
121
174
|
|
|
122
|
-
`full_step()` orchestrates
|
|
175
|
+
`full_step()` orchestrates the first seven in one call. `Flow` composes them into a sequence. `Issue` is ambient — every layer emits diagnostics into the same buffer.
|
|
123
176
|
|
|
124
177
|
## Domain adapters
|
|
125
178
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: margin
|
|
3
|
-
Version: 0.9.
|
|
3
|
+
Version: 0.9.32
|
|
4
4
|
Summary: Typed health classification, uncertainty algebra, and correction auditing for any system that measures things and needs to explain what happened.
|
|
5
5
|
Author: Cope Labs LLC
|
|
6
6
|
License: MIT
|
|
@@ -86,6 +86,8 @@ Then the correction loop:
|
|
|
86
86
|
- **Causal** — dependency graphs ("api is DEGRADED because db is ABLATED")
|
|
87
87
|
- **Auto-correlation** — discover which components move together from data, with lag detection
|
|
88
88
|
- **Streaming** — incremental trackers: `Monitor.update(values)` updates health + drift + anomaly + correlation in one call
|
|
89
|
+
- **Flow** — ordered stages with typed live guidance: ADVANCE / HOLD / RETRY / ROLLBACK / ESCALATE for multi-stage processes
|
|
90
|
+
- **Issue** — typed diagnostics: library surfaces its own misconfigurations, degraded assumptions, recovered failures
|
|
89
91
|
- **Config** — define everything in YAML/JSON: `margin.load_config("margin.yaml")`
|
|
90
92
|
- **Persistence** — save/restore Monitor state across restarts, batch replay from CSV
|
|
91
93
|
- **Intent** — goal feasibility: `intent.evaluate_monitor(monitor)` → FEASIBLE / AT_RISK / INFEASIBLE with ETA
|
|
@@ -123,6 +125,55 @@ Thresholds(intact=0.02, ablated=0.10, higher_is_better=False)
|
|
|
123
125
|
|
|
124
126
|
One flag. Threads through every comparison, every sigma calculation, every correction decision, every recovery ratio. You never think about it again.
|
|
125
127
|
|
|
128
|
+
## Multi-stage process guidance
|
|
129
|
+
|
|
130
|
+
For processes where each stage has distinct entry/quality/exit criteria — deployment pipelines, fermentation phases, clinical trials, incident response:
|
|
131
|
+
|
|
132
|
+
```python
|
|
133
|
+
from margin import Flow, Stage, Parser, Thresholds
|
|
134
|
+
|
|
135
|
+
flow = Flow(stages=[
|
|
136
|
+
Stage(
|
|
137
|
+
name="lag",
|
|
138
|
+
parser=Parser(baselines={"biomass": 0.1}, thresholds=Thresholds(intact=0.08, ablated=0.02)),
|
|
139
|
+
advance_when=lambda expr: expr.observations[0].value >= 0.15,
|
|
140
|
+
),
|
|
141
|
+
Stage(name="exponential", parser=..., advance_when=...),
|
|
142
|
+
Stage(name="stationary", parser=...),
|
|
143
|
+
], label="ferment")
|
|
144
|
+
|
|
145
|
+
result = flow.observe({"biomass": 0.12})
|
|
146
|
+
print(result.to_atom())
|
|
147
|
+
# flow:ferment:lag[1/3] → HOLD
|
|
148
|
+
|
|
149
|
+
# When the advance_when predicate fires:
|
|
150
|
+
result = flow.observe({"biomass": 0.18})
|
|
151
|
+
# result.guidance == Guidance.ADVANCE
|
|
152
|
+
flow.advance()
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
Predicate exceptions are caught as ERROR issues, guidance degrades to HOLD, the flow stays alive — safety-first semantics for safety-critical contexts.
|
|
156
|
+
|
|
157
|
+
## Typed diagnostics
|
|
158
|
+
|
|
159
|
+
The library surfaces its own operational state the same way it classifies observed state:
|
|
160
|
+
|
|
161
|
+
```python
|
|
162
|
+
monitor.update({"mystery": 1.0}) # emits WARNING: BASELINE_MISSING
|
|
163
|
+
monitor.drift("unknown") # emits WARNING: UNKNOWN_COMPONENT
|
|
164
|
+
|
|
165
|
+
# Query it programmatically
|
|
166
|
+
monitor.issues.by_code("BASELINE_MISSING") # [Issue(...)]
|
|
167
|
+
monitor.issues.at_least(IssueLevel.WARNING) # all severe enough
|
|
168
|
+
monitor.diagnose() # JSON-safe dashboard summary
|
|
169
|
+
|
|
170
|
+
# Bridge to your logger
|
|
171
|
+
import logging
|
|
172
|
+
monitor.issues.attach_logger(logging.getLogger("margin"))
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
**Harvest-safe on hard fail:** if `Monitor.update()` or `Flow.observe()` raises, a `FATAL(INTERNAL_ERROR)` issue is recorded *before* the exception propagates. `monitor.issues` survives crashes intact.
|
|
176
|
+
|
|
126
177
|
## Auto-calibrate from data
|
|
127
178
|
|
|
128
179
|
Don't guess thresholds. Derive them from healthy measurements:
|
|
@@ -147,8 +198,10 @@ parser = parser_from_calibration(
|
|
|
147
198
|
| **Contract** | Are we meeting our goals? | `HealthTarget`, `SustainHealth`, `RecoveryThreshold` |
|
|
148
199
|
| **Causal** | Why did this happen? | `CausalGraph`, `CausalLink`, `Explanation` |
|
|
149
200
|
| **Intent** | Can we still get there? | `Intent`, `Feasibility`, `IntentResult` |
|
|
201
|
+
| **Flow** | What stage are we in, and what should we do? | `Flow`, `Stage`, `Guidance`, `FlowResult` |
|
|
202
|
+
| **Issue** | What did the library notice about itself? | `Issue`, `IssueLevel`, `IssueBuffer`, `IssueCode` |
|
|
150
203
|
|
|
151
|
-
`full_step()` orchestrates
|
|
204
|
+
`full_step()` orchestrates the first seven in one call. `Flow` composes them into a sequence. `Issue` is ambient — every layer emits diagnostics into the same buffer.
|
|
152
205
|
|
|
153
206
|
## Domain adapters
|
|
154
207
|
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "margin"
|
|
7
|
-
version = "0.9.
|
|
7
|
+
version = "0.9.32"
|
|
8
8
|
description = "Typed health classification, uncertainty algebra, and correction auditing for any system that measures things and needs to explain what happened."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = {text = "MIT"}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|