//! iris - the shield over a portal site. A content push is an incoming //! wormhole; nothing comes through until it checks out. //! //! `iris check` loads a content repo (a Gitea URL or a local checkout) //! exactly the way the running site does, validates it, and holds it to //! the business needs it declares in `needs.yaml` - a path within //! budget for every kind of visitor, a desk for every handling group, //! every finished state reachable, no record stranding. It also exports //! what a trainer needs to grade the wording: personas as typed choice //! tasks, and a resolved simulation model. //! //! `iris replay` runs simulated cases through the site's real aggregate //! engine on a JetStream and reports every bucket by state; with //! `--report-only` it takes the same scorecard from a live site's //! buckets. //! //! Every type iris reads is portal's own, pinned to the portal release //! it matches, so the lint and the site never disagree about what a //! page is. mod check; mod local; mod needs; mod replay; #[tokio::main] async fn main() -> anyhow::Result<()> { let mut argv: Vec = std::env::args().skip(1).collect(); // `iris --path questions` reads as `iris check --path questions`, // so the content repos' one-line CI keeps working as it was. match argv.first().map(String::as_str) { Some("check") => { argv.remove(0); check::run(argv).await } Some("replay") => { argv.remove(0); replay::run(argv).await } Some("--version") | Some("-V") => { println!("iris {v} (portal v{v})", v = env!("CARGO_PKG_VERSION")); Ok(()) } Some(flag) if flag.starts_with("--") => check::run(argv).await, _ => { eprintln!( "iris {v} - matches portal v{v}\n\nusage:\n iris check (--repo [--branch main] [--subdir questions] | --path ) [--needs-tasks [--skim]] [--needs-sim ] [--access] [--needs-score [--min-accuracy 0.8]]\n iris replay --path --nats (--cases | --report-only)", v = env!("CARGO_PKG_VERSION") ); std::process::exit(2) } } }