@@ -140,6 +140,15 @@
/// Matrix kind: requirement (per-repo coverage, default) | standard (org-wide)
#[arg(long, default_value = "requirement")]
kind: String,
/// Anchor the matrix on a specific run (point-in-time coverage; forces strict_latest)
#[arg(long)]
run_id: Option<String>,
/// Coverage rollup policy: best | strict_latest | any_recent
#[arg(long)]
policy: Option<String>,
/// How many recent runs to consider (1..100)
#[arg(long)]
run_window: Option<u32>,
},
/// Check requirement coverage status (exit 1 if uncovered requirements exist)
Status(StatusArgs),
@@ -411,7 +420,20 @@
repo,
organization,
kind,
run_id,
policy,
} => matrix(repo.as_deref(), organization.as_deref(), &kind).await,
run_window,
} => {
matrix(
repo.as_deref(),
organization.as_deref(),
&kind,
run_id.as_deref(),
policy.as_deref(),
run_window,
)
.await
}
RequirementCommand::Status(args) => status(args).await,
RequirementCommand::Seed { repo, clear } => seed(repo.as_deref(), clear).await,
}
@@ -1271,19 +1293,38 @@
repo: Option<&str>,
organization: Option<&str>,
kind: &str,
run_id: Option<&str>,
policy: Option<&str>,
run_window: Option<u32>,
) -> Result<(), Box<dyn std::error::Error>> {
match kind {
"requirement" => matrix_requirements(repo).await,
"requirement" => matrix_requirements(repo, run_id, policy, run_window).await,
"standard" => matrix_standards(organization).await,
other => Err(format!("unknown --kind '{other}' (expected requirement | standard)").into()),
}
}
async fn matrix_requirements(
repo: Option<&str>,
run_id: Option<&str>,
async fn matrix_requirements(repo: Option<&str>) -> Result<(), Box<dyn std::error::Error>> {
policy: Option<&str>,
run_window: Option<u32>,
) -> Result<(), Box<dyn std::error::Error>> {
let client = Client::from_config()?;
let (org, name) = config::resolve_repo(repo)?;
let window_str = run_window.map(|w| w.to_string());
let mut query: Vec<(&str, &str)> = Vec::new();
if let Some(r) = run_id {
query.push(("run_id", r));
}
if let Some(p) = policy {
query.push(("policy", p));
}
if let Some(w) = window_str.as_deref() {
query.push(("run_window", w));
}
let resp: serde_json::Value = client
.get_with_query(&format!("/{org}/{name}/requirements/matrix"), &query)
.get(&format!("/{org}/{name}/requirements/matrix"))
.await?;
if output::is_json() {
@@ -2520,7 +2561,7 @@
#[tokio::test]
async fn matrix_standards_rejects_missing_organization() {
let err = matrix(None, None, "standard")
let err = matrix(None, None, "standard", None, None, None)
.await
.unwrap_err()
.to_string();
@@ -2529,6 +2570,9 @@
#[tokio::test]
async fn matrix_rejects_unknown_kind() {
let err = matrix(None, None, "bogus", None, None, None)
.await
let err = matrix(None, None, "bogus").await.unwrap_err().to_string();
.unwrap_err()
.to_string();
assert!(err.contains("bogus"), "got: {err}");
}