anomaly

Find the record that doesn't fit.

A Spotlight plugin for data.

Anomaly shows your agent which datasets cover your story and which checks fit them. It runs those checks and flags every record that stands out as a lead you can trace.

Records flow in from the sources an agent fetched. Detectors test every one, most pass straight through, and the few that don't fit come out as leads for a person to check.

Sources and detectors meet at a domain.

A domain is a type of data, such as lobbying or procurement. A source says which domain it covers, and a detector says which domain it reads. When a detector joins several tables, the domain publishes their shape, so any source that fills it works with every detector in that family. No source is ever wired to a detector.

Diagram of the selected domain's sources and detectors.

Look through the catalogue.

The catalogue is every data source, domain and detector an agent can look up. It is generated from the repository, so what you see here is exactly what anomaly.context returns.

An agent asks the questions and code does the counting.

Anomaly sits between an investigating agent, such as Spotlight, and the data. The model chooses what to look at, and deterministic code computes every flag. Pick a step to see what runs there.

  1. Ask

    A journalist or agent starts with a question about a person, a company or a beat.

  2. Look up

    anomaly.context lists the data for the domain and the detectors that fit it.

  3. Fetch

    The agent gets the tables itself, following each source's SKILL.md.

  4. Run

    anomaly.oneshot runs the detector's SQL in a sealed DuckDB. The same tables always give the same result.

  5. Lead

    Each flagged record comes back as a JSON line with its detector, inputs and parameters.

  6. Gate

    A person accepts or dismisses each lead. A detector never decides what's true.

Install it once, then three commands do the work.

Anomaly is a local Python package with no account, server or API key of its own. It needs Python 3.11 or later and uv.

Installgit clone https://github.com/buriedsignals/Anomaly.git && cd Anomaly && uv sync
Look upuv run python -m anomaly.context --domain lobbying
Sourceuv run python -m anomaly.context --source global/opensanctions
Runuv run python -m anomaly.oneshot --table examples/donations.csv --request examples/zscore_request.json --approved

Request format, domain detectors and every refusal rule are in docs/catalogue.md. The run command uses a small example in the repository and flags one donation. Without Spotlight, Anomaly can also run a full standalone case: see the README.

Join the Lab.

↳ now open

Indicator Lab

The Buried Signals tool suite, a monthly live workshop and Lab Newsletter, office hours every two weeks, and the training course.

$22.95/month · billed monthly, cancel anytime.

Join Indicator Lab

Newsletters

1,000+ journalists already reading.