Skip to content

Cope-Labs/margin-ts

Repository files navigation

margin

npm version npm downloads License: MIT

Typed health classification for systems that measure things.

TypeScript port of the Python margin library. Zero dependencies.

import { Parser, createThresholds, expressionToString } from 'margin-ts';

const parser = new Parser(
  { throughput: 500, errorRate: 0.002 },
  createThresholds(400, 150),
  { errorRate: createThresholds(0.01, 0.10, false) },
);

const expr = parser.parse({ throughput: 480, errorRate: 0.03 });
console.log(expressionToString(expr));
// [throughput:INTACT(-0.04σ)] [errorRate:DEGRADED(-14.00σ)]

Throughput and error rate on the same scale. One is higher-is-better, the other is lower-is-better. Both classified correctly. Sigma-normalised so you can compare them.

Install

npm install margin-ts

What it does

A number comes in. Margin gives it:

  • Health — INTACT / DEGRADED / ABLATED / RECOVERING / OOD
  • Polarity — higher-is-better or lower-is-better, handled correctly everywhere
  • Sigma — dimensionless deviation from baseline, always positive = healthier
  • Confidence — CERTAIN / HIGH / MODERATE / LOW / INDETERMINATE
  • Drift — trajectory: STABLE / DRIFTING / ACCELERATING / DECELERATING / REVERTING / OSCILLATING
  • Anomaly — outlier: EXPECTED / UNUSUAL / ANOMALOUS / NOVEL
  • Absence — typed nulls: NOT_MEASURED / BELOW_DETECTION / SENSOR_FAILED / REDACTED / ...
  • Flow — ordered stages with typed live guidance (ADVANCE / HOLD / RETRY / ROLLBACK / ESCALATE)
  • Issue — typed diagnostics the library surfaces about its own operation

Drift detection

import { classifyDrift, DriftState } from 'margin-ts';

const dc = classifyDrift(observations);
dc.state;     // DriftState.DRIFTING
dc.direction; // DriftDirection.WORSENING
dc.rate;      // slope per second (polarity-normalised)

Anomaly detection

import { classifyAnomaly, AnomalyState } from 'margin-ts';

const ac = classifyAnomaly(currentValue, referenceValues, { component: 'cpu' });
ac.state;    // AnomalyState.NOVEL
ac.zScore;   // how many σ from historical mean
ac.isNovel;  // true if outside historical range

Multi-stage process guidance

For processes where each stage has distinct entry/quality/exit criteria — CI/CD pipelines, fermentation, clinical trials, incident response:

import { Flow, Stage, Guidance, Parser, createThresholds, flowResultToAtom } from 'margin-ts';

const flow = new Flow([
  {
    name: 'lag',
    parser: new Parser({ biomass: 0.1 }, createThresholds(0.08, 0.02)),
    advanceWhen: (expr) => expr.observations[0].value >= 0.15,
  },
  { name: 'exponential', parser: /* ... */, advanceWhen: /* ... */ },
  { name: 'stationary',  parser: /* ... */ },
], 'ferment');

const result = flow.observe({ biomass: 0.12 });
flowResultToAtom(result);
// 'flow:ferment:lag[1/3] → HOLD'

// When advanceWhen fires:
if (result.guidance === Guidance.ADVANCE) flow.advance();

Predicate exceptions are caught as ERROR issues, guidance degrades to HOLD, the flow stays alive — safety-first semantics for production contexts.

Typed diagnostics

The library surfaces its own operational state the same way it classifies observed state:

import { IssueLevel, IssueCode } from 'margin-ts';

monitor.update({ mystery: 1.0 });       // emits WARNING: BASELINE_MISSING
monitor.drift('unknown');                // emits WARNING: UNKNOWN_COMPONENT

// Query programmatically
monitor.issues.byCode(IssueCode.BASELINE_MISSING);
monitor.issues.atLeast(IssueLevel.WARNING);
monitor.diagnose();                      // JSON-safe dashboard summary

// Bridge to any logger (pino, winston, console, webhook — your choice)
monitor.issues.attachListener((issue) => {
  logger.warn(issue.detail, { code: issue.code, component: issue.component });
});

Harvest-safe on hard fail: if StreamingMonitor.update() or Flow.observe() throws, a FATAL(INTERNAL_ERROR) issue is recorded before the exception propagates. monitor.issues survives crashes intact.

API parity with Python

The TypeScript port mirrors the Python library's core types:

Python TypeScript
Health.INTACT Health.INTACT
Thresholds(intact=80, ablated=30) createThresholds(80, 30)
classify(value, confidence, thresholds) classify(value, confidence, thresholds)
Parser(baselines, thresholds) new Parser(baselines, thresholds)
expr.to_string() expressionToString(expr)
classify_drift(observations) classifyDrift(observations)
classify_anomaly(value, ref) classifyAnomaly(value, ref)
Flow(stages, label) new Flow(stages, label)
result.to_atom() flowResultToAtom(result)
buffer.attach_logger(logger) buffer.attachListener(cb)

License

MIT — Copyright (c) 2026 Cope Labs LLC

About

Typed health classification for systems that measure things. TypeScript port of margin.

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors