gate-contract 0.1.1

Fail-closed gate contracts for staged build pipelines: a blocking gate that fails halts the run, and one that cannot be evaluated halts it too.
Documentation
# gate-contract

Fail-closed gate contracts for staged build pipelines, in dependency-free Rust.

A pipeline that only knows PASS and FAIL has a third state hiding inside it: the gate that
could not be evaluated, because a credential was missing, an API timed out, or the value it
needed was never produced. Treating that as a pass is how a run reaches deploy having
checked nothing.

The rule this crate enforces:

> A **blocking** gate halts the run when it fails **and** when it cannot be evaluated.
> "Could not check" is not "fine".

```rust
use gate_contract::{Gate, Known, Pipeline, Stage};

let run = Pipeline::new()
    .with_stage(Stage::new("intake").with_gate(Gate::pass("scope-present")))
    .with_stage(Stage::new("harvest").with_gate(Gate::unevaluable("rdap", "no credential")))
    .with_stage(Stage::new("build").with_gate(Gate::pass("never-reached")))
    .run();

assert!(run.halted());
assert_eq!(run.halted_stage(), Some("harvest"));
assert_eq!(run.stages_skipped(), 1); // never ran, so it has no outcome

// An unknown input cannot pass a gate by accident
let coverage: Known<u32> = Known::Unknown;
let g = Gate::check_known("coverage-floor", &coverage, |c| *c >= 50, "below floor");
assert!(g.halts());
```

- `Outcome``Pass` / `Fail` / `Unevaluable`, each with a reason.
- `Known<T>``Unknown` propagates through `map` and `zip_with` instead of decaying to a default.
- `Severity` — blocking or advisory; advisory failures are warnings in the report.
- `Stage::evaluate` visits every gate, so one run reports every problem, and names the first that halts.
- `Pipeline::run` stops at the first halting stage and reports how many stages never ran.

## Install

```toml
[dependencies]
gate-contract = "0.1"
```

`#![forbid(unsafe_code)]`, no dependencies, MSRV 1.63.

## Licence

MIT OR Apache-2.0.

Extracted from a real staged build pipeline, whose first blocking gate is worked through in
public at <https://aiwebsitepipeline.com/niche-score.html>: six subscores, the weights that
combine them, and the thresholds that decide the run — 55 and above proceeds, 40 to 55
proceeds under a page cap, below 40 the run halts. Worth reading before you decide what your
own blocking gates should refuse.