Functional Weave
Code in Python

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

  • mute_alert(latency, false, , maintenance down, latency, burn-rate, inhibit ×1) → muted false, reason —, by — nothing under maintenance and nothing else firing: not muted
  • mute_alert(latency, true, , maintenance down, latency, burn-rate, inhibit ×1) → muted true, reason maintenance, by — under maintenance, a kind maintenance mutes is muted
  • mute_alert(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.

def mute_alert(kind: str, under_maintenance: bool, firing_kinds: Sequence[str], policy: MutePolicy) -> AlertMute
kindstringthe alert's kind, e.g. down, latency or burn-rate
under_maintenanceboolwhether a maintenance window covers the alert's component now
firing_kindsstring[]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

@dataclass(frozen=True)
class MutePolicy:
    """What holds an alert back."""

    #: the kinds held back while their component is under maintenance
    maintenance: List[str]
    #: checked in order; the first that applies names the inhibitor
    inhibit: List[Inhibition]

@dataclass(frozen=True)
class Inhibition:
    """While an alert of kind source fires on a component, alerts of kind target on the same component are held back."""

    source: str
    #: not the same as source
    target: str

MuteReason = Literal["maintenance", "inhibited"]

@dataclass(frozen=True)
class AlertMute:
    """Whether to hold the alert back, and why."""

    muted: bool
    #: maintenance wins over inhibited; null when not muted
    reason: Optional[MuteReason]
    #: the firing kind that inhibits it; null unless inhibited
    by: Optional[str]

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

from fune.monitor.alert_mute import mute_alert  # monitor.alert-mute@^1
impl/python.py · 31 lines · open · raw
from typing import Sequence

from .monitor_alert_mute_types import AlertMute, MutePolicy


def mute_alert(kind: str, under_maintenance: bool, firing_kinds: Sequence[str], policy: MutePolicy) -> AlertMute:
    """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 and not muted` to
    monitor.alert-state."""
    if kind == "":
        raise ValueError("kind must not be empty")
    for k in policy.maintenance:
        if k == "":
            raise ValueError("maintenance kinds must not be empty")
    for rule in policy.inhibit:
        if rule.source == "":
            raise ValueError("inhibition source must not be empty")
        if rule.target == "":
            raise ValueError("inhibition target must not be empty")
        if rule.source == rule.target:
            raise ValueError("an alert kind cannot inhibit itself: %s" % (rule.source,))
    if under_maintenance and kind in policy.maintenance:
        return AlertMute(muted=True, reason="maintenance", by=None)
    for rule in policy.inhibit:
        # source != target, so an alert listed among firingKinds never inhibits itself.
        if rule.target == kind and rule.source in firing_kinds:
            return AlertMute(muted=True, reason="inhibited", by=rule.source)
    return AlertMute(muted=False, reason=None, by=None)

Install

fune build

With that line in your source, in a Python project (language python in fune.project), fune build resolves it and nothing else, pins them in fune.lock, downloads only the Python 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 Python monitor.alert-mute-1.0.0-python.fune · 13,269 bytes sha256 34a0e617174bf3bd9e3cf04ece07cca5f3e794ad4d9c8f3af1c19a88726aa47f

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

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