Your browser sucks, JavaScript disabled! Gunnar's console needs JavaScript and WebAssembly. Git over SSH on port 2222 does not.

nordisk/monetize

xtask/tests/wide_traits.rs

//! **The server speaks the small traits** (A24 §2.5-2.6, A32-12, A32-13; the owner's standing
//! order of 2026-10-26: "the monetize server moves onto the small Cloud and Payment traits, and
//! the wide ones go"). A vendor is the ten ops of `monetize_pay::Payment` and a cloud the nine
//! of `monetize_cloud::Cloud`; nothing in this repository names the 20-method and 37-method
//! traits that came before them, nor the macros that bridged the two.
//! The words are assembled here, so this guard does not name them either. A CHANGELOG may record
//! that they went.

use std::path::{Path, PathBuf};

fn root() -> PathBuf {
    Path::new(env!("CARGO_MANIFEST_DIR")).parent().expect("xtask sits in the repo").to_path_buf()
}

/// Every line of the repository (tracked files, this guard and CHANGELOGs left out) that names
/// one of `words` as a whole word.
fn named(words: &[String]) -> String {
    let mut args: Vec<String> = ["grep", "-I", "-n", "-w"].iter().map(|s| s.to_string()).collect();
    for w in words {
        args.push("-e".into());
        args.push(w.clone());
    }
    args.extend(["--", ".", ":(exclude)CHANGELOG*", ":(exclude)**/CHANGELOG*", ":(exclude)xtask/tests/wide_traits.rs"].map(String::from));
    let out = std::process::Command::new("git").args(&args).current_dir(root()).output().expect("git grep runs");
    match out.status.code() {
        Some(1) => String::new(),
        Some(0) => String::from_utf8_lossy(&out.stdout).into_owned(),
        other => panic!("git grep failed ({other:?}): {}", String::from_utf8_lossy(&out.stderr)),
    }
}

#[test]
fn nothing_names_the_wide_payment_trait_or_its_bridge() {
    let words = [["Payment", "Provider"].concat(), ["provider_over", "_payment"].concat()];
    let found = named(&words);
    assert!(found.is_empty(), "the wide payment trait is gone; a vendor is `monetize_pay::Payment` (ten ops). Named here:\n{found}");
}

#[test]
fn nothing_names_the_wide_cloud_trait_or_its_bridge() {
    let words = [["Cloud", "Provider"].concat(), ["estate_ops", "_over_cloud"].concat()];
    let found = named(&words);
    assert!(found.is_empty(), "the wide cloud trait is gone; a cloud is `monetize_cloud::Cloud` (nine ops), asked through `monetize_cloud::ask`. Named here:\n{found}");
}

/// **MONETIZE-FIX step 3c: there is no host product trait.** A product is its guest's facts,
/// served by the concrete `monetize_product::pulled::Pulled` from its estate's reports; nothing
/// in the repository declares the 19-method host trait, nor checks a ticket in a product.
#[test]
fn no_host_product_trait_is_declared() {
    let found = named(&[["pub trait ", "Product"].concat()]);
    // Rust sources only: the docs write the guest's six-op target trait (A24 §2.2) under this name.
    let found: String = found.lines().filter(|l| l.split(':').next().is_some_and(|f| f.ends_with(".rs"))).map(|l| format!("{l}\n")).collect();
    assert!(found.is_empty(), "a host product trait is declared again:\n{found}");
    let tickets = named(&[["fn may_act", "_for"].concat()]);
    assert!(tickets.is_empty(), "a product still checks its own tickets:\n{tickets}");
}

/// **v2.0 step 1: push code is deleted.** A served product queues a paid fact for the main to
/// pull and never refuses it, so the grace that waited out a product's refusal (and the words
/// that read a refusal as a restoring box) are gone.
#[test]
fn no_refused_push_grace_is_left() {
    let found = named(&[["REFUSED_PUSH", "_GRACE_MS"].concat(), ["refusal_reads_as", "_restoring"].concat(), ["refused_past", "_grace"].concat()]);
    assert!(found.is_empty(), "the refused-push path is back:\n{found}");
}

