---
name: zeno-divergent
description: Integrate Zeno Divergent, a model-relative surprise / model-validity forecaster, into an application. Use when a codebase needs to detect that its own forecasting or scoring models are about to stop being valid, surface typed surprise, blindness and admission actions.
homepage: https://zenodivergent.dev
model: mbarbosa1/zeno-divergent-v1
license: research-preview (synthetic training data — advisory use only)
checkpoint: main = v0.3.1 (v0.3.0 real-corpus weights, transformers-native); browser/ONNX path = v0.2.3
skill-revision: 2
---

# Zeno Divergent — integration skill

Zeno Divergent does **not** forecast the world. It forecasts **when the models that
describe the world stop being valid**. Its input is a set of *reference
expectations* (predictors, simulators, physical bounds, policy rules) plus how each
one is currently failing; attention runs over models, not features.

Use it as a **sidecar** to an existing stack. Never retrain or blend it into the
reference models it watches.

## Non-negotiable rules

1. `surprise_index` / `potential_units` (Ψ) is a **ranking score**, not a
   probability of harm. Never render it as a percentage risk.
2. Always surface `blindness` next to the score. High blindness + low score means
   **"not observed"**, never "not at risk". Suppressing it is a misuse.
3. `action == "request_evidence"` means the model declines to make a validity
   claim. Render it as a request, not as "all clear".
4. Never bind an `entity_id` to a natural person, account holder or household.
   Entities are models, sensors, instruments and processes.
5. Never wire the output to an automated action (trade, dispatch, switching,
   evacuation). A named human must see the trace first.
6. The published checkpoint is trained on synthetic data. State that in any UI
   that presents its numbers.

## Wiring your own models in — 5 steps

1. **List your references.** Every model, bound, base rate or rule that produces an
   expectation for the same observed quantity. One is enough to start; two that can
   disagree is where this becomes useful.
2. **Emit one row per reference, per timestep** with the six fields below. Keep your
   predictors exactly as they are — Zeno never retrains or blends them.
3. **Normalize** each residual by that reference's own uncertainty (see below).
4. **Score** with `score_sequence(steps, entity_id=...)`, or run the ONNX graph.
5. **Render** score + typed surprise + blindness + admission action together, with the
   reference table that produced them. Never a single aggregated risk number.

## Normalization — the field that breaks integrations

`residual` must be expressed in the reference's **own** uncertainty units:

```python
residual = (observed - prediction) / max(sigma, 1e-6)
```

Feeding raw units makes fusion collapse onto whichever reference has the largest
numeric scale, and the typed surprise becomes meaningless. Related rules:

- `uncertainty` must be that reference's honest spread (quantile head, ensemble std,
  interval half-width). A hardcoded constant σ contributes nothing.
- `validity` and `freshness` decay continuously, e.g.
  `freshness = exp(-age_days / half_life_days)`. Hard-switching them between 0 and 1
  makes blindness jump discontinuously and destroys lead-time behaviour.
- Roles are not interchangeable: a `physical` bound failing means something different
  from a `predictive` model drifting. Label them correctly.
- Below ~15 events an entity is cold-start — mark those reports provisional.

## Fusion — attention over models, not features

Each timestep carries K reference rows. The encoder attends **across references**,
weighting roughly by disagreement × validity × freshness, and the residual structure
that survives that weighting becomes the ten-axis surprise latent `z^S`, which is read
out as typed surprise, Ψ, blindness, horizons and an admission action.

Consequences worth designing around:

- A single reference degenerates into a residual monitor. Add a second, structurally
  different reference (a bound, a base rate) before judging the output.
- When references go stale or drop out, attention mass has nowhere to go: `blindness`
  rises rather than the score. That is the intended behaviour, not a failure.
- `z^S` is shared across domains; Ψ is comparable **within** an entity population, not
  across unrelated deployments.

## Python — no `trust_remote_code`

```bash
pip install zeno-divergent
```

```python
from transformers import AutoModel
import zeno_divergent  # registers config, model and pipeline

model = AutoModel.from_pretrained("mbarbosa1/zeno-divergent-v1").eval()
report = model.score_sequence(steps, entity_id="pump_17")
print(report.kind, report.surprise_index, report.blindness, report.action)
```

`steps` is `[T][K]` dicts (or a `[T, K, 6]` array), one entry per reference model
per timestep:

```python
step = [
  {"prediction": 0.42, "uncertainty": 0.08, "residual": 0.31,
   "role": "predictive", "validity": 0.9, "freshness": 1.0},   # your forecaster
  {"prediction": 0.40, "uncertainty": 0.20, "residual": 0.33,
   "role": "physical",  "validity": 1.0, "freshness": 0.7},    # a bound / simulator
]
```

All fields are scale-free. `role` ∈ `predictive | physical | historical |
ensemble | policy`. `validity` and `freshness` ∈ [0, 1]; drop `freshness` toward 0
when a reference is stale and `validity` toward 0 when it is out of its domain.

Raw event streams can be mapped with the documented light adapter — fine for a
demo, not for evaluation:

```python
from zeno_divergent import adapt_events
steps = adapt_events([{"amount": 120.0, "gap_ms": 900}, ...])
```

## Python — pipeline

```python
import zeno_divergent
detector = zeno_divergent.surprise_pipeline()
detector([{"action": "transfer", "amount": 50000, "gap_ms": 12}])
```

With remote code instead: `pipeline("surprise-detection",
model="mbarbosa1/zeno-divergent-v1", trust_remote_code=True)`.

## Browser / TypeScript — ONNX

The same checkpoint is exported to ONNX with dynamic reference axes and runs
client-side with `onnxruntime-web`. Input tensor `references` has shape
`[1, T, K, 6]` in the field order above; outputs include the surprise index,
the ten-axis latent `z^S`, blindness and per-horizon failure probabilities.

```ts
import * as ort from "onnxruntime-web";
const session = await ort.InferenceSession.create("/models/zeno-divergent-v1.onnx");
const refs = new ort.Tensor("float32", flat, [1, T, K, 6]);
const out = await session.run({ references: refs });
```

## Output contract

| Field | Meaning |
|---|---|
| `kind` | typed surprise — which region of `z^S` the state sits in (not a severity) |
| `surprise_index` / `potential_units` | Ψ, a ranking score |
| `blindness` | evidence loss; read with rule 2 above |
| `horizons` | near / medium / long model-failure probabilities |
| `action` | `ignore \| remember \| open_regime \| request_evidence \| rebuild` |

`calibration_table.json` (held-out percentiles) and `kind_rules.json` (region
taxonomy and read rule per region) ship in the model repo. Below ~15 events an
entity is cold-start; mark the report provisional.

## Suggested UI

Score + typed-surprise label + blindness bar + admission action, in that order,
with the reference table that produced them. Never a single aggregated risk
number across domains — the output contract has no such number.

## Links

- Model: https://huggingface.co/mbarbosa1/zeno-divergent-v1
- Demo Space: https://huggingface.co/spaces/mbarbosa1/zeno-divergent
- Benchmark protocol + live workspace: https://zenodivergent.dev
- This skill: https://zenodivergent.dev/zeno-divergent.skill.md
