integrate
One link, and your agent knows how to wire your models in
Zeno Divergent runs as a sidecar to the models you already have. Copy the skill link into your coding agent — it carries the input contract, normalization rules, fusion semantics, the output contract and the misuse rules.
step one
Copy the link
A single markdown file that any coding agent can read. Nothing to install to get started.
integration skill
https://zenodivergent.dev/zeno-divergent.skill.mdCanonical URL. Also served as /agents.md, and indexed from /llms.txt.
paste this into your agent
Read https://zenodivergent.dev/zeno-divergent.skill.md and wire my forecasting models into Zeno Divergent as a validity sidecar. Keep my predictors unchanged; emit one reference row per model per timestep and render score, typed surprise, blindness and admission action together.Gives the agent the link plus the one architectural constraint that matters: your predictors stay untouched.
model repo
mbarbosa1/zeno-divergent-v1Hugging Face. Loads without trust_remote_code via the pip package.
onnx checkpoint
https://zenodivergent.dev/models/zeno-divergent-v1.onnxDynamic reference axes; runs client-side with onnxruntime-web.
step two
What you actually plug in
Zeno does not read your features. It reads your models: one reference row per model, per timestep. Everything below is scale-free, so a temperature model and a payments model can sit in the same tensor.
| field | range | meaning | example |
|---|---|---|---|
| prediction | any scale | What this reference expected for the observed quantity at this timestep. | your gradient-boosted forecaster outputs 0.42 expected load |
| uncertainty | > 0, same units as prediction | This reference's own stated spread — a σ, an interval half-width, an ensemble std. | 0.08 from the model's quantile head |
| residual | normalized, ≈ [-6, 6] | How wrong the reference just was, expressed in its own uncertainty units. | (observed − prediction) / uncertainty = 0.31 |
| role | predictive | physical | historical | ensemble | policy | What kind of expectation this is. Roles are not interchangeable — a physical bound failing means something different from a forecaster drifting. | "physical" for a conservation or capacity bound |
| validity | [0, 1] | How far inside its declared domain this reference currently is. Out of domain → toward 0. | 0.4 when the input is outside the training envelope |
| freshness | [0, 1] | How recently this reference was actually updated or refit. | exp(−age_days / 30) for a monthly refit |
normalization
Scale-free, or the biggest number wins
The single most common integration error is feeding raw residuals. Fusion then reduces to 'whichever reference has the largest units', and the typed surprise becomes meaningless.
normalize each reference by itself
# right: each reference is scaled by its own stated uncertainty
residual = (observed - pred) / max(sigma, 1e-6)
# wrong: raw units — the reference with the biggest numbers dominates fusion
residual = observed - pred
# staleness and domain, decayed rather than hard-switched
freshness = math.exp(-age_days / half_life_days)
validity = 0.0 if out_of_domain else in_domain_score # [0, 1]- residual is in units of the reference's own uncertainty — never in a global or dataset-wide scale. Two references disagreeing by 2σ each is the signal; their absolute magnitudes are not.
- uncertainty must be that reference's honest spread. A model with a hardcoded constant σ will look permanently well-calibrated and contribute nothing.
- validity and freshness decay continuously. Hard-switching them to 0/1 makes blindness jump discontinuously and destroys the lead-time behaviour.
- Below roughly 15 events an entity is cold-start. Mark those reports provisional rather than acting on them.
fusion
Attention over models, not over features
Each timestep's K reference rows are attended jointly. The encoder learns which reference to trust for this state, and the residual structure that survives that weighting is what becomes the surprise latent.
outputs
What comes back, and what it is not
The output contract is deliberately narrow. There is no single aggregated cross-domain risk number, and adding one is a misuse.
kind
Typed surprise: which region of z^S the state sits in — drift, contradiction, novelty, silence.
Not a severity ladder. A 'novelty' is not worse than a 'drift'.
surprise_index / Ψ
A ranking score for triage: which of your entities deserves a human look first.
Not a probability of harm, and never renderable as a percentage risk.
blindness
Evidence loss — how much of the reference set went stale, out of domain or missing.
Not a confidence interval. High blindness + low score means not observed, never not at risk.
horizons
Near / medium / long probabilities that the reference models stop being valid.
Not forecasts of the world's variable. They are forecasts about your models.
action
ignore | remember | open_regime | request_evidence | rebuild — admission control on your own stack.
Not an automated trigger. request_evidence is a declined claim, not an all clear.
quickstart
Three ways to run it
Same checkpoint in all three. Pick the boundary that fits your stack.
install
pip install zeno-divergentpython — no trust_remote_code
from transformers import AutoModel
import zeno_divergent # registers config, model and pipeline
model = AutoModel.from_pretrained("mbarbosa1/zeno-divergent-v1").eval()
# one row per reference model, per timestep
step = [
{"prediction": 0.42, "uncertainty": 0.08, "residual": 0.31,
"role": "predictive", "validity": 0.9, "freshness": 1.0},
{"prediction": 0.40, "uncertainty": 0.20, "residual": 0.33,
"role": "physical", "validity": 1.0, "freshness": 0.7},
]
report = model.score_sequence([step] * 32, entity_id="pump_17")
print(report.kind, report.surprise_index, report.blindness, report.action)python — pipeline
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
import * as ort from "onnxruntime-web";
const session = await ort.InferenceSession.create(
"https://zenodivergent.dev/models/zeno-divergent-v1.onnx",
);
// [1, T, K, 6] — field order:
// prediction, uncertainty, residual, role_id, validity, freshness
const refs = new ort.Tensor("float32", flat, [1, T, K, 6]);
const out = await session.run({ references: refs });
const psi = out.surprise_index.data[0];
const blindness = out.blindness.data[0];rules
Six things that make an integration wrong
These are part of the skill file, so an agent reading the link enforces them for you.
- 01Ψ is a ranking score, not a probability of harm. Never render it as a percentage risk.
- 02Always surface blindness next to the score. High blindness + low score means 'not observed'.
- 03action == "request_evidence" means the model declines to make a validity claim. Render it as a request.
- 04Never bind an entity_id to a natural person, account holder or household. Entities are models, sensors, instruments, processes.
- 05Never wire the output to an automated action. A named human must see the trace first.
- 06The published checkpoint is trained on synthetic data. State that in any UI presenting its numbers.
artifacts · skill.md · agents.md · llms.txt · model card