/// **v2.0 step 1, the poller's half.** A served product's `push_entitlement` queues the fact for
/// its main and answers `Unavailable` at worst, never `Refused`, so a pending push is retried
/// until the main has it: no pending push carries a "refused, not retried" flag, and the
/// poller has no branch that gives up on one.
#[test]
fn no_pending_push_is_given_up_as_refused() {
    let found = named(&[["push", "-refused"].concat()]);
    assert!(found.is_empty(), "the poller still gives up on a refused push:\n{found}");
    let ledger = std::fs::read_to_string(root().join("crates/monetize/src/ledger.rs")).expect("the ledger");
    let pending = ledger.split("pub struct PendingPush {").nth(1).and_then(|s| s.split('}').next()).expect("PendingPush is declared");
    assert!(!pending.contains("pub refused"), "PendingPush still carries a refused flag:\n{pending}");
    let proto = std::fs::read_to_string(root().join("crates/monetize-proto/proto/monetize/v1/entitlement.proto")).expect("the proto");
    assert!(!proto.contains("bool refused = 4;"), "the PendingPush message still carries `refused`");
}

/// **The appliance grow drive is deleted** (owner 2026-11-02, final). No pulled estate answers
/// an appliance's grow verbs, so the drive that called them (the runs, approve/start/flush/
/// status/resume, the batch evaluation that started it, the twin lane), its `GrowAdmin` gRPC
/// service, the console's grow page and routes, and the reach that counted what the drive could
/// grow are gone. A box grows through the v2 path only: a paid step is a signed `grow_go_ahead`
/// the box pulls, and the box asks `Fleet.Ask{grow_set}` to size its own disks
/// (`estate_order::sold::grow_own`, `monetize_cloud::grow`'s journal).
#[test]
fn no_appliance_grow_drive_is_left() {
    let words = [
        ["Grow", "Runs"].concat(),
        ["Product", "Grow"].concat(),
        ["Appliance", "Grow"].concat(),
        ["trait ", "Appliance"].concat(),
        ["Grow", "Admin"].concat(),
        ["grow", "_admin"].concat(),
        ["growable", "_reach"].concat(),
        ["can_absorb", "_growing"].concat(),
        ["arm", "_reach"].concat(),
        ["with", "_appliance"].concat(),
        ["resume_if", "_open"].concat(),
    ];
    let found = named(&words);
    // Code only: a CHANGELOG or a design note may record that the drive went.
    let found: String = found.lines().filter(|l| l.split(':').next().is_some_and(|f| f.ends_with(".rs") || f.ends_with(".proto"))).map(|l| format!("{l}\n")).collect();
    assert!(found.is_empty(), "the appliance grow drive is back:\n{found}");
    for gone in [
        "crates/monetize-server/src/grow/batch/twin.rs",
        "crates/monetize-server/src/services/grow_admin.rs",
        "crates/monetize-proto/proto/monetize/v1/grow_admin.proto",
        "crates/monetize-ui-web/src/pages/grow.rs",
    ] {
        assert!(!root().join(gone).exists(), "{gone} is back");
    }
}

/// **No leftover of the grow cut stays** (lane mon-tidy). `window_settled` only ever answered
/// true once the reach went, `held_gib_of` lost its last caller with `mark_twin_behind`, and the
/// batcher's knobs (`TwinGrowConfig`, `GrowTrigger`) were read and did nothing. Code only.
#[test]
fn no_grow_cut_leftover_is_left() {
    let words = [["window", "_settled"].concat(), ["held_gib", "_of"].concat(), ["TwinGrow", "Config"].concat(), ["Grow", "Trigger"].concat()];
    let found = named(&words);
    let found: String = found.lines().filter(|l| l.split(':').next().is_some_and(|f| f.ends_with(".rs") || f.ends_with(".proto"))).map(|l| format!("{l}\n")).collect();
    assert!(found.is_empty(), "a grow-cut leftover is back:\n{found}");
}