Functional Weave
Code in Rust

monitor.check-status

Combine the statuses of one target's probes from several locations into one up, degraded or down, with a quorum.

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

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

What it does

The shared vocabulary of the `monitor.*` capabilities, and the rule for combining probes of one target from several locations.

- `CheckStatus` is `up`, `degraded` or `down`: what one probe concluded. - `Check` is one probe outcome at a moment: `{ at, status }`, `at` in Unix seconds. `monitor.uptime`, `monitor.incidents`, `monitor.uptime-bars` and the others take lists of these, so a monitoring app stores exactly this.

For example

  • aggregate_status(up, up, up, 2) → up every location up is up
  • aggregate_status(up, down, up, 2) → degraded one down below a quorum of two is degraded, not down
  • aggregate_status(down, up, down, 2) → down two down meets a quorum of two

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.

pub fn aggregate_status(statuses: &[String], down_quorum: i64) -> String
statusesCheckStatus[]the latest status from each probe location of one target, at least one
down_quoruminthow many locations must say down before the target is down, 1 to the number of statuses
returnsCheckStatus

The types it declares, generated into your project

// CheckStatus is a string in Rust, one of: "up", "degraded", "down".
// Parameters take it as &str and results hold it as String.

/// One probe outcome at a moment, the unit every monitor.* series is made of.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Check {
    /// Unix seconds when the probe ran
    pub at: i64,
    pub status: String,
}

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

fune!(monitor.check-status@^1);  // then call aggregate_status(…)
impl/rust.rs · 48 lines · open · raw

Imports name this capability’s declared dependencies, which fune builds next to it in your project; each one links to its page.

use super::funejson::Value;  ← the fune runtime: the JSON value the test vectors use; fune build keeps it only where a signature takes one

/// One status for a target probed from several places. Down needs a quorum,
/// so one location with a broken route cannot page anyone; a down vote below
/// the quorum still counts as degraded, because something is wrong somewhere.
///
/// # Panics
/// Panics on an empty list, a quorum outside 1..=len, or an unknown status.
pub fn aggregate_status(statuses: &[String], down_quorum: i64) -> String {
    if statuses.is_empty() {
        panic!("statuses must not be empty");
    }
    if down_quorum < 1 || down_quorum > statuses.len() as i64 {
        panic!("downQuorum must be 1 to {}, received {}", statuses.len(), down_quorum);
    }
    let mut down = 0i64;
    let mut degraded = 0i64;
    for s in statuses {
        match s.as_str() {
            "down" => down += 1,
            "degraded" => degraded += 1,
            "up" => {}
            other => panic!("unknown check status: {}", other),
        }
    }
    if down >= down_quorum {
        return "down".to_string();
    }
    if down > 0 || degraded > 0 { "degraded".to_string() } else { "up".to_string() }
}

/// A `Check` from its JSON form, for adapters of capabilities built on this one.
pub fn check_from_value(v: &Value) -> Check {
    Check { at: v.get("at").as_i64(), status: v.get("status").as_str().to_string() }
}

pub fn checks_from_value(v: &Value) -> Vec<Check> {
    v.as_arr().iter().map(check_from_value).collect()
}

pub fn check_to_value(c: &Check) -> Value {
    Value::obj(vec![("at", Value::Int(c.at)), ("status", Value::str(&c.status))])
}

pub fn fune_vector(args: &[Value]) -> Value {
    let statuses: Vec<String> = args[0].as_arr().iter().map(|s| s.as_str().to_string()).collect();
    Value::str(&aggregate_status(&statuses, args[1].as_i64()))
}

Install

fune build

With that line in your source, in a Rust project (language rust in fune.project), fune build resolves it and nothing else, pins them in fune.lock, downloads only the Rust 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. A crate’s build.rs runs it before every compile. Or pin a range in fune.project and build in one step:

fune add monitor.check-status
Download for Rust monitor.check-status-1.0.0-rust.fune · 6,803 bytes sha256 17876ab49c43bcbf087e2d2995194e38db955392f747df039f418e41758a8180

The manifest, vectors and README with only the Rust implementation. Install it without the registry with fune add ./monitor.check-status-1.0.0-rust.fune, or fetch it from a terminal with fune pull monitor.check-status@1.0.0:rust.

The whole function, every language, is one file too: monitor.check-status-1.0.0.fune, 9,013 bytes, sha256 be76e5bc993feafeee52850214d86b9e76091db0f91f029ed87fadc0576185ff. 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.check-status

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

// fune: after monitor.check-status

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.check-status --steps.

// fune: step monitor.check-status 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
every location up is up up, up, up, 2 → up
one down below a quorum of two is degraded, not down up, down, up, 2 → degraded
two down meets a quorum of two down, up, down, 2 → down
a quorum of one: any down location is down up, down, 1 → down
a single location with a quorum of one decides alone down, 1 → down
a slow location alone makes the target degraded up, degraded, up, 2 → degraded
degraded votes never add up to down degraded, degraded, degraded, 1 → degraded
quorum equal to the number of locations needs all of them down down, down, degraded, 3 → degraded
all down with a unanimous quorum is down down, down, down, 3 → down
an empty list is an error , 1 → error: statuses must not be empty
Show the other 3 tests
CaseArgumentsExpected
a quorum of zero is an error up, 0 → error: downQuorum must be 1 to 1, received 0
a quorum larger than the locations can never be met up, down, 3 → error: downQuorum must be 1 to 2, received 3
an unknown status is an error up, offline, 1 → error: unknown check status: offline

More from the author

## aggregateStatus

Monitoring services confirm an outage from more than one location before alerting, because a single probe location with a bad route is far more common than a real outage. The rule, in order:

1. At least `downQuorum` statuses are `down`: **down**. 2. Any `down` (below the quorum) or any `degraded`: **degraded**. Something is wrong somewhere, but not enough to call it an outage. 3. Otherwise **up**.

Degraded votes never add up to down, however many there are: a slow site is still serving.

## Errors

- `statuses must not be empty` - `downQuorum must be 1 to N` where N is the number of statuses: a quorum that can never be met would hide every outage. - `unknown check status: X`

Files

PathBytes
README.md1,163
impl/python.py1,073
impl/rust.rs1,775
impl/typescript.ts1,015
vectors.json1,536