pushkin-core 0.2.1

Core envelope, manifest, pipeline, and waiver types for the pushkin write-gate
Documentation
//! `[features]` (2026-08-18 refactor): the manifest-level switch for
//! whole enforcement planes. One feature today — `git_hooks`, the
//! git-plane floor. The table is strict like every other manifest
//! surface (`deny_unknown_fields`), defaults to everything-enabled when
//! absent, and only a positively parsed `false` reads as disabled.

use pushkin_core::manifest::Manifest;

const BASE: &str = r#"
version = 1
canonical = "json-schema-2020-12"
authoring = "zod"

[gates]
"#;

#[test]
fn absent_features_table_means_enabled() {
    let manifest = Manifest::parse(BASE).expect("base manifest parses");
    assert!(
        manifest.git_hooks_enabled(),
        "no [features] table must default to git hooks enabled"
    );
}

#[test]
fn empty_features_table_means_enabled() {
    let text = format!("{BASE}\n[features]\n");
    let manifest = Manifest::parse(&text).expect("empty [features] parses");
    assert!(manifest.git_hooks_enabled());
}

#[test]
fn git_hooks_false_reads_as_disabled() {
    let text = format!("{BASE}\n[features]\ngit_hooks = false\n");
    let manifest = Manifest::parse(&text).expect("disabled manifest parses");
    assert!(!manifest.git_hooks_enabled());
}

#[test]
fn git_hooks_true_reads_as_enabled() {
    let text = format!("{BASE}\n[features]\ngit_hooks = true\n");
    let manifest = Manifest::parse(&text).expect("enabled manifest parses");
    assert!(manifest.git_hooks_enabled());
}

#[test]
fn unknown_feature_key_is_rejected() {
    let text = format!("{BASE}\n[features]\ntelemetry = true\n");
    let error = Manifest::parse(&text).expect_err("unknown feature key must reject");
    let message = error.to_string();
    assert!(
        message.contains("telemetry"),
        "the rejection names the unknown key: {message}"
    );
}

#[test]
fn non_boolean_git_hooks_is_rejected() {
    let text = format!("{BASE}\n[features]\ngit_hooks = \"off\"\n");
    assert!(
        Manifest::parse(&text).is_err(),
        "a string value must not silently coerce"
    );
}