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.md

Canonical 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-v1

Hugging Face. Loads without trust_remote_code via the pip package.

onnx checkpoint

https://zenodivergent.dev/models/zeno-divergent-v1.onnx

Dynamic 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.

fieldrangemeaningexample
predictionany scaleWhat this reference expected for the observed quantity at this timestep.your gradient-boosted forecaster outputs 0.42 expected load
uncertainty> 0, same units as predictionThis reference's own stated spread — a σ, an interval half-width, an ensemble std.0.08 from the model's quantile head
residualnormalized, ≈ [-6, 6]How wrong the reference just was, expressed in its own uncertainty units.(observed − prediction) / uncertainty = 0.31
rolepredictive | physical | historical | ensemble | policyWhat 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

python
# 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.

predictiveyour forecasterphysicalbound / simulatorhistoricalempirical base ratepolicyrule or thresholdattention over models, not features — weight ∝ disagreement × validity × freshnessz^S10-axis latent+ temporal statekindtyped surpriseΨranking scoreblindnessevidence lossactionadmission control
Each timestep carries K reference rows. The encoder attends across references — a single reference gives you a residual monitor; the second, disagreeing reference is what makes the fusion informative. When references go stale (freshness → 0) or drop out, weight mass has nowhere to go and blindness rises instead of the score.

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

bash
pip install zeno-divergent

python — no trust_remote_code

python
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

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

typescript
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.

  1. 01Ψ is a ranking score, not a probability of harm. Never render it as a percentage risk.
  2. 02Always surface blindness next to the score. High blindness + low score means 'not observed'.
  3. 03action == "request_evidence" means the model declines to make a validity claim. Render it as a request.
  4. 04Never bind an entity_id to a natural person, account holder or household. Entities are models, sensors, instruments, processes.
  5. 05Never wire the output to an automated action. A named human must see the trace first.
  6. 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