Functional Weave
Code in TypeScript

monitor.alert-mute

Whether to hold an alert back: its component is under maintenance, or a related alert on it is already firing.

1.0.0 · published 2026-10-03 by charlie · Anterra

Pinned by 19 tests, run in TypeScript, Python and Rust.

What it does

Whether to hold an alert back right now. It answers the question every alerting system has to ask before it pages someone, in the two ways Alertmanager does:

- **Maintenance** (a silence): the alert's component is inside a scheduled maintenance window and the alert's kind is one maintenance mutes. Planned work should not page for the downtime, slowness or error-budget burn it was planned to cause. - **Inhibition**: an alert that makes this one redundant is already firing on the same component. When a target is down, its latency alert adds nothing but a second page, so `down` inhibits `latency`.

For example

  • muteAlert(latency, false, , maintenance down, latency, burn-rate, inhibit ×1) → muted false, reason —, by — nothing under maintenance and nothing else firing: not muted
  • muteAlert(latency, true, , maintenance down, latency, burn-rate, inhibit ×1) → muted true, reason maintenance, by — under maintenance, a kind maintenance mutes is muted
  • muteAlert(cert-critical, true, , maintenance down, latency, burn-rate, inhibit ×1) → muted false, reason —, by — under maintenance, a kind maintenance does not list still alerts (a certificate expiring is not planned work)

The function

The same function in TypeScript, Python and Rust, pinned by the same tests. Pick your language; the choice follows you around the registry.

export function muteAlert(kind: string, underMaintenance: boolean, firingKinds: readonly string[], policy: MutePolicy): AlertMute
kindstringthe alert's kind, e.g. down, latency or burn-rate
underMaintenanceboolwhether a maintenance window covers the alert's component now
firingKindsstring[]the kinds of the alerts firing on the same component now; kind itself, if listed, is ignored
policyMutePolicywhich kinds maintenance holds back, and which alerts hold back which
returnsAlertMute

The types it declares, generated into your project

/** What holds an alert back. */
export interface MutePolicy {
  /** the kinds held back while their component is under maintenance */
  readonly maintenance: readonly string[];
  /** checked in order; the first that applies names the inhibitor */
  readonly inhibit: readonly Inhibition[];
}

/** While an alert of kind source fires on a component, alerts of kind target on the same component are held back. */
export interface Inhibition {
  readonly source: string;
  /** not the same as source */
  readonly target: string;
}

export type MuteReason = "maintenance" | "inhibited";

/** Whether to hold the alert back, and why. */
export interface AlertMute {
  readonly muted: boolean;
  /** maintenance wins over inhibited; null when not muted */
  readonly reason: MuteReason | null;
  /** the firing kind that inhibits it; null unless inhibited */
  readonly by: string | null;
}

Your code names it in one line, in the file that uses it

import { muteAlert } from "#fune/monitor.alert-mute@^1";
impl/typescript.ts · 26 lines · open · raw
import { type AlertMute, type MutePolicy } from "./monitor_alert_mute_types.ts";

/**
 * Hold an alert back, as Alertmanager's silences and inhibit rules do: during
 * its component's maintenance if its kind is one maintenance mutes, or while
 * an alert that inhibits it fires on the same component. The whole policy is
 * checked every time, so a mistake in it fails loudly rather than silently
 * never muting. The caller passes `condition && !muted` to monitor.alert-state.
 */
export function muteAlert(kind: string, underMaintenance: boolean, firingKinds: readonly string[], policy: MutePolicy): AlertMute {
  if (kind === "") throw new RangeError("kind must not be empty");
  for (const k of policy.maintenance) {
    if (k === "") throw new RangeError("maintenance kinds must not be empty");
  }
  for (const rule of policy.inhibit) {
    if (rule.source === "") throw new RangeError("inhibition source must not be empty");
    if (rule.target === "") throw new RangeError("inhibition target must not be empty");
    if (rule.source === rule.target) throw new RangeError(`an alert kind cannot inhibit itself: ${rule.source}`);
  }
  if (underMaintenance && policy.maintenance.includes(kind)) return { muted: true, reason: "maintenance", by: null };
  for (const rule of policy.inhibit) {
    // source !== target, so an alert listed among firingKinds never inhibits itself.
    if (rule.target === kind && firingKinds.includes(rule.source)) return { muted: true, reason: "inhibited", by: rule.source };
  }
  return { muted: false, reason: null, by: null };
}

Install

fune build

With that line in your source, in a TypeScript project (language typescript in fune.project), fune build resolves it and nothing else, pins them in fune.lock, downloads only the TypeScript package of each, and builds the code above into your project’s .fune/build, one readable file per capability with a header linking back here. Or pin a range in fune.project and build in one step:

fune add monitor.alert-mute
Download for TypeScript monitor.alert-mute-1.0.0-typescript.fune · 13,229 bytes sha256 2dc0a42df46f5ebcfdf2eb9b8ac4378cc4a4f9b99bc6a28257aad30185f08757

The manifest, vectors and README with only the TypeScript implementation. Install it without the registry with fune add ./monitor.alert-mute-1.0.0-typescript.fune, or fetch it from a terminal with fune pull monitor.alert-mute@1.0.0:typescript.

The whole function, every language, is one file too: monitor.alert-mute-1.0.0.fune, 17,962 bytes, sha256 2ff913849ec88695dff7947db041136c9efcfe0adce2756ea7e73ae57951391b. It installs into a project of any language.

Customise it in your app

The seams this capability offers. Put a marker directly above a function of your own and fune build wires it into the built code; the package on the registry is not changed, the built file’s header lists it under CUSTOMISED, and fune hooks lists every hook in the project. How hooks work.

