Browse Source

Automate Prioritization meeting

Santiago Pastorino 5 years ago
parent
commit
23589c70ce

+ 25 - 0
src/bin/prioritization.rs

@@ -0,0 +1,25 @@
+use std::io::{self, Write};
+use triagebot::prioritization::{self, Action};
+
+#[tokio::main]
+async fn main() {
+    let meeting = prioritization::config::prepare_meeting();
+
+    for step in &meeting.steps {
+        println!("{}", step.call());
+
+        //press_key_to_continue();
+    }
+}
+
+fn press_key_to_continue() {
+    let mut stdout = io::stdout();
+    stdout
+        .write(b"Press a key to continue ...")
+        .expect("Unable to write to stdout");
+    stdout.flush().expect("Unable to flush stdout");
+
+    io::stdin()
+        .read_line(&mut String::new())
+        .expect("Unable to read user input");
+}

+ 69 - 3
src/github.rs

@@ -5,7 +5,10 @@ use futures::stream::{FuturesUnordered, StreamExt};
 use once_cell::sync::OnceCell;
 use reqwest::header::{AUTHORIZATION, USER_AGENT};
 use reqwest::{Client, RequestBuilder, Response, StatusCode};
-use std::fmt;
+use std::{
+    fmt,
+    time::{Duration, SystemTime},
+};
 
 #[derive(Debug, PartialEq, Eq, serde::Deserialize)]
 pub struct User {
@@ -20,7 +23,41 @@ impl GithubClient {
 
         let req_dbg = format!("{:?}", req);
 
-        let resp = self.client.execute(req).await?;
+        let mut resp = self.client.execute(req.try_clone().unwrap()).await?;
+
+        if resp.status().as_u16() == 403 {
+            let headers = dbg!(resp.headers());
+            if headers.contains_key("X-RateLimit-Remaining")
+                && headers.contains_key("X-RateLimit-Reset")
+            {
+                if headers["X-RateLimit-Remaining"] == "0" {
+                    let epoch_time = dbg!(SystemTime::now()
+                        .duration_since(SystemTime::UNIX_EPOCH)
+                        .unwrap()
+                        .as_secs());
+                    let reset_time = dbg!(headers["X-RateLimit-Reset"]
+                        .to_str()
+                        .unwrap()
+                        .parse::<u64>()
+                        .unwrap());
+                    tokio::time::delay_for(Duration::from_secs(dbg!(
+                        reset_time.saturating_sub(epoch_time) + 5
+                    )))
+                    .await;
+                    dbg!(SystemTime::now()
+                        .duration_since(SystemTime::UNIX_EPOCH)
+                        .unwrap()
+                        .as_secs());
+                    resp = self.client.execute(req.try_clone().unwrap()).await?;
+                    if resp.status().as_u16() == 403 {
+                        dbg!(resp.headers());
+                        tokio::time::delay_for(Duration::from_secs(60)).await;
+                        resp = self.client.execute(req).await?;
+                        dbg!(resp.headers());
+                    }
+                }
+            }
+        }
 
         resp.error_for_status_ref()?;
 
@@ -120,7 +157,7 @@ pub struct Issue {
     pub body: String,
     created_at: chrono::DateTime<Utc>,
     pub title: String,
-    html_url: String,
+    pub html_url: String,
     pub user: User,
     labels: Vec<Label>,
     assignees: Vec<User>,
@@ -500,11 +537,40 @@ pub struct IssuesEvent {
     pub label: Option<Label>,
 }
 
+#[derive(Debug, serde::Deserialize)]
+pub struct IssueSearchResult {
+    pub total_count: usize,
+    pub incomplete_results: bool,
+    pub items: Vec<Issue>,
+}
+
 #[derive(Debug, serde::Deserialize)]
 pub struct Repository {
     pub full_name: String,
 }
 
+impl Repository {
+    fn base_url(&self) -> &str {
+        "https://api.github.com"
+    }
+
+    pub async fn get_issues(
+        &self,
+        client: &GithubClient,
+        filters: &Vec<&str>,
+    ) -> anyhow::Result<IssueSearchResult> {
+        let filters = filters.join("+");
+
+        let url = format!("{}/search/issues?q={}", self.base_url(), filters);
+
+        let result = client.get(&url);
+        client
+            .json(result)
+            .await
+            .with_context(|| format!("failed to list issues from {}", url))
+    }
+}
+
 #[derive(Debug)]
 pub enum Event {
     IssueComment(IssueCommentEvent),

+ 1 - 0
src/lib.rs

@@ -12,6 +12,7 @@ pub mod handlers;
 pub mod interactions;
 pub mod notification_listing;
 pub mod payload;
+pub mod prioritization;
 pub mod team;
 mod team_data;
 pub mod zulip;

+ 469 - 0
src/prioritization/config.rs

@@ -0,0 +1,469 @@
+use super::{Meeting, NamedQuery, Query, RepoQuery, Step};
+
+pub fn prepare_meeting<'a>() -> Meeting<Step<'a>> {
+    Meeting {
+        steps: vec![
+            unpri_i_prioritize(),
+            regressions(),
+            nominations(),
+            prs_waiting_on_team(),
+            agenda(),
+            final_review(),
+        ],
+    }
+}
+
+pub fn unpri_i_prioritize<'a>() -> Step<'a> {
+    let mut queries = Vec::new();
+
+    queries.push(NamedQuery {
+        name: "unpri_i_prioritize.all",
+        query: Query {
+            filters: vec!["state:open"],
+            include_labels: vec!["I-prioritize"],
+            exclude_labels: vec!["P-critical", "P-high", "P-medium", "P-low"],
+        },
+    });
+
+    queries.push(NamedQuery {
+        name: "unpri_i_prioritize.t_compiler",
+        query: Query {
+            filters: vec!["state:open"],
+            include_labels: vec!["I-prioritize", "T-compiler"],
+            exclude_labels: vec!["P-critical", "P-high", "P-medium", "P-low"],
+        },
+    });
+
+    queries.push(NamedQuery {
+        name: "unpri_i_prioritize.libs_impl",
+        query: Query {
+            filters: vec!["state:open"],
+            include_labels: vec!["I-prioritize", "libs-impl"],
+            exclude_labels: vec!["P-critical", "P-high", "P-medium", "P-low"],
+        },
+    });
+
+    Step {
+        name: "unpri_i_prioritize",
+        actions: vec![RepoQuery {
+            repo: "rust-lang/rust",
+            queries,
+        }],
+    }
+}
+
+// FIXME: we should filter out `T-libs` ones given that we only want `libs-impl` but meanwhile
+// we are in a kind of transition state we have all of them.
+pub fn regressions<'a>() -> Step<'a> {
+    let mut queries = Vec::new();
+
+    queries.push(NamedQuery {
+        name: "regressions.stable_to_beta",
+        query: Query {
+            filters: vec!["state:open"],
+            include_labels: vec!["regression-from-stable-to-beta"],
+            exclude_labels: vec![
+                "P-critical",
+                "P-high",
+                "P-medium",
+                "P-low",
+                "T-infra",
+                "T-release",
+            ],
+        },
+    });
+
+    queries.push(NamedQuery {
+        name: "regressions.stable_to_nightly",
+        query: Query {
+            filters: vec!["state:open"],
+            include_labels: vec!["regression-from-stable-to-nightly"],
+            exclude_labels: vec![
+                "P-critical",
+                "P-high",
+                "P-medium",
+                "P-low",
+                "T-infra",
+                "T-release",
+            ],
+        },
+    });
+
+    queries.push(NamedQuery {
+        name: "regressions.stable_to_stable",
+        query: Query {
+            filters: vec!["state:open", "per_page=100"],
+            include_labels: vec!["regression-from-stable-to-stable"],
+            exclude_labels: vec![
+                "P-critical",
+                "P-high",
+                "P-medium",
+                "P-low",
+                "T-infra",
+                "T-release",
+            ],
+        },
+    });
+
+    Step {
+        name: "regressions",
+        actions: vec![RepoQuery {
+            repo: "rust-lang/rust",
+            queries,
+        }],
+    }
+}
+
+pub fn nominations<'a>() -> Step<'a> {
+    let mut queries = Vec::new();
+
+    queries.push(NamedQuery {
+        name: "nominations.stable_nominated",
+        query: Query {
+            filters: vec![],
+            include_labels: vec!["stable-nominated"],
+            exclude_labels: vec!["stable-accepted"],
+        },
+    });
+
+    queries.push(NamedQuery {
+        name: "nominations.beta_nominated",
+        query: Query {
+            filters: vec![],
+            include_labels: vec!["beta-nominated"],
+            exclude_labels: vec!["beta-accepted"],
+        },
+    });
+
+    queries.push(NamedQuery {
+        name: "nominations.i_nominated",
+        query: Query {
+            filters: vec!["state:open"],
+            include_labels: vec!["I-nominated"],
+            exclude_labels: vec![],
+        },
+    });
+
+    queries.push(NamedQuery {
+        name: "nominations.i_nominated_t_compiler",
+        query: Query {
+            filters: vec!["state:open"],
+            include_labels: vec!["I-nominated", "T-compiler"],
+            exclude_labels: vec![],
+        },
+    });
+
+    queries.push(NamedQuery {
+        name: "nominations.i_nominated_libs_impl",
+        query: Query {
+            filters: vec!["state:open"],
+            include_labels: vec!["I-nominated", "libs-impl"],
+            exclude_labels: vec![],
+        },
+    });
+
+    Step {
+        name: "nominations",
+        actions: vec![RepoQuery {
+            repo: "rust-lang/rust",
+            queries,
+        }],
+    }
+}
+
+pub fn prs_waiting_on_team<'a>() -> Step<'a> {
+    let mut queries = Vec::new();
+
+    queries.push(NamedQuery {
+        name: "prs_waiting_on_team.all",
+        query: Query {
+            filters: vec!["state:open"],
+            include_labels: vec!["S-waiting-on-team"],
+            exclude_labels: vec![],
+        },
+    });
+
+    queries.push(NamedQuery {
+        name: "prs_waiting_on_team.t_compiler",
+        query: Query {
+            filters: vec!["state:open"],
+            include_labels: vec!["S-waiting-on-team", "T-compiler"],
+            exclude_labels: vec![],
+        },
+    });
+
+    queries.push(NamedQuery {
+        name: "prs_waiting_on_team.libs_impl",
+        query: Query {
+            filters: vec!["state:open"],
+            include_labels: vec!["S-waiting-on-team", "libs-impl"],
+            exclude_labels: vec![],
+        },
+    });
+
+    Step {
+        name: "prs_waiting_on_team",
+        actions: vec![RepoQuery {
+            repo: "rust-lang/rust",
+            queries,
+        }],
+    }
+}
+
+pub fn agenda<'a>() -> Step<'a> {
+    let mut queries = Vec::new();
+    let mut actions = Vec::new();
+
+    queries.push(NamedQuery {
+        name: "mcp.seconded",
+        query: Query {
+            filters: vec!["state:open"],
+            include_labels: vec!["major-change", "final-comment-period"],
+            exclude_labels: vec![],
+        },
+    });
+
+    queries.push(NamedQuery {
+        name: "mcp.new_not_seconded",
+        query: Query {
+            filters: vec!["state:open"],
+            include_labels: vec!["major-change", "to-announce"],
+            exclude_labels: vec!["final-comment-period"],
+        },
+    });
+
+    queries.push(NamedQuery {
+        name: "mcp.old_not_seconded",
+        query: Query {
+            filters: vec!["state:open"],
+            include_labels: vec!["major-change"],
+            exclude_labels: vec!["to-announce", "final-comment-period"],
+        },
+    });
+
+    actions.push(RepoQuery {
+        repo: "rust-lang/compiler-team",
+        queries,
+    });
+
+    let mut queries = Vec::new();
+
+    queries.push(NamedQuery {
+        name: "beta_nominated.t_compiler",
+        query: Query {
+            filters: vec![],
+            include_labels: vec!["beta-nominated", "T-compiler"],
+            exclude_labels: vec!["beta-accepted"],
+        },
+    });
+
+    queries.push(NamedQuery {
+        name: "beta_nominated.libs_impl",
+        query: Query {
+            filters: vec![],
+            include_labels: vec!["beta-nominated", "libs-impl"],
+            exclude_labels: vec!["beta-accepted"],
+        },
+    });
+
+    queries.push(NamedQuery {
+        name: "beta_nominated.t_rustdoc",
+        query: Query {
+            filters: vec![],
+            include_labels: vec!["beta-nominated", "T-rustdoc"],
+            exclude_labels: vec!["beta-accepted"],
+        },
+    });
+
+    queries.push(NamedQuery {
+        name: "stable_nominated.t_compiler",
+        query: Query {
+            filters: vec![],
+            include_labels: vec!["stable-nominated", "T-compiler"],
+            exclude_labels: vec!["stable-accepted"],
+        },
+    });
+
+    queries.push(NamedQuery {
+        name: "stable_nominated.t_rustdoc",
+        query: Query {
+            filters: vec![],
+            include_labels: vec!["stable-nominated", "T-rustdoc"],
+            exclude_labels: vec!["stable-accepted"],
+        },
+    });
+
+    queries.push(NamedQuery {
+        name: "stable_nominated.libs_impl",
+        query: Query {
+            filters: vec![],
+            include_labels: vec!["stable-nominated", "libs-impl"],
+            exclude_labels: vec!["stable-accepted"],
+        },
+    });
+
+    queries.push(NamedQuery {
+        name: "prs_waiting_on_team.t_compiler",
+        query: Query {
+            filters: vec!["state:open"],
+            include_labels: vec!["S-waiting-on-team", "T-compiler"],
+            exclude_labels: vec![],
+        },
+    });
+
+    queries.push(NamedQuery {
+        name: "prs_waiting_on_team.libs_impl",
+        query: Query {
+            filters: vec!["state:open"],
+            include_labels: vec!["S-waiting-on-team", "libs-impl"],
+            exclude_labels: vec![],
+        },
+    });
+
+    queries.push(NamedQuery {
+        name: "issues_of_note.p_critical",
+        query: Query {
+            filters: vec!["state:open"],
+            include_labels: vec!["T-compiler", "P-critical"],
+            exclude_labels: vec![],
+        },
+    });
+
+    queries.push(NamedQuery {
+        name: "issues_of_note.unassigned_p_critical",
+        query: Query {
+            filters: vec!["state:open", "no=assignee"],
+            include_labels: vec!["T-compiler", "P-critical"],
+            exclude_labels: vec![],
+        },
+    });
+
+    queries.push(NamedQuery {
+        name: "issues_of_note.p_high",
+        query: Query {
+            filters: vec!["state:open"],
+            include_labels: vec!["T-compiler", "P-high"],
+            exclude_labels: vec![],
+        },
+    });
+
+    queries.push(NamedQuery {
+        name: "issues_of_note.unassigned_p_high",
+        query: Query {
+            filters: vec!["state:open", "no=assignee"],
+            include_labels: vec!["T-compiler", "P-high"],
+            exclude_labels: vec![],
+        },
+    });
+
+    // - [N regression-from-stable-to-stable](https://github.com/rust-lang/rust/labels/regression-from-stable-to-stable)
+    //   - [M of those are not prioritized](https://github.com/rust-lang/rust/issues?q=is%3Aopen+label%3Aregression-from-stable-to-stable+-label%3AP-critical+-label%3AP-high+-label%3AP-medium+-label%3AP-low).
+    //
+    // There are N (more|less) `P-critical` issues and M (more|less) `P-high` issues in comparison with last week.
+    queries.push(NamedQuery {
+        name: "issues_of_note.regression_from_stable_to_beta",
+        query: Query {
+            filters: vec!["state:open"],
+            include_labels: vec!["regression-from-stable-to-beta"],
+            exclude_labels: vec![],
+        },
+    });
+
+    queries.push(NamedQuery {
+        name: "issues_of_note.regression_from_stable_to_nightly",
+        query: Query {
+            filters: vec!["state:open"],
+            include_labels: vec!["regression-from-stable-to-nightly"],
+            exclude_labels: vec![],
+        },
+    });
+
+    queries.push(NamedQuery {
+        name: "issues_of_note.regression_from_stable_to_stable",
+        query: Query {
+            filters: vec!["state:open"],
+            include_labels: vec!["regression-from-stable-to-stable"],
+            exclude_labels: vec![],
+        },
+    });
+
+    queries.push(NamedQuery {
+        name: "p_critical.t_compiler",
+        query: Query {
+            filters: vec!["state:open"],
+            include_labels: vec!["T-compiler", "P-critical"],
+            exclude_labels: vec![],
+        },
+    });
+
+    queries.push(NamedQuery {
+        name: "p_critical.libs_impl",
+        query: Query {
+            filters: vec!["state:open"],
+            include_labels: vec!["libs-impl", "P-critical"],
+            exclude_labels: vec![],
+        },
+    });
+
+    queries.push(NamedQuery {
+        name: "p_critical.t_rustdoc",
+        query: Query {
+            filters: vec!["state:open"],
+            include_labels: vec!["T-rustdoc", "P-critical"],
+            exclude_labels: vec![],
+        },
+    });
+
+    queries.push(NamedQuery {
+        name: "beta_regressions.unassigned_p_high",
+        query: Query {
+            filters: vec!["state:open", "no=assignee"],
+            include_labels: vec!["regression-from-stable-to-beta", "P-high"],
+            exclude_labels: vec!["T-infra", "T-release"],
+        },
+    });
+
+    queries.push(NamedQuery {
+        name: "nightly_regressions.unassigned_p_high",
+        query: Query {
+            filters: vec!["state:open", "no=assignee"],
+            include_labels: vec!["regression-from-stable-to-nightly", "P-high"],
+            exclude_labels: vec!["T-infra", "T-release"],
+        },
+    });
+
+    queries.push(NamedQuery {
+        name: "i_nominated.t_compiler",
+        query: Query {
+            filters: vec!["state:open"],
+            include_labels: vec!["I-nominated", "T-compiler"],
+            exclude_labels: vec![],
+        },
+    });
+
+    queries.push(NamedQuery {
+        name: "i_nominated.libs_impl",
+        query: Query {
+            filters: vec!["state:open"],
+            include_labels: vec!["I-nominated", "libs-impl"],
+            exclude_labels: vec![],
+        },
+    });
+
+    actions.push(RepoQuery {
+        repo: "rust-lang/rust",
+        queries,
+    });
+
+    Step {
+        name: "agenda",
+        actions,
+    }
+}
+
+pub fn final_review<'a>() -> Step<'a> {
+    Step {
+        name: "final_review",
+        actions: vec![],
+    }
+}

+ 138 - 0
src/prioritization/mod.rs

@@ -0,0 +1,138 @@
+use futures::executor::block_on;
+use reqwest::Client;
+use std::env;
+use std::fs::File;
+use std::io::Read;
+
+use crate::github::{GithubClient, Issue, Repository};
+
+pub mod config;
+
+pub struct Meeting<A: Action> {
+    pub steps: Vec<A>,
+}
+
+pub trait Action {
+    fn call(&self) -> String;
+}
+
+pub struct Step<'a> {
+    pub name: &'a str,
+    pub actions: Vec<RepoQuery<'a>>,
+}
+
+pub struct RepoQuery<'a> {
+    pub repo: &'a str,
+    pub queries: Vec<NamedQuery<'a>>,
+}
+
+pub struct NamedQuery<'a> {
+    pub name: &'a str,
+    pub query: Query<'a>,
+}
+
+pub struct Query<'a> {
+    pub filters: Vec<&'a str>,
+    pub include_labels: Vec<&'a str>,
+    pub exclude_labels: Vec<&'a str>,
+}
+
+pub trait Template {
+    fn render(&self) -> String;
+}
+
+pub struct FileTemplate<'a> {
+    name: &'a str,
+    map: Vec<(&'a str, Vec<Issue>)>,
+}
+
+impl<'a> Action for Step<'a> {
+    fn call(&self) -> String {
+        let gh = GithubClient::new(
+            Client::new(),
+            env::var("GITHUB_API_TOKEN").expect("Missing GITHUB_API_TOKEN"),
+        );
+
+        let map = self
+            .actions
+            .iter()
+            .flat_map(|RepoQuery { repo, queries }| {
+                let repository = Repository {
+                    full_name: repo.to_string(),
+                };
+
+                queries
+                    .iter()
+                    .map(|NamedQuery { name, query }| {
+                        let filters = query
+                            .filters
+                            .iter()
+                            .map(|s| s.to_string())
+                            .chain(
+                                query
+                                    .include_labels
+                                    .iter()
+                                    .map(|label| format!("label:{}", label)),
+                            )
+                            .chain(
+                                query
+                                    .exclude_labels
+                                    .iter()
+                                    .map(|label| format!("-label:{}", label)),
+                            )
+                            .chain(std::iter::once(format!("repo:{}", repository.full_name)))
+                            .collect::<Vec<_>>();
+                        let issues_search_result = block_on(
+                            repository
+                                .get_issues(&gh, &filters.iter().map(|s| s.as_ref()).collect()),
+                        )
+                        .unwrap();
+
+                        (*name, issues_search_result.items)
+                    })
+                    .collect::<Vec<_>>()
+            })
+            .collect();
+
+        let template = FileTemplate::new(self.name, map);
+        template.render()
+    }
+}
+
+impl<'a> FileTemplate<'a> {
+    fn new(name: &'a str, map: Vec<(&'a str, Vec<Issue>)>) -> Self {
+        Self { name, map }
+    }
+}
+
+impl<'a> Template for FileTemplate<'a> {
+    fn render(&self) -> String {
+        let relative_path = format!("templates/{}.tt", self.name);
+        let path = env::current_dir().unwrap().join(relative_path);
+        let path = path.as_path();
+        let mut file = File::open(path).unwrap();
+        let mut contents = String::new();
+        file.read_to_string(&mut contents).unwrap();
+
+        for (var, issues) in &self.map {
+            let var = format!("{{{}}}", var);
+            if !issues.is_empty() {
+                let issues = issues
+                    .iter()
+                    .map(|issue| {
+                        format!(
+                            "- \"{}\" [#{}]({})",
+                            issue.title, issue.number, issue.html_url
+                        )
+                    })
+                    .collect::<Vec<_>>()
+                    .join("\n");
+                contents = contents.replace(&var, &format!("{}", issues));
+            } else {
+                contents = contents.replace(&var, &format!("Empty"));
+            }
+        }
+
+        contents
+    }
+}

