diff --git a/docs/roadmap.md b/docs/roadmap.md index 5716073..7366df5 100644 --- a/docs/roadmap.md +++ b/docs/roadmap.md @@ -1462,3 +1462,9 @@ principles named were durable, accurate and reactive. Found in rally 0x7 by plus with when a green pull request isn't submitted; and a team's session start, when it has a story claimed or sent back, says under its open stories to wait for the checks with `rally wait-green --repo ` before submitting, and says nothing of it when all its stories are in review; with a test (docs/session-hook.md). +- [ ] **RAL-187: The wait-green line only where stories go by pull request.** From the watchman (2026-11-14), + after RAL-186: DiscoSim's teams land by gated trains, with no pull requests. Done when: a team's session start + gives RAL-186's wait-green line only for a claimed or sent-back story whose project has no `gate` in the + projects list beside the pairing (RAL-161's field), so a team with only DSM stories, DSM gated, isn't told it, + and one with a RAL story beside them still is; an unreadable list takes every project as by pull request; + with a test (docs/session-hook.md). diff --git a/docs/session-hook.md b/docs/session-hook.md index 593f6f4..51320da 100644 --- a/docs/session-hook.md +++ b/docs/session-hook.md @@ -12,7 +12,9 @@ One block, between `` and ``, in this orde - the open rally, each of its projects' roadmap stages, and the cycle (RAL-131), and the stories assigned to the team (RAL-130) - the team's open stories (claimed, in review or sent back), and, when one is claimed or sent back, to wait for its - pull request's checks with `rally wait-green --repo ` before submitting (RAL-186) + pull request's checks with `rally wait-green --repo ` before submitting (RAL-186); not for a + story whose project's entry in the projects list beside the pairing names a `gate` (docs/digest.md), such as + DiscoSim's gated trains, which have no pull request to wait on (RAL-187) - under them, what bears on them (RAL-171): each condition in force naming one of the team's stories ("a condition in force names this story: …", declared, information and never a block), and each verified pull request of the team's that has fallen behind its base, read through `gh` as the digest's RAL-105 section reads it, in what's left diff --git a/src/main.rs b/src/main.rs index 784af94..ac0466a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1570,9 +1570,23 @@ fn session_start( ); let left = rally::session::DEADLINE.saturating_sub(started.elapsed()); about_stories.extend(behind_lines(team, &stories, left)); + // RAL-187: projects whose merges pass a gate, not a pull request's checks, from the + // projects list beside the pairing; unreadable, every project is taken as by PR. + let gated = projects_text(None, &path) + .ok() + .and_then(|text| web::parse_projects(&text).ok()) + .map(|(projects, _)| { + projects + .into_iter() + .filter(|p| p.gate.is_some()) + .map(|p| p.prefix) + .collect() + }) + .unwrap_or_default(); let extra = rally::session::Extra { resume, about_stories, + gated, }; println!( "{}", diff --git a/src/session.rs b/src/session.rs index 6040be8..29dd44c 100644 --- a/src/session.rs +++ b/src/session.rs @@ -306,10 +306,20 @@ pub fn render_in( format!("- {}: {} ({kind})", clean(id), clean(state)) }) .collect(); - // RAL-186: a story still to be submitted is one whose pull request's checks will be waited on. - let to_submit = stories - .values() - .any(|row| mine(row) && matches!(row["state"].as_str(), Some("claimed" | "sent_back"))); + // RAL-186: a story still to be submitted is one whose pull request's checks will be waited on, + // unless its project's merges go by a gate rather than a pull request (RAL-187). + let by_pull_request = |id: &str| { + let prefix = id.split_once('-').map_or(id, |(prefix, _)| prefix); + !extra + .gated + .iter() + .any(|gated| gated.eq_ignore_ascii_case(prefix)) + }; + let to_submit = stories.iter().any(|(id, row)| { + mine(row) + && matches!(row["state"].as_str(), Some("claimed" | "sent_back")) + && by_pull_request(id) + }); body.push("Your team's open stories:".into()); push_or_none(&mut body, own); if to_submit { @@ -455,6 +465,10 @@ pub const RECORD_PAGES: usize = 10; pub struct Extra { pub resume: Vec, pub about_stories: Vec, + /// The prefixes of projects whose merges pass a gate other than CI on GitHub (the projects + /// list's `gate`, RAL-161), such as DiscoSim's gated trains: their stories have no pull + /// request to wait on, so no wait-green line is given for them (RAL-187). + pub gated: Vec, } /// One line for each condition in force naming one of `team`'s open stories (RAL-171), from @@ -717,6 +731,41 @@ mod tests { } } + /// RAL-187: a team whose project lands by gated trains, with no pull requests, such as + /// DiscoSim's, isn't told to wait with wait-green; a story of a pull-request project still is. + #[test] + fn a_gated_projects_team_isnt_told_to_wait_with_wait_green() { + let extra = Extra { + gated: vec!["dsm".into()], + ..Extra::default() + }; + let stories = json!({"DSM-56": {"state": "claimed", "team": "spry-minnow"}, + "DSM-57": {"state": "sent_back", "team": "spry-minnow"}}); + let out = render_in( + "spry-minnow", + stories.as_object().unwrap(), + &[], + Harness::Other, + None, + None, + &extra, + ); + assert!(out.contains("- DSM-56: claimed"), "{out}"); + assert!(!out.contains("rally wait-green"), "{out}"); + let stories = json!({"DSM-56": {"state": "claimed", "team": "spry-minnow"}, + "RAL-9": {"state": "claimed", "team": "spry-minnow"}}); + let out = render_in( + "spry-minnow", + stories.as_object().unwrap(), + &[], + Harness::Other, + None, + None, + &extra, + ); + assert!(out.contains("rally wait-green "), "{out}"); + } + /// RAL-171: a condition in force naming one of the team's open stories is said under them, /// as information; another team's story, or a merged one, says nothing. #[test] @@ -744,8 +793,8 @@ mod tests { ] ); let extra = Extra { - resume: Vec::new(), about_stories: lines, + ..Extra::default() }; let out = render_in("rustdaw", stories, &[], Harness::Other, None, None, &extra); let open = out.find("Your team's open stories:").unwrap();