before — your function gets the arguments and returns them, changed or not, or throws to refuse the call.

// fune: before monitor.alert-mute

after — your function gets the result and the arguments, and returns the final result.

// fune: after monitor.alert-mute

replace — it requires no other capability, so there is no dependency to replace.

step — your function runs at a numbered point inside the function’s body, receives the in-scope values it names as parameters, and may return replacements. List the points with fune show monitor.alert-mute --steps.

// fune: step monitor.alert-mute after <n|label>

Tests

A version published now needs at least 8 tests for every function, and one that expects the error for each function that throws; the registry refuses it otherwise. fune verify --all runs each case in TypeScript, Python and Rust, and a project runs them again with fune verify. This page lists the cases; it does not run them. The exact JSON is vectors.json.

CaseArgumentsExpected
nothing under maintenance and nothing else firing: not muted latency, false, , maintenance down, latency, burn-rate, inhibit ×1 → muted false, reason —, by —
under maintenance, a kind maintenance mutes is muted latency, true, , maintenance down, latency, burn-rate, inhibit ×1 → muted true, reason maintenance, by —
under maintenance, a kind maintenance does not list still alerts (a certificate expiring is not planned work) cert-critical, true, , maintenance down, latency, burn-rate, inhibit ×1 → muted false, reason —, by —
a kind maintenance mutes, outside maintenance, alerts burn-rate, false, , maintenance down, latency, burn-rate, inhibit ×1 → muted false, reason —, by —
a firing down alert inhibits the latency alert on the same component latency, false, down, maintenance down, latency, burn-rate, inhibit ×1 → muted true, reason inhibited, by down
a firing down alert does not inhibit a kind no rule names burn-rate, false, down, maintenance down, latency, burn-rate, inhibit ×1 → muted false, reason —, by —
the inhibitor must be firing: a rule alone does nothing latency, false, burn-rate, maintenance down, latency, burn-rate, inhibit ×1 → muted false, reason —, by —
maintenance wins over inhibition, and names no inhibitor latency, true, down, maintenance down, latency, burn-rate, inhibit ×1 → muted true, reason maintenance, by —
the first inhibition in policy order names the inhibitor, whatever order the firing kinds come in latency, false, burn-rate, down, maintenance , inhibit ×2 → muted true, reason inhibited, by down
an alert listed among the firing kinds does not inhibit itself latency, false, latency, maintenance down, latency, burn-rate, inhibit ×1 → muted false, reason —, by —
Show the other 9 tests
CaseArgumentsExpected
kinds match exactly: Latency is not latency Latency, true, down, maintenance down, latency, burn-rate, inhibit ×1 → muted false, reason —, by —
an empty policy never mutes, even under maintenance with everything firing down, true, latency, burn-rate, maintenance , inhibit → muted false, reason —, by —
a kind that is both inhibited and inhibits others is still muted by its own inhibitor burn-rate, false, down, latency, maintenance , inhibit ×2 → muted true, reason inhibited, by latency
an empty kind is an error , false, , maintenance down, latency, burn-rate, inhibit ×1 → error: kind must not be empty
an empty kind in the maintenance list is an error latency, false, , maintenance down, , inhibit → error: maintenance kinds must not be empty
an inhibition with no source is an error latency, false, , maintenance , inhibit ×1 → error: inhibition source must not be empty
an inhibition with no target is an error latency, false, , maintenance , inhibit ×1 → error: inhibition target must not be empty
a kind inhibiting itself is an error down, false, , maintenance , inhibit ×1 → error: an alert kind cannot inhibit itself: latency
the policy is checked even when maintenance already mutes the alert latency, true, , maintenance latency, inhibit ×1 → error: an alert kind cannot inhibit itself: down

More from the author

Pass the result on as the condition of `monitor.alert-state`: `condition and not muted`. A muted alert behaves as if its condition were false: it never fires, and one already firing resolves after its `clearForSeconds`, as the down alert does when a component goes into maintenance.

## Why it is shaped this way

- **Kinds are strings the caller chooses** (`down`, `latency`, `burn-rate`, `cert-critical`), matched exactly. The policy is data, usually read from the project's config, so a new kind of alert needs no new version. - **Maintenance lists what it mutes**, rather than muting everything: a certificate that is about to expire is not planned work, and should still alert during a database upgrade. - **Maintenance wins over inhibition** and `by` stays null, since the maintenance window is the reason that holds for the whole period. - **Inhibitions are checked in policy order**, not in the order the firing kinds arrive, so the answer does not depend on how the caller collected them. - **An alert never inhibits itself**: `source` and `target` must differ, so a kind listed in `firingKinds` for itself changes nothing. - **The whole policy is checked on every call**, even when the first maintenance kind already decides it. A mistake in the policy fails loudly instead of silently never muting. - Inhibition is per component (Alertmanager's `equal: [component]`): the caller passes only the kinds firing on the alert's own component.

## Errors

- `kind must not be empty` - `maintenance kinds must not be empty` - `inhibition source must not be empty` - `inhibition target must not be empty` - `an alert kind cannot inhibit itself: X`

## Sources

Prometheus Alertmanager: silences and inhibition (https://prometheus.io/docs/alerting/latest/alertmanager/#inhibition) and `inhibit_rules` in the configuration reference (https://prometheus.io/docs/alerting/latest/configuration/#inhibit_rule).

Files

PathBytes
README.md2,564
impl/python.py1,605
impl/rust.rs2,942
impl/typescript.ts1,568
vectors.json5,012