API
AssaySentinel.AssaySentinel — Module
AssaySentinelKnow when the measurement changed before the science does.
AssaySentinel.jl is a high-performance analytical quality and drift-detection framework for repeated quantitative measurements. It monitors the measurement process itself — instruments, reagent lots, calibrations, batches, and controls — rather than diagnosing patients.
Safety boundary
This software is intended for research, analytical-quality assessment, method development, and scientific decision support. It is not a diagnostic medical device and must not independently determine patient diagnosis or treatment.
Example
using AssaySentinel
using Dates
stream = AssayStream(analyte = :glucose, unit = "mg/dL", instrument = "Analyzer-A")
push!(stream, Measurement(value = 101.2, timestamp = now(), batch = "B104", lot = "R22", control = true))
report = analyze(stream)
explain(report)AssaySentinel.AbstractDetector — Type
AbstractDetectorGeneric detector API for research extensions.
fit!(detector, baseline)
update!(detector, measurement)
result(detector)AssaySentinel.AbstractEvent — Type
AbstractEventAssaySentinel.Alert — Type
AlertAnalytical alert. Severity is one of :info, :watch, :warning, :critical. These are analytical-process severities, not clinical interpretations.
AssaySentinel.Assay — Type
AssayAssaySentinel.AssayPanel — Type
AssayPanelSimultaneous monitoring of many analytes.
AssaySentinel.AssaySentinelError — Type
AssaySentinelErrorBase error type for AssaySentinel.
AssaySentinel.AssayStream — Type
AssayStream{T}Ordered stream of measurements for one analyte / measurement system.
AssaySentinel.AttributionResult — Type
AttributionResultTemporal association between a detected change and nearby operational events. Never claims causation from association alone.
AssaySentinel.Baseline — Type
BaselineReference window against which incoming measurements are compared.
AssaySentinel.Batch — Type
BatchAssaySentinel.BatchEffectResult — Type
BatchEffectResultAssaySentinel.Calibration — Type
CalibrationAssaySentinel.CalibrationCurve — Type
CalibrationCurveAssaySentinel.ChangePointResult — Type
ChangePointResultAssaySentinel.ComparisonResult — Type
ComparisonResultAssaySentinel.ControlSample — Type
ControlSampleAssaySentinel.ControlSeries — Type
ControlSeriesAssaySentinel.DistributionComparison — Type
DistributionComparisonAssaySentinel.DriftEvent — Type
DriftEventAssaySentinel.DriftResult — Type
DriftResultAssaySentinel.Event — Type
EventGeneric operational event on the measurement system.
AssaySentinel.EventTimeline — Type
EventTimelineAssaySentinel.Experiment — Type
ExperimentAssaySentinel.HierarchicalSiteResult — Type
HierarchicalSiteResultStudy-level random-effects summary. Attribution is statistical (global vs site-specific vs mixed), not causal.
AssaySentinel.IncrementalCUSUM — Type
IncrementalCUSUMPage CUSUM with Welford moments. O(1) update.
AssaySentinel.IncrementalEWMA — Type
IncrementalEWMARoberts (1959) EWMA with incremental variance.
AssaySentinel.Instrument — Type
InstrumentAssaySentinel.InsufficientDataError — Type
InsufficientDataErrorRaised when a method cannot be applied because too few valid observations remain after missing/NaN handling.
AssaySentinel.Measurement — Type
Measurement{T}A single quantitative observation with provenance metadata.
Keyword lot is accepted as an alias for reagent_lot. Missing values and NaN must be represented explicitly; they are never coerced to zero.
AssaySentinel.Method — Type
MethodAssaySentinel.OutlierResult — Type
OutlierResultAssaySentinel.PanelReport — Type
PanelReportFrozen multi-analyte product: per-analyte QualityReports plus a panel reconstruction. Schema version is stored for reload compatibility.
.panel is an alias of .name so older analyze(panel) NamedTuple accessors keep working.
AssaySentinel.PartitionResult — Type
PartitionResultAssaySentinel.ProvenanceRecord — Type
ProvenanceRecordAssaySentinel.QCRule — Type
QCRuleAssaySentinel.QCRuleResult — Type
QCRuleResultAssaySentinel.QCSpec — Type
QCSpecAssaySentinel.QualityReport — Type
QualityReportAssaySentinel.ReagentLot — Type
ReagentLotAssaySentinel.Reconstruction — Type
ReconstructionA defensible reconstruction of how the measurement system behaved: ordered story beats, uncertainty, lot/instrument evidence, SVG charts, and a provenance graph. Produced by analyze / reconstruct.
AssaySentinel.ReferenceInterval — Type
ReferenceIntervalAssaySentinel.Sentinel — Type
SentinelStreaming monitor over a baseline. Incremental detectors, alert callbacks, persistence, and cooldown are first-class.
AssaySentinel.SentinelScore — Type
SentinelScoreComposite analytical-stability score on 0–100. This is not a patient-risk score. Components are always retained and never hidden.
AssaySentinel.Site — Type
SiteAssaySentinel.SiteEffect — Type
SiteEffectEmpirical-Bayes site location after hierarchical shrinkage.
AssaySentinel.StoryBeat — Type
StoryBeatOne dated chapter in an analytical reconstruction.
AssaySentinel.Study — Type
StudyHierarchical monitoring container: Study → Site → Instrument.
AssaySentinel.StudyReport — Type
StudyReportFrozen study-level product: per-site QualityReports plus the hierarchical combine. Schema version is stored for reload compatibility.
AssaySentinel.StudySentinel — Type
StudySentinelStreaming monitor for several sites. A study-level alert fires when enough sites alarm inside the concordance window (default: 2 sites within 7 days). Concordance alerts themselves respect concordance_cooldown so a persistent shared signal is not re-emitted on every subsequent observation.
AssaySentinel.UncertaintyBudget — Type
UncertaintyBudgetHow measurement uncertainty and analytical scatter combine. This is analytical-process uncertainty, not a clinical interval.
AssaySentinel.UnitMismatchError — Type
UnitMismatchErrorRaised when two quantities are compared or combined without an explicit unit conversion. mg/dL is never treated as equivalent to mmol/L.
AssaySentinel.analyze — Method
analyze(panel::AssayPanel; parallel=false, rng)Analyze each analyte stream independently and return a PanelReport with a panel reconstruction (status chart, story, provenance). Per-analyte RNGs are independent. Units are never pooled. .panel remains an alias of .name for older NamedTuple-style access.
AssaySentinel.analyze — Method
analyze(stream::AssayStream; rng, outlier_policy=:annotate, parallel=false)Combine change-point, drift, QC, distribution, outlier, and attribution evidence into a QualityReport.
AssaySentinel.analyze — Method
analyze(study, streams; rng)Analyze each site stream and combine them with hierarchical_sites. streams maps site name → AssayStream. The study reconstruction (forest plot, sharing narrative, uncertainty budget) is attached to the StudyReport.
AssaySentinel.annotate_outliers — Method
annotate_outliers(x; kwargs...)Alias of detect_outliers. Removal is never performed here.
AssaySentinel.assess_partitions — Method
assess_partitions(data; group, value)Harris–Boyd style statistical partitioning evidence (Harris & Boyd 1990). Does not recommend clinical partitions.
AssaySentinel.calibrate — Method
calibrate(concentrations, responses; model=:linear, weights=nothing)Fit a calibration curve.
Models: :linear, :weighted_linear, :polynomial, :robust, :spline, :fourpl.
AssaySentinel.calibration_diagnostics — Method
calibration_diagnostics(curve)Residual, runs, relative-error, and (when replicates exist) lack-of-fit diagnostics for a fitted calibration. Does not claim clinical impact.
AssaySentinel.check_units — Method
check_units(a, b)Throw UnitMismatchError unless a and b denote the same unit after alias normalization. Empty units are treated as unspecified and allowed.
AssaySentinel.compare_calibrations — Method
compare_calibrations(curve1, curve2)Report slope/intercept/nonlinearity/residual change. Does not claim a clinical impact.
AssaySentinel.compare_distribution — Method
compare_distribution(baseline, current; method=:auto)AssaySentinel.compare_lots — Function
compare_lots(data, lotcol; value=:value)Estimate whether a lot transition corresponds to location, variance, or distributional change.
AssaySentinel.compare_methods — Method
compare_methods(a, b; method=:ba)Compare paired measurements from two methods.
Methods:
:ba— Bland–Altman (Bland & Altman 1986):deming— Deming regression (errors-in-variables):passing_bablok— Passing & Bablok (1983):ols— ordinary least squares:robust— Theil–Sen
AssaySentinel.compare_sites — Method
compare_sites(data; site, value)AssaySentinel.convert_unit — Method
convert_unit(value, from, to; molar_mass=nothing, factor=nothing)Explicit conversion only. Common clinical-chemistry conversions require molar_mass (g/mol) when moving between mass and molar concentration.
This function never guesses an analyte.
AssaySentinel.correct_batch_effects — Method
correct_batch_effects(X, batch; method=:combat, design=nothing)Multi-feature ComBat. X is observations × features. Empirical-Bayes priors for each batch are estimated across features (Johnson et al. 2007).
AssaySentinel.correct_batch_effects — Method
correct_batch_effects(dataset; method=:median, batch, value, biological_group=nothing)Optional correction. Always returns a new table-like vector of named tuples and never mutates the original. The transformation is intended to be stored in provenance by the caller.
Methods:
:median— location centering to the grand mean:quantile— batch-wise quantile matching to the pooled empirical distribution:ruv— control-anchored unwanted-variation removal (RUV-lite):combat— parametric empirical-Bayes location/scale adjustment (Johnson, Li & Rabinovic 2007). Batch locationγand scaleδ²are shrunk toward hyperparameters estimated from the batch-level moments. Whenbiological_groupis set, that design is protected (not removed).
AssaySentinel.detect_batch_effects — Method
detect_batch_effects(dataset; batch, value, biological_group=nothing)Distinguish likely technical variation from variation associated with an experimental grouping. Does not correct data.
AssaySentinel.detect_changes — Method
detect_changes(data; method=:auto, timestamps=nothing, has_controls=false, rng)Detect change points in a univariate series.
Methods:
:cusum— Page (1954) two-sided CUSUM plus CUSUM-path localization:likelihood— Gaussian mean-change likelihood ratio / SIC scan:pelt— PELT for piecewise mean (Killick, Fearnhead & Eckley 2012):robust_median— CUSUM on robustly standardized observations:rolling— rolling two-sample Welch tests:bayesian— Fearnhead (2006) product-partition posterior (multiple changes):turing— MCMC via the Turing.jl extension (model=:single|:multiple|:hierarchical,ncutsfor piecewise means,sampler=:mh|:nuts):kernel— energy-distance scan (Székely & Rizzo):auto— choose from sample size, tail weight, missingness, cadence
AssaySentinel.detect_drift — Method
detect_drift(X::AbstractMatrix; method=:mahalanobis, timestamps=nothing)X is observations × features. Methods:
:mahalanobis— Hotelling-style distance from baseline mean/covariance:pca— Hotelling T² on leading principal components:covariance— Frobenius shift of correlation matrices:energy— multivariate energy distance between halves
AssaySentinel.detect_drift — Method
detect_drift(data; kind=:auto, timestamps=nothing, baseline=nothing, rng)Detect analytical drift of a specified kind.
Kinds: :linear, :nonlinear, :sudden, :cyclic, :variance, :distribution, :calibration, :auto.
AssaySentinel.detect_outliers — Method
detect_outliers(x; method=:mad, k=3.5)Detect outliers without removing them.
Methods:
:mad— robust z-score using normalized MAD (Rousseeuw & Croux 1993):iqr— Tukey fences atQ1 − k IQR,Q3 + k IQR(Tukey 1977); default k=1.5:zscore— classical |z| > k:robust_z— alias of:mad
AssaySentinel.evaluate_detector — Function
evaluate_detector(kind; nrep=20, n=800, rng)Compare detection delay, false-positive rate, and sensitivity on simulated streams. Useful as a research-platform harness for new detectors.
AssaySentinel.explain — Method
explain(result)Human-readable reconstruction of why a conclusion was reached. Leads with the dated analytical story, then the uncertainty budget, then the evidence that assigned status. Distinguishes observed facts, statistical results, algorithmic inference, and user annotations.
AssaySentinel.forest_chart — Function
forest_chart(result::HierarchicalSiteResult)Makie forest plot of site means with a prediction-interval overlay. Requires the optional Makie.jl extension. The SVG counterpart svg_forest_chart is always available in core.
AssaySentinel.hierarchical_sites — Method
hierarchical_sites(data; site, value=:value, timestamps=:timestamp, method=:eb)Random-effects site model (DerSimonian–Laird / empirical Bayes):
- Per-site mean, SD, and standard error
- Between-site τ² and within-site σ²
- Shrink site means toward the grand mean
- Higgins I² and a 95% prediction interval for a new site mean
- Per-site and pooled drift
- Cochran Q on site drift magnitudes →
:global,:site_specific,:mixed, or:stable
method=:turing uses the Turing extension (hierarchical site intercepts + shared change). The default :eb path is stdlib-only.
Attribution is a statistical description of sharing, not a cause.
AssaySentinel.instrument_chart — Function
instrument_chart(data; instrument, value)Makie instrument comparison chart. Requires the optional Makie.jl extension.
AssaySentinel.levey_jennings — Function
levey_jennings(...)Makie control chart. Requires the optional Makie.jl extension.
AssaySentinel.levey_jennings_data — Method
levey_jennings_data(values, spec) / control_chart_dataStructured series for Levey–Jennings charts (Levey & Jennings 1950). Plotting is provided by the optional Makie extension.
AssaySentinel.lot_chart — Function
lot_chart(data; lot, value)Makie lot comparison chart. Requires the optional Makie.jl extension.
AssaySentinel.monitor — Method
monitor(control, measurements; rules=westgard_rules())Evaluate control material against target ± SD and the configured rule set.
AssaySentinel.online_series — Method
online_series(stats...)Build an OnlineStats.jl Series for streaming assay values. Requires using OnlineStats.
AssaySentinel.reconstruct — Function
reconstruct(hierarchy, site_reports; rng_seed, name)Study-level reconstruction: sharing narrative, between/within uncertainty, forest plot, and provenance. Called by analyze(study, streams).
AssaySentinel.reconstruct — Method
reconstruct(stream, report_pieces; rng_seed)Build the ordered analytical story, uncertainty budget, charts, and provenance graph from an already-run analysis. Called by analyze.
AssaySentinel.reconstruct — Method
reconstruct(stream; rng)Analyze a stream and return only the Reconstruction.
AssaySentinel.reconstruct — Method
reconstruct(reports; rng_seed, name)Panel-level reconstruction: per-analyte status narrative, score chart, and provenance. Called by analyze(panel). Units are never pooled.
AssaySentinel.reference_curve — Method
reference_curve(covariate, values; quantiles=(0.025, 0.50, 0.975), span=0.3, method=:quantile)Continuous reference curves. :quantile uses locally weighted empirical quantiles. :lms uses Cole LMS (global λ, local μ and σ) and back-transforms the requested probabilities. Not a clinical reference system.
AssaySentinel.reference_interval — Method
reference_interval(values; method=:nonparametric, rng, α=0.05, unit="")Methods:
:nonparametric— 2.5th–97.5th percentiles (CLSI EP28-style):parametric— mean ± 1.96 SD:robust— median ± 1.96 × normalized MAD:transformed— log-scale parametric, back-transformed:boxcox— Box–Cox profile-likelihood transform, then parametric limits:horn— Tukey fences then nonparametric limits on the remaining sample:lms— Cole LMS (λ, μ, σ) 2.5th–97.5th quantiles
AssaySentinel.report — Method
report(result, path)Write a professional analytical report. Alias of save for path targets.
AssaySentinel.result — Method
result(r::PanelReport)Snapshot of per-analyte status and scores. Analytical process, not clinical risk.
AssaySentinel.result — Method
result(study::StudySentinel)Snapshot of per-site sentinels and any study-level concordance alerts.
AssaySentinel.showcase_dataset — Method
showcase_dataset(; rng)12 months of synthetic measurements: 3 reagent lots, 2 instruments, one calibration event, gradual drift, variance shift, and control failures.
AssaySentinel.simulate_assay — Method
simulate_assay(; n, drift=:none, rng, ...)Generate a synthetic measurement stream.
Drift kinds: :none, :linear, :step, :variance, :lot, :batch, :periodic, :failure, :outliers.
AssaySentinel.svg_forest_chart — Method
svg_forest_chart(result::HierarchicalSiteResult; title)Forest plot of per-site means with 95% CIs (raw SE), shrunk means as filled markers, a diamond at the grand mean, and dashed prediction-interval guides.
AssaySentinel.svg_panel_chart — Method
svg_panel_chart(reports; title)Horizontal Sentinel Score bars for each analyte in a PanelReport. Scores describe analytical-process stability, not patient risk.
AssaySentinel.westgard_rules — Method
Westgard-style multirule logic (Westgard, Barry, Hunt & Groth 1981) expressed as independent composable rules.
AssaySentinel.@qcrule — Macro
@qcrule name begin
...
endCustom rule body sees values::Vector{Float64} and spec::QCSpec and must return a QCRuleResult.