Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Architecture

A small workspace of focused crates. The guiding rule: the contract is engine-independent, so the heavy machinery can change without the output shape moving.

crates/
  ax-core        contract types: RecordSet, the anomaly taxonomy, the tq1
                 envelope, evidence handles, deterministic reductions.
                 Deliberately no heavy deps — keeps the contract independent
                 and the mutation gate fast. (crate: anomalyx-core)
  ax-normalize   any input format → RecordSet. CSV/TSV/NDJSON/JSON via a lean
                 deterministic reader; Parquet/Arrow IPC via the Polars
                 backbone, behind the default-on `polars` feature.
                 (crate: anomalyx-normalize)
  ax-detect      the Detector trait + registry; the nine detectors and their
                 math (assembled from statrs, not reinvented).
                 (crate: anomalyx-detect)
  anomalyx       the four-verb CLI surface — the installable binary.
  ax-validate    NIST StRD validation + stress harness (publish = false).

Engine independence

Polars lives only inside ax-normalize’s binary-format reader. It reads a DataFrame and lowers it to a RecordSet; no Polars type ever reaches a detector, the envelope, or the contract. That’s what lets the text-only build drop Polars entirely, and what keeps ax-core — where the taxonomy and envelope live — a tiny, dependency-light crate that the mutation gate can sweep quickly.

Adding a format (the parser plugin system)

ax-normalize is a parser-plugin registry. Each format is an independent FormatParser (id, extensions, content sniff, parse) living in its own file under crates/ax-normalize/src/parsers/. The ParserRegistry resolves a byte stream by file extension first, then by the highest-confidence sniff (deterministic: confidences are registered in descending order). Adding a format is a new parsers/<fmt>.rs plus one register(...) line in default_registry — no central match to edit. See the open format issues for the backlog.

The detector contract

A Detector is itself a contract. Given a ScanContext { current, baseline } it either runs and emits Findings, or declares honest Absence. The Registry runs the set deterministically and merges everything into one Report, which the CLI turns into a tq1 envelope. Adding a detector is: implement the trait, register it, and gate it.

Naming

The crates.io packages are namespaced under the brand (anomalyx-core, anomalyx-normalize, anomalyx-detect) because the short ax-* names were taken; the in-source module/import names remain ax_core etc. via Cargo’s dependency-rename, so the code reads cleanly.