+ 106 - 0
templates/agenda.tt

@@ -0,0 +1,106 @@
+---
+tags: prioritization, rustc
+---
+
+# T-compiler Meeting Agenda YYYY-MM-DD
+
+[Tracking Issue](https://github.com/rust-lang/rust/issues/54818)
+
+## Announcements
+
+- Major Changes Proposals:
+  - Seconded proposals (in FCP)
+{mcp.seconded}
+  - New proposals (not seconded)
+{mcp.new_not_seconded}
+  - Old proposals (not seconded)
+{mcp.old_not_seconded}
+
+## Beta-nominations
+
+[T-compiler beta noms](https://github.com/rust-lang/rust/issues?utf8=%E2%9C%93&q=label%3Abeta-nominated+label%3AT-compiler)
+
+- {beta_nominated.t_compiler} :back: / :hand:
+
+[libs-impl beta noms](https://github.com/rust-lang/rust/issues?utf8=%E2%9C%93&q=label%3Abeta-nominated+label%3Alibs-impl)
+
+- {beta_nominated.libs_impl} :back: / :hand:
+
+[T-rustdoc beta noms](https://github.com/rust-lang/rust/issues?utf8=%E2%9C%93&q=label%3Abeta-nominated+label%3AT-rustdoc)
+
+- {beta_nominated.t_rustdoc} :back: / :hand:
+
+## Stable-nominations
+
+[T-compiler stable noms](https://github.com/rust-lang/rust/issues?utf8=%E2%9C%93&q=label%3Astable-nominated+label%3AT-compiler)
+
+- {stable_nominated.t_compiler} :back: / :hand:
+
+[libs-impl stable noms](https://github.com/rust-lang/rust/issues?utf8=%E2%9C%93&q=label%3Astable-nominated+label%3Alibs-impl)
+
+- {stable_nominated.libs_impl} :back: / :hand:
+
+[T-rustdoc stable noms](https://github.com/rust-lang/rust/issues?utf8=%E2%9C%93&q=label%3Astable-nominated+label%3AT-rustdoc)
+
+- {stable_nominated.t_rustdoc} :back: / :hand:
+
+## PR's S-waiting-on-team
+
+[T-compiler S-waiting-on-team](https://github.com/rust-lang/rust/pulls?utf8=%E2%9C%93&q=is%3Aopen+label%3AS-waiting-on-team+label%3AT-compiler)
+
+- {prs_waiting_on_team.t_compiler}
+
+[libs-impl S-waiting-on-team](https://github.com/rust-lang/rust/pulls?utf8=%E2%9C%93&q=is%3Aopen+label%3AS-waiting-on-team+label%3Alibs-impl)
+
+- {prs_waiting_on_team.libs_impl}
+
+## Issues of Note
+
+### Short Summary
+
+- [N P-critical issues](https://github.com/rust-lang/rust/issues?utf8=%E2%9C%93&q=is%3Aopen+is%3Aissue+label%3AT-compiler+label%3AP-critical+)
+  - [M of those are unassigned](https://github.com/rust-lang/rust/issues?utf8=%E2%9C%93&q=is%3Aopen+is%3Aissue+label%3AT-compiler+label%3AP-critical+no%3Aassignee)
+- [N P-high issues](https://github.com/rust-lang/rust/issues?utf8=%E2%9C%93&q=is%3Aopen+is%3Aissue+label%3AT-compiler+label%3AP-high+)
+  - [M of those are unassigned](https://github.com/rust-lang/rust/issues?utf8=%E2%9C%93&q=is%3Aopen+is%3Aissue+label%3AT-compiler+label%3AP-high+no%3Aassignee)
+- [N P-high, M P-medium, P P-low regression-from-stable-to-beta](https://github.com/rust-lang/rust/labels/regression-from-stable-to-beta)
+  - The only assigned are the P-high ones.
+- [N P-high, M P-medium, P P-low regression-from-stable-to-nightly](https://github.com/rust-lang/rust/labels/regression-from-stable-to-nightly)
+  - There are N P-medium assigned.
+- [N regression-from-stable-to-stable](https://github.com/rust-lang/rust/labels/regression-from-stable-to-stable)
+  - [M of those are not prioritized](https://github.com/rust-lang/rust/issues?q=is%3Aopen+label%3Aregression-from-stable-to-stable+-label%3AP-critical+-label%3AP-high+-label%3AP-medium+-label%3AP-low).
+
+There are N (more|less) `P-critical` issues and M (more|less) `P-high` issues in comparison with last week.
+
+### P-critical
+
+- {p_critical.t_compiler}
+  - This issue is assigned to @person
+
+- {p_critical.libs_impl}
+
+- {p_critical.t_rustdoc}
+
+### Unassigned P-high regressions
+
+- {beta_regressions.unassigned_p_high}
+- {nightly_regressions.unassigned_p_high}
+
+## Nominated Issues
+
+[T-compiler I-nominated](https://github.com/rust-lang/rust/issues?q=is%3Aopen+label%3AI-nominated+label%3AT-compiler)
+
+- {i_nominated.t_compiler}
+
+[libs-impl I-nominated](https://github.com/rust-lang/rust/issues?q=is%3Aopen+label%3AI-nominated+label%3Alibs-impl)
+
+- {i_nominated.libs_impl}
+
+## WG checkins
+
+@**WG-X** checkin by @**person1**:
+
+> Checkin text
+
+@**WG-Y** checkin by @**person2**:
+
+> Checkin text

+ 26 - 0
templates/final_review.tt

@@ -0,0 +1,26 @@
+## Announcements
+
+- Check the compiler calendar to see if there's an outstanding event to announce.
+
+## Nominate issues
+
+Check how packed the agenda looks like and if there's room for more, consider the following ...
+
+- [All Stable-to-beta regressions](https://github.com/rust-lang/rust/labels/regression-from-stable-to-beta)
+  - Check if there are relevant issues that are worth raising awareness.
+  - Assign if possible; if it remains unassigned, add it to agenda so we can assign during the meeting.
+- [All Stable-to-nightly regressions](https://github.com/rust-lang/rust/labels/regression-from-stable-to-nightly)
+  - Check if there are relevant issues that are worth raising awareness.
+  - Assign if possible; if it remains unassigned, add it to agenda so we can assign during the meeting.
+- [P-high issues](https://github.com/rust-lang/rust/issues?utf8=%E2%9C%93&q=is%3Aopen+is%3Aissue+label%3AT-compiler+label%3AP-high+)
+  - [unassigned](https://github.com/rust-lang/rust/issues?utf8=%E2%9C%93&q=is%3Aopen+is%3Aissue+label%3AT-compiler+label%3AP-high+no%3Aassignee)
+
+## Toolstate
+
+- Check [toolstate](https://rust-lang-nursery.github.io/rust-toolstate/) for outstanding tool breakage.
+  - Notify teams in the corresponding channels
+
+## Performance regressions
+
+- Check [perf regressions](http://perf.rust-lang.org/index.html).
+  - Notify involved actors.

+ 45 - 0
templates/nominations.tt

@@ -0,0 +1,45 @@
+## Stable nominations
+
+- [All stable nominations](https://github.com/rust-lang/rust/issues?utf8=%E2%9C%93&q=+label%3Astable-nominated)
+    - Add T-compiler, libs-impl or T-rustdoc tag when it corresponds.
+    - Do a sanity check on them.
+
+### Issues
+
+{nominations.stable_nominated}
+
+## Beta nominations
+
+- [All beta nominations](https://github.com/rust-lang/rust/issues?utf8=%E2%9C%93&q=label%3Abeta-nominated)
+    - Add T-compiler, libs-impl or T-rustdoc tag when it corresponds.
+    - Do a sanity check on them.
+
+### Issues
+
+{nominations.beta_nominated}
+
+## I-nominated
+
+1. [All I-nominated](https://github.com/rust-lang/rust/labels/I-nominated)
+    - Add T-compiler or libs-impl tag when it corresponds.
+    - Do a sanity check on them.
+
+### Issues
+
+{nominations.i_nominated}
+
+2. [I-nominated T-compiler](https://github.com/rust-lang/rust/issues?utf8=%E2%9C%93&q=is%3Aopen+label%3AI-nominated+label%3AT-compiler)
+    - Remove leftovers from last meeting.
+    - Do a sanity check on them.
+
+### Issues
+
+{nominations.i_nominated_t_compiler}
+
+3. [I-nominated libs-impl](https://github.com/rust-lang/rust/issues?utf8=%E2%9C%93&q=is%3Aopen+label%3AI-nominated+label%3Alibs-impl)
+    - Remove leftovers from last meeting.
+    - Do a sanity check on them.
+
+### Issues
+
+{nominations.i_nominated_libs_impl}

+ 23 - 0
templates/prs_waiting_on_team.tt

@@ -0,0 +1,23 @@
+## PR's waiting for on team
+
+1. [All PR's waiting on team](https://github.com/rust-lang/rust/pulls?q=is%3Aopen+is%3Apr+label%3AS-waiting-on-team)
+    - Add T-compiler or libs-impl tag when it corresponds.
+    - Do a sanity check on them.
+
+### Issues
+
+{prs_waiting_on_team.all}
+
+2. [PR's waiting on T-compiler](https://github.com/rust-lang/rust/pulls?q=is%3Aopen+is%3Apr+label%3AS-waiting-on-team+label%3AT-compiler)
+    - Explicitly nominate any that you think may be able to be resolved *quickly* in triage meeting.
+
+### Issues
+
+{prs_waiting_on_team.t_compiler}
+
+3. [PR's waiting on libs-impl](https://github.com/rust-lang/rust/pulls?q=is%3Aopen+is%3Apr+label%3AS-waiting-on-team+label%3Alibs-impl)
+    - Explicitly nominate any that you think may be able to be resolved *quickly* in triage meeting.
+
+### Issues
+
+{prs_waiting_on_team.libs_impl}

+ 28 - 0
templates/regressions.tt

@@ -0,0 +1,28 @@
+## Regressions
+
+1. [Beta regressions without P-label](https://github.com/rust-lang/rust/issues?q=is%3Aopen+label%3Aregression-from-stable-to-beta+-label%3AP-critical+-label%3AP-high+-label%3AP-medium+-label%3AP-low+-label%3AT-infra+-label%3AT-release)
+    - Prioritize.
+    - Ping appropriate people and/or [ICE-breakers](https://rustc-dev-guide.rust-lang.org/ice-breaker/about.html#tagging-an-issue-for-an-ice-breaker-group).
+    - Assign if possible; if it remains unassigned, add it to agenda so we can assign during the meeting.
+
+### Issues
+
+{regressions.stable_to_beta}
+
+2. [Nightly regressions without P-label](https://github.com/rust-lang/rust/issues?q=is%3Aopen+label%3Aregression-from-stable-to-nightly+-label%3AP-critical+-label%3AP-high+-label%3AP-medium+-label%3AP-low+-label%3AT-infra+-label%3AT-release)
+    - Prioritize.
+    - Ping appropriate people and/or [ICE-breakers](https://rustc-dev-guide.rust-lang.org/ice-breaker/about.html#tagging-an-issue-for-an-ice-breaker-group).
+    - Assign if possible; if it remains unassigned, add it to agenda so we can assign during the meeting.
+
+### Issues
+
+{regressions.stable_to_nightly}
+
+3. [Stable regressions without P-label](https://github.com/rust-lang/rust/issues?q=is%3Aopen+label%3Aregression-from-stable-to-stable+-label%3AP-critical+-label%3AP-high+-label%3AP-medium+-label%3AP-low+-label%3AT-infra+-label%3AT-release)
+    - Prioritize (once we have this under control).
+    - Ping appropriate people and/or [ICE-breakers](https://rustc-dev-guide.rust-lang.org/ice-breaker/about.html#tagging-an-issue-for-an-ice-breaker-group).
+    - Assign if possible; if it remains unassigned, add it to agenda so we can assign during the meeting.
+
+### Issues
+
+{regressions.stable_to_stable}

+ 27 - 0
templates/unpri_i_prioritize.tt

@@ -0,0 +1,27 @@
+## Unprioritized I-prioritize
+
+1. [All unprioritized I-prioritize](https://github.com/rust-lang/rust/issues?q=is%3Aopen+is%3Aissue+-label%3AP-critical+-label%3AP-high+-label%3AP-medium+-label%3AP-low+label%3AI-prioritize)
+    - Add T-compiler or libs-impl tag when it corresponds.
+    - Do a sanity check on them.
+
+### Issues
+
+{unpri_i_prioritize.all}
+
+2. [T-compiler](https://github.com/rust-lang/rust/issues?q=is%3Aopen+is%3Aissue+label%3AT-compiler+-label%3AP-critical+-label%3AP-high+-label%3AP-medium+-label%3AP-low+label%3AI-prioritize)
+    - Prioritize issues and remove nomination of the ones not worth discussing.
+    - Tag regressions accordingly.
+    - Ping appropriate people and/or [ICE-breakers](https://rustc-dev-guide.rust-lang.org/ice-breaker/about.html#tagging-an-issue-for-an-ice-breaker-group).
+
+### Issues
+
+{unpri_i_prioritize.t_compiler}
+
+3. [libs-impl](https://github.com/rust-lang/rust/issues?q=is%3Aopen+is%3Aissue+label%3Alibs-impl+-label%3AP-critical+-label%3AP-high+-label%3AP-medium+-label%3AP-low+label%3AI-prioritize)
+    - Prioritize issues and remove nomination of the ones not worth discussing.
+    - Tag regressions accordingly.
+    - Ping appropriate people and/or [ICE-breakers](https://rustc-dev-guide.rust-lang.org/ice-breaker/about.html#tagging-an-issue-for-an-ice-breaker-group).
+
+### Issues
+
+{unpri_i_prioritize.libs_impl}