Pick any line to see why it is the way it is: the commit, the pull request and issue it came from, and what the agent was thinking.
| The work, events, webhooks, actions, repos and packages services no longer run g1t's own agents. Work drops the 35 calls that queued issues for g1t, delegated them, messaged and reviewed through the agent, planned, captured memories automatically, rated confidence and capped agent spend, and its pull request lifecycle no longer revises or reviews: a pull request only advances to a merge by the repository's auto-merge setting. Merging a pull request that is behind now catches it up itself, through the repository's branch update, and lands it when the push arrives; when it can't be caught up cleanly the merge says to merge the base in and try again. Recording a session on a pull request, memory recall and remember, overlaps, the merge queue and required checks stay as they were. | 1 | //! Where a pull request stands, and merging it on the repository's say. |
| 2 | //! | |
| 3 | //! A pull request g1t's agent once made (`made_by_g1t`, `managed = 1`) | |
| 4 | //! still shows where it stands: its checks, the approvals the branch | |
| 5 | //! wants, the merge queue, and whether a person has to step in. Where the | |
| 6 | //! repository's settings say ready pull requests land by themselves | |
| 7 | //! (`auto_merge`), such a pull request is merged here once everything its | |
| 8 | //! branch's rules ask for is met, attributed to `g1t` (`POLICY_ACTOR_ID`). | |
| Project settings say who created the project, by name | 9 | //! |
| The work, events, webhooks, actions, repos and packages services no longer run g1t's own agents. Work drops the 35 calls that queued issues for g1t, delegated them, messaged and reviewed through the agent, planned, captured memories automatically, rated confidence and capped agent spend, and its pull request lifecycle no longer revises or reviews: a pull request only advances to a merge by the repository's auto-merge setting. Merging a pull request that is behind now catches it up itself, through the repository's branch update, and lands it when the push arrives; when it can't be caught up cleanly the merge says to merge the base in and try again. Recording a session on a pull request, memory recall and remember, overlaps, the merge queue and required checks stay as they were. | 10 | //! A merge asked for while a pull request is behind is recorded here |
| 11 | //! (`request_landing`) and lands once the push that brings it up to date | |
| 12 | //! arrives (`land_if_requested`). | |
| Project settings say who created the project, by name | 13 | |
| 14 | use g1t_contracts::repos::{GetByIdArgs, Repo, RepoPath}; | |
| 15 | use g1t_contracts::time::rfc3339; | |
| 16 | use g1t_contracts::work::*; | |
| 17 | use g1t_contracts::{FailureCode, Membership, Outcome, User, Viewer}; | |
| 18 | use g1t_kit::now_ms; | |
| 19 | use std::collections::HashMap; | |
| 20 | ||
| 21 | use serde::Deserialize; | |
| 22 | use worker::Result; | |
| 23 | use worker::wasm_bindgen::JsValue; | |
| 24 | ||
| 25 | use crate::Work; | |
| 26 | use crate::prefetch::Slot; | |
| 27 | use crate::reviews::{AGENT_ID, AGENT_NAME}; | |
| 28 | use crate::statuses::{self, WorkflowFacts}; | |
| 29 | use crate::rows::ValueRow; | |
| 30 | ||
| 31 | /// How long a claimed step is waited for before it may be taken again. | |
| 32 | const CATCH_UP_MINUTES: u64 = 30; | |
| 33 | const MERGE_MINUTES: u64 = 2; | |
| 34 | /// Who a merge made by a repository's settings is attributed to. Not an | |
| 35 | /// account: `g1t` cannot be registered. | |
| 36 | pub(crate) const POLICY_ACTOR_ID: &str = "g1t_policy"; | |
| 37 | pub(crate) const POLICY_ACTOR_NAME: &str = "g1t"; | |
| 38 | const MANAGED_PAGE: u32 = 200; | |
| 39 | ||
| 40 | /// The part of a pull request's row that tracks its lifecycle. | |
| 41 | #[derive(Deserialize)] | |
| 42 | struct Progress { | |
| 43 | managed: u8, | |
| 44 | revisions: u32, | |
| 45 | revised_at: Option<String>, | |
| 46 | working_on: Option<String>, | |
| 47 | working_until: Option<String>, | |
| 48 | stalled: Option<String>, | |
| 49 | } | |
| 50 | ||
| 51 | /// A merge asked for while the pull request was behind, as stored. | |
| 52 | #[derive(serde::Serialize, Deserialize)] | |
| 53 | #[serde(rename_all = "camelCase")] | |
| 54 | struct LandRequest { | |
| 55 | actor: User, | |
| 56 | keep_issue_open: bool, | |
| 57 | } | |
| 58 | ||
| 59 | #[derive(Deserialize)] | |
| 60 | struct LandRow { | |
| 61 | land_requested: Option<String>, | |
| 62 | land_requested_at: Option<String>, | |
| 63 | stalled: Option<String>, | |
| 64 | } | |
| 65 | ||
| 66 | /// What should happen next, if it is g1t's turn. | |
| 67 | pub(crate) enum Next { | |
| 68 | /// A step is under way, or it is a person's turn. | |
| 69 | Wait, | |
| 70 | /// Land it, because the repository says ready pull requests land. | |
| 71 | Merge, | |
| 72 | } | |
| 73 | ||
| The work, events, webhooks, actions, repos and packages services no longer run g1t's own agents. Work drops the 35 calls that queued issues for g1t, delegated them, messaged and reviewed through the agent, planned, captured memories automatically, rated confidence and capped agent spend, and its pull request lifecycle no longer revises or reviews: a pull request only advances to a merge by the repository's auto-merge setting. Merging a pull request that is behind now catches it up itself, through the repository's branch update, and lands it when the push arrives; when it can't be caught up cleanly the merge says to merge the base in and try again. Recording a session on a pull request, memory recall and remember, overlaps, the merge queue and required checks stay as they were. | 74 | /// Whether g1t's agent made this pull request, and so g1t shows where it |
| 75 | /// stands. Only pull requests from before g1t stopped hosting agents. | |
| Project settings say who created the project, by name | 76 | pub(crate) fn made_by_g1t(pull: &Pull) -> bool { |
| 77 | pull.runtime == Runtime::Hosted && pull.agent == AGENT_NAME && pull.fork.is_some() | |
| 78 | } | |
| 79 | ||
| 80 | /// Whether a verdict by `reviewer` is a person's other than the pull | |
| 81 | /// request's owner (whoever asked g1t for it, or its author): neither | |
| 82 | /// theirs nor g1t's agent's. | |
| 83 | pub(crate) fn from_someone_else(pull: &Pull, reviewer: &str) -> bool { | |
| 84 | !pull.is_owned_by(reviewer) && reviewer != AGENT_ID | |
| 85 | } | |
| 86 | ||
| 87 | fn at(stage: Stage, detail: impl Into<String>, revisions: u32) -> Lifecycle { | |
| 88 | Lifecycle { | |
| 89 | stage, | |
| 90 | detail: detail.into(), | |
| 91 | revisions, | |
| 92 | } | |
| 93 | } | |
| 94 | ||
| 95 | /// Everything the next step depends on. | |
| 96 | struct Facts { | |
| 97 | /// Still being made: not yet marked ready for review. | |
| 98 | draft: bool, | |
| 99 | /// Why g1t stopped, if it has. | |
| 100 | stalled: Option<String>, | |
| 101 | /// The step under way, if one was claimed and is still being waited for. | |
| 102 | working_on: Option<String>, | |
| 103 | /// `failed` when the merge queue took it out. | |
| 104 | check_status: Option<CheckStatus>, | |
| 105 | revisions: u32, | |
| 106 | /// Whether the branch it would land on has moved without it. | |
| 107 | behind: bool, | |
| 108 | /// Whether it is known to conflict with the branch it would land on. | |
| 109 | conflicting: bool, | |
| 110 | /// Whether the repository lands a ready pull request by itself. | |
| 111 | auto_merge: bool, | |
| 112 | /// Whether the repository refuses to merge one that is behind. | |
| 113 | require_up_to_date: bool, | |
| 114 | /// What the repository's approval rule still wants, if anything. | |
| 115 | approvals_missing: Option<String>, | |
| The work, events, webhooks, actions, repos and packages services no longer run g1t's own agents. Work drops the 35 calls that queued issues for g1t, delegated them, messaged and reviewed through the agent, planned, captured memories automatically, rated confidence and capped agent spend, and its pull request lifecycle no longer revises or reviews: a pull request only advances to a merge by the repository's auto-merge setting. Merging a pull request that is behind now catches it up itself, through the repository's branch update, and lands it when the push arrives; when it can't be caught up cleanly the merge says to merge the base in and try again. Recording a session on a pull request, memory recall and remember, overlaps, the merge queue and required checks stay as they were. | 116 | /// The person who asked for changes since the last revision, if one did. |
| 117 | person_request: Option<String>, | |
| Project settings say who created the project, by name | 118 | /// Its place in the merge queue, and what is ahead of it there. |
| 119 | queued: Option<(QueueState, Vec<u32>)>, | |
| 120 | /// What the checks on its head say, against the required ones. | |
| 121 | workflows: WorkflowFacts, | |
| 122 | } | |
| 123 | ||
| The work, events, webhooks, actions, repos and packages services no longer run g1t's own agents. Work drops the 35 calls that queued issues for g1t, delegated them, messaged and reviewed through the agent, planned, captured memories automatically, rated confidence and capped agent spend, and its pull request lifecycle no longer revises or reviews: a pull request only advances to a merge by the repository's auto-merge setting. Merging a pull request that is behind now catches it up itself, through the repository's branch update, and lands it when the push arrives; when it can't be caught up cleanly the merge says to merge the base in and try again. Recording a session on a pull request, memory recall and remember, overlaps, the merge queue and required checks stay as they were. | 124 | /// Where a pull request stands, and whether to merge it. |
| Project settings say who created the project, by name | 125 | /// |
| 126 | /// The order is: nothing while a step is under way; a person asking for | |
| The work, events, webhooks, actions, repos and packages services no longer run g1t's own agents. Work drops the 35 calls that queued issues for g1t, delegated them, messaged and reviewed through the agent, planned, captured memories automatically, rated confidence and capped agent spend, and its pull request lifecycle no longer revises or reviews: a pull request only advances to a merge by the repository's auto-merge setting. Merging a pull request that is behind now catches it up itself, through the repository's branch update, and lands it when the push arrives; when it can't be caught up cleanly the merge says to merge the base in and try again. Recording a session on a pull request, memory recall and remember, overlaps, the merge queue and required checks stay as they were. | 127 | /// changes holds it; its checks must finish, and the required ones pass; |
| 128 | /// it must not conflict, and where the repository insists, it must be up | |
| 129 | /// to date; then the approvals the branch wants. Once everything is met, | |
| 130 | /// it merges where the repository says so. | |
| Project settings say who created the project, by name | 131 | fn decide(facts: Facts) -> (Lifecycle, Next) { |
| 132 | let revisions = facts.revisions; | |
| 133 | let wait = |stage, detail: &str| (at(stage, detail, revisions), Next::Wait); | |
| 134 | ||
| 135 | if facts.draft { | |
| The work, events, webhooks, actions, repos and packages services no longer run g1t's own agents. Work drops the 35 calls that queued issues for g1t, delegated them, messaged and reviewed through the agent, planned, captured memories automatically, rated confidence and capped agent spend, and its pull request lifecycle no longer revises or reviews: a pull request only advances to a merge by the repository's auto-merge setting. Merging a pull request that is behind now catches it up itself, through the repository's branch update, and lands it when the push arrives; when it can't be caught up cleanly the merge says to merge the base in and try again. Recording a session on a pull request, memory recall and remember, overlaps, the merge queue and required checks stay as they were. | 136 | return wait(Stage::Working, "It is still a draft."); |
| Project settings say who created the project, by name | 137 | } |
| 138 | if let Some(reason) = &facts.stalled { | |
| 139 | return wait(Stage::NeedsYou, reason); | |
| 140 | } | |
| 141 | if let Some((state, ahead)) = &facts.queued { | |
| 142 | let named = ahead.iter().map(|n| format!("#{n}")).collect::<Vec<_>>().join(", "); | |
| 143 | let detail = match (state, ahead.is_empty()) { | |
| 144 | (QueueState::Testing, true) => "In the merge queue: being tested on the default branch as it is.".to_owned(), | |
| 145 | (QueueState::Testing, false) => { | |
| 146 | format!("In the merge queue: being tested together with {named}, ahead of it.") | |
| 147 | } | |
| 148 | (QueueState::Passed, true) => "Passed in the merge queue. Landing.".to_owned(), | |
| 149 | (QueueState::Passed, false) => { | |
| 150 | format!("Passed in the merge queue together with {named}. It lands once they have.") | |
| 151 | } | |
| 152 | _ => "In the merge queue, waiting for its turn to be tested.".to_owned(), | |
| 153 | }; | |
| 154 | return wait(Stage::Queued, &detail); | |
| 155 | } | |
| 156 | match facts.working_on.as_deref() { | |
| 157 | Some("catch_up") => { | |
| The work, events, webhooks, actions, repos and packages services no longer run g1t's own agents. Work drops the 35 calls that queued issues for g1t, delegated them, messaged and reviewed through the agent, planned, captured memories automatically, rated confidence and capped agent spend, and its pull request lifecycle no longer revises or reviews: a pull request only advances to a merge by the repository's auto-merge setting. Merging a pull request that is behind now catches it up itself, through the repository's branch update, and lands it when the push arrives; when it can't be caught up cleanly the merge says to merge the base in and try again. Recording a session on a pull request, memory recall and remember, overlaps, the merge queue and required checks stay as they were. | 158 | return wait(Stage::CatchingUp, "Bringing it up to date with the branch it will land on."); |
| Project settings say who created the project, by name | 159 | } |
| 160 | Some("merge") => return wait(Stage::Ready, "Merging."), | |
| The work, events, webhooks, actions, repos and packages services no longer run g1t's own agents. Work drops the 35 calls that queued issues for g1t, delegated them, messaged and reviewed through the agent, planned, captured memories automatically, rated confidence and capped agent spend, and its pull request lifecycle no longer revises or reviews: a pull request only advances to a merge by the repository's auto-merge setting. Merging a pull request that is behind now catches it up itself, through the repository's branch update, and lands it when the push arrives; when it can't be caught up cleanly the merge says to merge the base in and try again. Recording a session on a pull request, memory recall and remember, overlaps, the merge queue and required checks stay as they were. | 161 | _ => {} |
| Project settings say who created the project, by name | 162 | } |
| The work, events, webhooks, actions, repos and packages services no longer run g1t's own agents. Work drops the 35 calls that queued issues for g1t, delegated them, messaged and reviewed through the agent, planned, captured memories automatically, rated confidence and capped agent spend, and its pull request lifecycle no longer revises or reviews: a pull request only advances to a merge by the repository's auto-merge setting. Merging a pull request that is behind now catches it up itself, through the repository's branch update, and lands it when the push arrives; when it can't be caught up cleanly the merge says to merge the base in and try again. Recording a session on a pull request, memory recall and remember, overlaps, the merge queue and required checks stay as they were. | 163 | if let Some(name) = &facts.person_request { |
| 164 | return wait(Stage::NeedsYou, &format!("{name} asked for changes.")); | |
| Project settings say who created the project, by name | 165 | } |
| 166 | match facts.check_status { | |
| The work, events, webhooks, actions, repos and packages services no longer run g1t's own agents. Work drops the 35 calls that queued issues for g1t, delegated them, messaged and reviewed through the agent, planned, captured memories automatically, rated confidence and capped agent spend, and its pull request lifecycle no longer revises or reviews: a pull request only advances to a merge by the repository's auto-merge setting. Merging a pull request that is behind now catches it up itself, through the repository's branch update, and lands it when the push arrives; when it can't be caught up cleanly the merge says to merge the base in and try again. Recording a session on a pull request, memory recall and remember, overlaps, the merge queue and required checks stay as they were. | 167 | Some(CheckStatus::Failed) => return wait(Stage::NeedsYou, "It failed in the merge queue."), |
| 168 | Some(CheckStatus::Errored) => return wait(Stage::NeedsYou, "Its checks could not be run."), | |
| Project settings say who created the project, by name | 169 | _ => {} |
| 170 | } | |
| The work, events, webhooks, actions, repos and packages services no longer run g1t's own agents. Work drops the 35 calls that queued issues for g1t, delegated them, messaged and reviewed through the agent, planned, captured memories automatically, rated confidence and capped agent spend, and its pull request lifecycle no longer revises or reviews: a pull request only advances to a merge by the repository's auto-merge setting. Merging a pull request that is behind now catches it up itself, through the repository's branch update, and lands it when the push arrives; when it can't be caught up cleanly the merge says to merge the base in and try again. Recording a session on a pull request, memory recall and remember, overlaps, the merge queue and required checks stay as they were. | 171 | // Only the checks the branch requires hold it. |
| 172 | let required = facts.workflows.required_failed(); | |
| 173 | if !required.is_empty() { | |
| 174 | return wait( | |
| 175 | Stage::NeedsYou, | |
| 176 | &format!( | |
| 177 | "The required {} {} {}.", | |
| 178 | if required.len() == 1 { "check" } else { "checks" }, | |
| 179 | statuses::list(&required), | |
| 180 | if required.len() == 1 { "fails" } else { "fail" }, | |
| 181 | ), | |
| 182 | ); | |
| Project settings say who created the project, by name | 183 | } |
| 184 | if !facts.workflows.pending.is_empty() { | |
| 185 | return wait( | |
| 186 | Stage::Checking, | |
| 187 | &format!("Waiting for {} to finish.", statuses::list(&facts.workflows.pending)), | |
| 188 | ); | |
| 189 | } | |
| 190 | let expected = facts.workflows.expected(); | |
| 191 | if !expected.is_empty() { | |
| 192 | return wait( | |
| 193 | Stage::Checking, | |
| 194 | &format!( | |
| 195 | "Waiting for the required {} {} to report on its latest commit.", | |
| 196 | if expected.len() == 1 { "check" } else { "checks" }, | |
| 197 | statuses::list(&expected) | |
| 198 | ), | |
| 199 | ); | |
| 200 | } | |
| 201 | if facts.conflicting { | |
| The work, events, webhooks, actions, repos and packages services no longer run g1t's own agents. Work drops the 35 calls that queued issues for g1t, delegated them, messaged and reviewed through the agent, planned, captured memories automatically, rated confidence and capped agent spend, and its pull request lifecycle no longer revises or reviews: a pull request only advances to a merge by the repository's auto-merge setting. Merging a pull request that is behind now catches it up itself, through the repository's branch update, and lands it when the push arrives; when it can't be caught up cleanly the merge says to merge the base in and try again. Recording a session on a pull request, memory recall and remember, overlaps, the merge queue and required checks stay as they were. | 202 | return wait( |
| 203 | Stage::NeedsYou, | |
| 204 | "It conflicts with the branch it will land on. Merge that branch in, resolve the conflicts and push.", | |
| Project settings say who created the project, by name | 205 | ); |
| 206 | } | |
| 207 | if facts.behind && facts.require_up_to_date { | |
| The work, events, webhooks, actions, repos and packages services no longer run g1t's own agents. Work drops the 35 calls that queued issues for g1t, delegated them, messaged and reviewed through the agent, planned, captured memories automatically, rated confidence and capped agent spend, and its pull request lifecycle no longer revises or reviews: a pull request only advances to a merge by the repository's auto-merge setting. Merging a pull request that is behind now catches it up itself, through the repository's branch update, and lands it when the push arrives; when it can't be caught up cleanly the merge says to merge the base in and try again. Recording a session on a pull request, memory recall and remember, overlaps, the merge queue and required checks stay as they were. | 208 | return wait( |
| 209 | Stage::NeedsYou, | |
| 210 | "The branch it will land on has moved, and this repository requires pull requests to be up to date. Catch up with it first.", | |
| Project settings say who created the project, by name | 211 | ); |
| 212 | } | |
| 213 | if let Some(missing) = &facts.approvals_missing { | |
| 214 | return wait(Stage::NeedsYou, missing); | |
| 215 | } | |
| 216 | if facts.auto_merge { | |
| 217 | return ( | |
| 218 | at( | |
| 219 | Stage::Ready, | |
| 220 | "Everything this repository asks for is met. Merging, as its settings say.", | |
| 221 | revisions, | |
| 222 | ), | |
| 223 | Next::Merge, | |
| 224 | ); | |
| 225 | } | |
| 226 | wait( | |
| 227 | Stage::Ready, | |
| 228 | if facts.behind { | |
| 229 | "Ready to merge. Merging brings it up to date with the default branch first." | |
| 230 | } else { | |
| 231 | "Everything this repository asks for is met. Ready to merge." | |
| 232 | }, | |
| 233 | ) | |
| 234 | } | |
| 235 | ||
| 236 | impl Work { | |
| The work, events, webhooks, actions, repos and packages services no longer run g1t's own agents. Work drops the 35 calls that queued issues for g1t, delegated them, messaged and reviewed through the agent, planned, captured memories automatically, rated confidence and capped agent spend, and its pull request lifecycle no longer revises or reviews: a pull request only advances to a merge by the repository's auto-merge setting. Merging a pull request that is behind now catches it up itself, through the repository's branch update, and lands it when the push arrives; when it can't be caught up cleanly the merge says to merge the base in and try again. Recording a session on a pull request, memory recall and remember, overlaps, the merge queue and required checks stay as they were. | 237 | /// Where a pull request stands and whether g1t merges it, remembered |
| 238 | /// so lists can show it without working it out again. `None` for one | |
| 239 | /// g1t is not seeing through. | |
| Project settings say who created the project, by name | 240 | pub(crate) async fn assess( |
| 241 | &self, | |
| 242 | pull: &Pull, | |
| The work, events, webhooks, actions, repos and packages services no longer run g1t's own agents. Work drops the 35 calls that queued issues for g1t, delegated them, messaged and reviewed through the agent, planned, captured memories automatically, rated confidence and capped agent spend, and its pull request lifecycle no longer revises or reviews: a pull request only advances to a merge by the repository's auto-merge setting. Merging a pull request that is behind now catches it up itself, through the repository's branch update, and lands it when the push arrives; when it can't be caught up cleanly the merge says to merge the base in and try again. Recording a session on a pull request, memory recall and remember, overlaps, the merge queue and required checks stay as they were. | 243 | _issue: &Option<Issue>, |
| Project settings say who created the project, by name | 244 | behind: bool, |
| 245 | ) -> Result<Option<(Lifecycle, Next)>> { | |
| The work, events, webhooks, actions, repos and packages services no longer run g1t's own agents. Work drops the 35 calls that queued issues for g1t, delegated them, messaged and reviewed through the agent, planned, captured memories automatically, rated confidence and capped agent spend, and its pull request lifecycle no longer revises or reviews: a pull request only advances to a merge by the repository's auto-merge setting. Merging a pull request that is behind now catches it up itself, through the repository's branch update, and lands it when the push arrives; when it can't be caught up cleanly the merge says to merge the base in and try again. Recording a session on a pull request, memory recall and remember, overlaps, the merge queue and required checks stay as they were. | 246 | let assessed = self.assess_now(pull, behind).await?; |
| 247 | if let Some((lifecycle, _)) = &assessed | |
| Project settings say who created the project, by name | 248 | && !self.remembered_as(&pull.id, lifecycle)? |
| 249 | { | |
| 250 | self.remember(&pull.id, lifecycle).await?; | |
| 251 | } | |
| 252 | Ok(assessed) | |
| 253 | } | |
| 254 | ||
| 255 | /// Whether the row read for this request already says `lifecycle`, so | |
| 256 | /// that showing a pull request does not write it again unchanged. | |
| 257 | fn remembered_as(&self, pull_id: &str, lifecycle: &Lifecycle) -> Result<bool> { | |
| 258 | let Some(found) = self.prefetched_pull(pull_id) else { | |
| 259 | return Ok(false); | |
| 260 | }; | |
| 261 | Ok(found | |
| 262 | .first::<crate::rows::Snapshot>(Slot::Pull)? | |
| 263 | .is_some_and(|stored| { | |
| 264 | stored.stage == Some(lifecycle.stage) && stored.stage_detail.as_deref() == Some(lifecycle.detail.as_str()) | |
| 265 | })) | |
| 266 | } | |
| 267 | ||
| The work, events, webhooks, actions, repos and packages services no longer run g1t's own agents. Work drops the 35 calls that queued issues for g1t, delegated them, messaged and reviewed through the agent, planned, captured memories automatically, rated confidence and capped agent spend, and its pull request lifecycle no longer revises or reviews: a pull request only advances to a merge by the repository's auto-merge setting. Merging a pull request that is behind now catches it up itself, through the repository's branch update, and lands it when the push arrives; when it can't be caught up cleanly the merge says to merge the base in and try again. Recording a session on a pull request, memory recall and remember, overlaps, the merge queue and required checks stay as they were. | 268 | /// Saves where a pull request stands. |
| Project settings say who created the project, by name | 269 | pub(crate) async fn remember(&self, pull_id: &str, lifecycle: &Lifecycle) -> Result<()> { |
| 270 | let stage = serde_json::to_value(lifecycle.stage)?; | |
| 271 | self.db | |
| 272 | .prepare("UPDATE pulls SET stage = ?, stage_detail = ? WHERE id = ?") | |
| 273 | .bind(&[ | |
| 274 | stage.as_str().unwrap_or_default().into(), | |
| 275 | lifecycle.detail.as_str().into(), | |
| 276 | pull_id.into(), | |
| 277 | ])? | |
| 278 | .run() | |
| 279 | .await?; | |
| 280 | Ok(()) | |
| 281 | } | |
| 282 | ||
| The work, events, webhooks, actions, repos and packages services no longer run g1t's own agents. Work drops the 35 calls that queued issues for g1t, delegated them, messaged and reviewed through the agent, planned, captured memories automatically, rated confidence and capped agent spend, and its pull request lifecycle no longer revises or reviews: a pull request only advances to a merge by the repository's auto-merge setting. Merging a pull request that is behind now catches it up itself, through the repository's branch update, and lands it when the push arrives; when it can't be caught up cleanly the merge says to merge the base in and try again. Recording a session on a pull request, memory recall and remember, overlaps, the merge queue and required checks stay as they were. | 283 | async fn assess_now(&self, pull: &Pull, behind: bool) -> Result<Option<(Lifecycle, Next)>> { |
| Project settings say who created the project, by name | 284 | if !pull.status.is_active() { |
| 285 | return Ok(None); | |
| 286 | } | |
| 287 | // As read for this request (prefetch.rs), or now. | |
| 288 | let prefetched = self.prefetched_pull(&pull.id); | |
| 289 | let progress = match &prefetched { | |
| 290 | Some(found) => found.first::<Progress>(Slot::Pull)?, | |
| 291 | None => { | |
| 292 | self.db | |
| 293 | .prepare( | |
| 294 | "SELECT managed, revisions, revised_at, working_on, working_until, stalled | |
| 295 | FROM pulls WHERE id = ?", | |
| 296 | ) | |
| 297 | .bind(&[pull.id.as_str().into()])? | |
| 298 | .first::<Progress>(None) | |
| 299 | .await? | |
| 300 | } | |
| 301 | }; | |
| 302 | let Some(progress) = progress.filter(|progress| progress.managed != 0) else { | |
| 303 | return Ok(None); | |
| 304 | }; | |
| 305 | let now = rfc3339(now_ms()); | |
| 306 | let working_on = progress | |
| 307 | .working_until | |
| 308 | .as_deref() | |
| 309 | .is_some_and(|until| until > now.as_str()) | |
| 310 | .then(|| progress.working_on.clone().unwrap_or_default()); | |
| Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar | 311 | let settings = self.settings_for(pull).await?; |
| A required check named for a workflow job, as GitHub's branch rules name them, is met by that job. Each g1t Actions job on a pull request's head counts as a check of its name (`test`, `build (ubuntu-latest, 22)`, or `deploy / build` for a called workflow's job), from the latest attempt of its workflow: skipped passes, cancelled fails, and a job still going holds the merge. So a ruleset or required check copied from GitHub needs no renaming, and one that names the workflow, such as `CI`, is met by the whole run as before. This holds in the merge box, for the merge queue and for agents' merges; jobs count only toward required checks, so a `continue-on-error` job that fails doesn't send an agent back. The pull requests guide says so. | 312 | let statuses = self.statuses(&pull.repo_id, pull.head_commit.as_deref()).await?; |
| 313 | let workflows = self.workflow_facts(&pull.repo_id, pull.head_commit.as_deref(), &statuses, &settings.required_checks).await?; | |
| Merge rulesets: branch and tag rules, agent-first, enforced on push and merge | 314 | // The rules of the branch it merges into, as g1t sees them when it |
| The work, events, webhooks, actions, repos and packages services no longer run g1t's own agents. Work drops the 35 calls that queued issues for g1t, delegated them, messaged and reviewed through the agent, planned, captured memories automatically, rated confidence and capped agent spend, and its pull request lifecycle no longer revises or reviews: a pull request only advances to a merge by the repository's auto-merge setting. Merging a pull request that is behind now catches it up itself, through the repository's branch update, and lands it when the push arrives; when it can't be caught up cleanly the merge says to merge the base in and try again. Recording a session on a pull request, memory recall and remember, overlaps, the merge queue and required checks stay as they were. | 315 | // merges by itself: what people must still do, and whether an |
| 316 | // agent's change may land here unattended. | |
| Merge rulesets: branch and tag rules, agent-first, enforced on push and merge | 317 | let gate = match self.repo_for(pull).await? { |
| 318 | Some(repo) => Some(self.merge_gate(&repo, pull, Some(&User::system(&repo.namespace)), false, true).await?), | |
| 319 | None => None, | |
| 320 | }; | |
| 321 | let rules_allow_auto_merge = gate | |
| 322 | .as_ref() | |
| The contracts no longer describe g1t's own agents, and workspaces can keep their AI providers. Every consumer had already stopped using them, so the TypeScript and Rust contracts drop the workspace-agent, ability, look, skill-library, context, runner-agent and mention modules, the chat and docs RPCs agents posted and read through, the folio agent methods, the agent_waiting notification, agent.asked and memory.changed events, AI credit, token and gateway billing types, model routes and sessions, team agents, agent tokens, plan, review, message and confidence types, command rules, cost caps and confidence rules. The agents:run and models:* scopes are no longer offered by any preset, OAuth default or permission form, but a stored token or grant that names them still reads; and the removed operations are gone from the scope table, so the API's check that every operation has exactly one scope is strict again. What stored data needs in order to parse (old run and credential kinds, an agent team lead, retired rules and webhook events, old guardrail and integration settings) still parses, and a run's token is still PrincipalKind::Agent, which now means a run's token and the @g1t rows written before. The customize, marketplace, skills and claude-plugin contracts stay only until the mobile app drops its Customize screens. | 323 | .is_none_or(|gate| g1t_rules::merge::auto_merge_refusal(&gate.requirements).is_none()); |
| Project settings say who created the project, by name | 324 | let (lifecycle, next) = decide(Facts { |
| 325 | draft: pull.status == PullStatus::Draft, | |
| The work, events, webhooks, actions, repos and packages services no longer run g1t's own agents. Work drops the 35 calls that queued issues for g1t, delegated them, messaged and reviewed through the agent, planned, captured memories automatically, rated confidence and capped agent spend, and its pull request lifecycle no longer revises or reviews: a pull request only advances to a merge by the repository's auto-merge setting. Merging a pull request that is behind now catches it up itself, through the repository's branch update, and lands it when the push arrives; when it can't be caught up cleanly the merge says to merge the base in and try again. Recording a session on a pull request, memory recall and remember, overlaps, the merge queue and required checks stay as they were. | 326 | stalled: progress.stalled, |
| Project settings say who created the project, by name | 327 | working_on, |
| 328 | check_status: pull.check_status, | |
| 329 | revisions: progress.revisions, | |
| 330 | behind, | |
| 331 | conflicting: self.conflicting_files(pull).await?.is_some(), | |
| Merge rulesets: branch and tag rules, agent-first, enforced on push and merge | 332 | auto_merge: settings.auto_merge && rules_allow_auto_merge, |
| Project settings say who created the project, by name | 333 | require_up_to_date: settings.require_up_to_date, |
| Merge rulesets: branch and tag rules, agent-first, enforced on push and merge | 334 | approvals_missing: gate.as_ref().and_then(|gate| gate.people_gap()), |
| The work, events, webhooks, actions, repos and packages services no longer run g1t's own agents. Work drops the 35 calls that queued issues for g1t, delegated them, messaged and reviewed through the agent, planned, captured memories automatically, rated confidence and capped agent spend, and its pull request lifecycle no longer revises or reviews: a pull request only advances to a merge by the repository's auto-merge setting. Merging a pull request that is behind now catches it up itself, through the repository's branch update, and lands it when the push arrives; when it can't be caught up cleanly the merge says to merge the base in and try again. Recording a session on a pull request, memory recall and remember, overlaps, the merge queue and required checks stay as they were. | 335 | person_request: self.person_request(pull, progress.revised_at.as_deref()).await?, |
| Project settings say who created the project, by name | 336 | queued: self.queued_entry(&pull.id).await?, |
| 337 | workflows, | |
| 338 | }); | |
| The work, events, webhooks, actions, repos and packages services no longer run g1t's own agents. Work drops the 35 calls that queued issues for g1t, delegated them, messaged and reviewed through the agent, planned, captured memories automatically, rated confidence and capped agent spend, and its pull request lifecycle no longer revises or reviews: a pull request only advances to a merge by the repository's auto-merge setting. Merging a pull request that is behind now catches it up itself, through the repository's branch update, and lands it when the push arrives; when it can't be caught up cleanly the merge says to merge the base in and try again. Recording a session on a pull request, memory recall and remember, overlaps, the merge queue and required checks stay as they were. | 339 | Ok(Some((lifecycle, next))) |
| Project settings say who created the project, by name | 340 | } |
| 341 | ||
| The work, events, webhooks, actions, repos and packages services no longer run g1t's own agents. Work drops the 35 calls that queued issues for g1t, delegated them, messaged and reviewed through the agent, planned, captured memories automatically, rated confidence and capped agent spend, and its pull request lifecycle no longer revises or reviews: a pull request only advances to a merge by the repository's auto-merge setting. Merging a pull request that is behind now catches it up itself, through the repository's branch update, and lands it when the push arrives; when it can't be caught up cleanly the merge says to merge the base in and try again. Recording a session on a pull request, memory recall and remember, overlaps, the merge queue and required checks stay as they were. | 342 | /// Who last asked for changes, if it was a person other than its owner |
| 343 | /// (whoever asked g1t for it, or its author), that person's latest | |
| 344 | /// verdict, and after the last revision. | |
| 345 | async fn person_request(&self, pull: &Pull, revised_at: Option<&str>) -> Result<Option<String>> { | |
| Project settings say who created the project, by name | 346 | #[derive(Deserialize)] |
| 347 | struct Verdicts { | |
| 348 | author_id: String, | |
| 349 | author_name: String, | |
| 350 | verdict: Verdict, | |
| 351 | created_at: String, | |
| 352 | } | |
| 353 | let rows = match self.prefetched_pull(&pull.id) { | |
| 354 | Some(found) => found | |
| 355 | .rows::<Verdicts>(Slot::Verdicts)? | |
| 356 | .into_iter() | |
| 357 | .filter(|row| from_someone_else(pull, &row.author_id)) | |
| 358 | .collect(), | |
| 359 | None => self | |
| 360 | .db | |
| 361 | .prepare( | |
| 362 | "SELECT author_id, author_name, verdict, created_at FROM comments | |
| 363 | WHERE repo_id = ? AND number = ? AND verdict IS NOT NULL | |
| 364 | AND author_id != ? AND author_id != ? | |
| 365 | ORDER BY id", | |
| 366 | ) | |
| 367 | .bind(&[ | |
| 368 | pull.repo_id.as_str().into(), | |
| 369 | pull.number.into(), | |
| 370 | pull.owner().id.as_str().into(), | |
| 371 | AGENT_ID.into(), | |
| 372 | ])? | |
| 373 | .all() | |
| 374 | .await? | |
| 375 | .results::<Verdicts>()?, | |
| 376 | }; | |
| 377 | // Each person's latest verdict is the one that stands. | |
| 378 | let mut latest: HashMap<String, Verdicts> = HashMap::new(); | |
| 379 | for row in rows { | |
| 380 | latest.insert(row.author_id.clone(), row); | |
| 381 | } | |
| 382 | Ok(latest | |
| 383 | .into_values() | |
| 384 | .filter(|row| row.verdict == Verdict::RequestChanges) | |
| 385 | .filter(|row| revised_at.is_none_or(|revised| row.created_at.as_str() > revised)) | |
| 386 | .max_by(|a, b| a.created_at.cmp(&b.created_at)) | |
| The work, events, webhooks, actions, repos and packages services no longer run g1t's own agents. Work drops the 35 calls that queued issues for g1t, delegated them, messaged and reviewed through the agent, planned, captured memories automatically, rated confidence and capped agent spend, and its pull request lifecycle no longer revises or reviews: a pull request only advances to a merge by the repository's auto-merge setting. Merging a pull request that is behind now catches it up itself, through the repository's branch update, and lands it when the push arrives; when it can't be caught up cleanly the merge says to merge the base in and try again. Recording a session on a pull request, memory recall and remember, overlaps, the merge queue and required checks stay as they were. | 387 | .map(|row| row.author_name)) |
| Project settings say who created the project, by name | 388 | } |
| 389 | ||
| 390 | /// Takes a step for a pull request, if nobody else has. One statement, | |
| 391 | /// so that two callers cannot both take it. | |
| The work, events, webhooks, actions, repos and packages services no longer run g1t's own agents. Work drops the 35 calls that queued issues for g1t, delegated them, messaged and reviewed through the agent, planned, captured memories automatically, rated confidence and capped agent spend, and its pull request lifecycle no longer revises or reviews: a pull request only advances to a merge by the repository's auto-merge setting. Merging a pull request that is behind now catches it up itself, through the repository's branch update, and lands it when the push arrives; when it can't be caught up cleanly the merge says to merge the base in and try again. Recording a session on a pull request, memory recall and remember, overlaps, the merge queue and required checks stay as they were. | 392 | pub(crate) async fn claim(&self, pull_id: &str, step: &str, minutes: u64) -> Result<bool> { |
| Project settings say who created the project, by name | 393 | let now = now_ms(); |
| 394 | Ok(self | |
| 395 | .db | |
| The work, events, webhooks, actions, repos and packages services no longer run g1t's own agents. Work drops the 35 calls that queued issues for g1t, delegated them, messaged and reviewed through the agent, planned, captured memories automatically, rated confidence and capped agent spend, and its pull request lifecycle no longer revises or reviews: a pull request only advances to a merge by the repository's auto-merge setting. Merging a pull request that is behind now catches it up itself, through the repository's branch update, and lands it when the push arrives; when it can't be caught up cleanly the merge says to merge the base in and try again. Recording a session on a pull request, memory recall and remember, overlaps, the merge queue and required checks stay as they were. | 396 | .prepare( |
| 397 | "UPDATE pulls SET working_on = ?2, working_until = ?3 | |
| Project settings say who created the project, by name | 398 | WHERE id = ?4 AND status = 'open' AND stalled IS NULL |
| 399 | AND (working_until IS NULL OR working_until < ?1) | |
| The work, events, webhooks, actions, repos and packages services no longer run g1t's own agents. Work drops the 35 calls that queued issues for g1t, delegated them, messaged and reviewed through the agent, planned, captured memories automatically, rated confidence and capped agent spend, and its pull request lifecycle no longer revises or reviews: a pull request only advances to a merge by the repository's auto-merge setting. Merging a pull request that is behind now catches it up itself, through the repository's branch update, and lands it when the push arrives; when it can't be caught up cleanly the merge says to merge the base in and try again. Recording a session on a pull request, memory recall and remember, overlaps, the merge queue and required checks stay as they were. | 400 | RETURNING id AS value", |
| 401 | ) | |
| Project settings say who created the project, by name | 402 | .bind(&[ |
| 403 | rfc3339(now).into(), | |
| 404 | step.into(), | |
| 405 | rfc3339(now + minutes * 60 * 1000).into(), | |
| 406 | pull_id.into(), | |
| 407 | ])? | |
| 408 | .first::<ValueRow>(None) | |
| 409 | .await? | |
| 410 | .is_some()) | |
| 411 | } | |
| 412 | ||
| The work, events, webhooks, actions, repos and packages services no longer run g1t's own agents. Work drops the 35 calls that queued issues for g1t, delegated them, messaged and reviewed through the agent, planned, captured memories automatically, rated confidence and capped agent spend, and its pull request lifecycle no longer revises or reviews: a pull request only advances to a merge by the repository's auto-merge setting. Merging a pull request that is behind now catches it up itself, through the repository's branch update, and lands it when the push arrives; when it can't be caught up cleanly the merge says to merge the base in and try again. Recording a session on a pull request, memory recall and remember, overlaps, the merge queue and required checks stay as they were. | 413 | /// Merges a pull request g1t is seeing through once it is ready, where |
| 414 | /// the repository's settings say ready pull requests land by | |
| 415 | /// themselves. Returns what the runner is to do, which is now always | |
| 416 | /// nothing: no step is carried out in a sandbox any more. | |
| Project settings say who created the project, by name | 417 | pub(crate) async fn advance(&self, a: AdvanceArgs) -> Result<Advance> { |
| 418 | let Some(pull) = self.pull_by_id(&a.pull_id).await? else { | |
| 419 | return Ok(Advance::None); | |
| 420 | }; | |
| 421 | if pull.status != PullStatus::Open { | |
| 422 | return Ok(Advance::None); | |
| 423 | } | |
| 424 | // As a member: a private repository would look missing otherwise, | |
| 425 | // and the pull request would never move. | |
| 426 | let viewer: Viewer = self.owner_viewer(&pull).await?; | |
| 427 | let repo: Outcome<Repo> = g1t_kit::call( | |
| 428 | &self.repos, | |
| 429 | "get_by_id", | |
| 430 | &GetByIdArgs { | |
| 431 | id: pull.repo_id.clone(), | |
| 432 | viewer, | |
| 433 | }, | |
| 434 | ) | |
| 435 | .await?; | |
| The work, events, webhooks, actions, repos and packages services no longer run g1t's own agents. Work drops the 35 calls that queued issues for g1t, delegated them, messaged and reviewed through the agent, planned, captured memories automatically, rated confidence and capped agent spend, and its pull request lifecycle no longer revises or reviews: a pull request only advances to a merge by the repository's auto-merge setting. Merging a pull request that is behind now catches it up itself, through the repository's branch update, and lands it when the push arrives; when it can't be caught up cleanly the merge says to merge the base in and try again. Recording a session on a pull request, memory recall and remember, overlaps, the merge queue and required checks stay as they were. | 436 | let Outcome::Ok(repo) = crate::retired::unless_archived(repo) else { |
| Project settings say who created the project, by name | 437 | return Ok(Advance::None); |
| 438 | }; | |
| 439 | let behind = self.is_behind(&repo.id, &pull).await?; | |
| The work, events, webhooks, actions, repos and packages services no longer run g1t's own agents. Work drops the 35 calls that queued issues for g1t, delegated them, messaged and reviewed through the agent, planned, captured memories automatically, rated confidence and capped agent spend, and its pull request lifecycle no longer revises or reviews: a pull request only advances to a merge by the repository's auto-merge setting. Merging a pull request that is behind now catches it up itself, through the repository's branch update, and lands it when the push arrives; when it can't be caught up cleanly the merge says to merge the base in and try again. Recording a session on a pull request, memory recall and remember, overlaps, the merge queue and required checks stay as they were. | 440 | if let Some((_, Next::Merge)) = self.assess(&pull, &None, behind).await? { |
| Project settings say who created the project, by name | 441 | self.merge_by_policy(&repo, &pull).await?; |
| 442 | } | |
| The work, events, webhooks, actions, repos and packages services no longer run g1t's own agents. Work drops the 35 calls that queued issues for g1t, delegated them, messaged and reviewed through the agent, planned, captured memories automatically, rated confidence and capped agent spend, and its pull request lifecycle no longer revises or reviews: a pull request only advances to a merge by the repository's auto-merge setting. Merging a pull request that is behind now catches it up itself, through the repository's branch update, and lands it when the push arrives; when it can't be caught up cleanly the merge says to merge the base in and try again. Recording a session on a pull request, memory recall and remember, overlaps, the merge queue and required checks stay as they were. | 443 | Ok(Advance::None) |
| Project settings say who created the project, by name | 444 | } |
| 445 | ||
| 446 | /// Lands a pull request that is ready, on the authority of the | |
| 447 | /// repository's settings instead of a person's click. | |
| 448 | async fn merge_by_policy(&self, repo: &Repo, pull: &Pull) -> Result<()> { | |
| The work, events, webhooks, actions, repos and packages services no longer run g1t's own agents. Work drops the 35 calls that queued issues for g1t, delegated them, messaged and reviewed through the agent, planned, captured memories automatically, rated confidence and capped agent spend, and its pull request lifecycle no longer revises or reviews: a pull request only advances to a merge by the repository's auto-merge setting. Merging a pull request that is behind now catches it up itself, through the repository's branch update, and lands it when the push arrives; when it can't be caught up cleanly the merge says to merge the base in and try again. Recording a session on a pull request, memory recall and remember, overlaps, the merge queue and required checks stay as they were. | 449 | if !self.claim(&pull.id, "merge", MERGE_MINUTES).await? { |
| Project settings say who created the project, by name | 450 | return Ok(()); |
| 451 | } | |
| 452 | // g1t acts for the workspace whose members turned this on, with the | |
| 453 | // default base permission (Write), which merging needs. | |
| 454 | let actor = User { | |
| 455 | id: POLICY_ACTOR_ID.to_owned(), | |
| 456 | username: POLICY_ACTOR_NAME.to_owned(), | |
| 457 | verified: true, | |
| 458 | workspaces: vec![Membership::member(repo.namespace.to_lowercase())], | |
| 459 | ..User::default() | |
| 460 | }; | |
| 461 | let merged = self | |
| 462 | .merge_pull(PullActionArgs { | |
| 463 | actor, | |
| 464 | repo: RepoPath { | |
| 465 | namespace: repo.namespace.clone(), | |
| 466 | name: repo.name.clone(), | |
| 467 | }, | |
| 468 | number: pull.number, | |
| 469 | summary: String::new(), | |
| 470 | keep_issue_open: false, | |
| 471 | ignore_checks: false, | |
| Merge rulesets: branch and tag rules, agent-first, enforced on push and merge | 472 | bypass_rules: false, |
| Project settings say who created the project, by name | 473 | }) |
| 474 | .await?; | |
| 475 | match merged { | |
| 476 | Outcome::Ok(_) => Ok(()), | |
| 477 | // Most likely the branch moved in the moment between: let go, and | |
| The work, events, webhooks, actions, repos and packages services no longer run g1t's own agents. Work drops the 35 calls that queued issues for g1t, delegated them, messaged and reviewed through the agent, planned, captured memories automatically, rated confidence and capped agent spend, and its pull request lifecycle no longer revises or reviews: a pull request only advances to a merge by the repository's auto-merge setting. Merging a pull request that is behind now catches it up itself, through the repository's branch update, and lands it when the push arrives; when it can't be caught up cleanly the merge says to merge the base in and try again. Recording a session on a pull request, memory recall and remember, overlaps, the merge queue and required checks stay as they were. | 478 | // the next look at it tries again. |
| Project settings say who created the project, by name | 479 | Outcome::Fail(failure) if failure.code == FailureCode::Conflict => { |
| 480 | self.db | |
| 481 | .prepare( | |
| 482 | "UPDATE pulls SET working_on = NULL, working_until = NULL | |
| 483 | WHERE id = ? AND working_on = 'merge'", | |
| 484 | ) | |
| 485 | .bind(&[pull.id.as_str().into()])? | |
| 486 | .run() | |
| 487 | .await?; | |
| 488 | Ok(()) | |
| 489 | } | |
| 490 | Outcome::Fail(failure) => { | |
| 491 | self.stall(StallArgs { | |
| 492 | pull_id: pull.id.clone(), | |
| 493 | reason: format!("g1t could not merge this: {}", failure.message), | |
| Events: review requests, assignments, stops and deployments are published | 494 | by: None, |
| Project settings say who created the project, by name | 495 | }) |
| 496 | .await?; | |
| 497 | Ok(()) | |
| 498 | } | |
| 499 | } | |
| 500 | } | |
| 501 | ||
| 502 | /// Records that a merge was asked for while the pull request was | |
| The work, events, webhooks, actions, repos and packages services no longer run g1t's own agents. Work drops the 35 calls that queued issues for g1t, delegated them, messaged and reviewed through the agent, planned, captured memories automatically, rated confidence and capped agent spend, and its pull request lifecycle no longer revises or reviews: a pull request only advances to a merge by the repository's auto-merge setting. Merging a pull request that is behind now catches it up itself, through the repository's branch update, and lands it when the push arrives; when it can't be caught up cleanly the merge says to merge the base in and try again. Recording a session on a pull request, memory recall and remember, overlaps, the merge queue and required checks stay as they were. | 503 | /// behind, so that it lands once the push that brings it up to date |
| 504 | /// arrives, and announces it. | |
| Project settings say who created the project, by name | 505 | pub(crate) async fn request_landing( |
| 506 | &self, | |
| 507 | pull: &Pull, | |
| 508 | actor: &User, | |
| 509 | keep_issue_open: bool, | |
| 510 | ) -> Result<()> { | |
| 511 | let now = now_ms(); | |
| 512 | let request = serde_json::to_string(&LandRequest { | |
| 513 | actor: actor.clone(), | |
| 514 | keep_issue_open, | |
| 515 | })?; | |
| 516 | self.db | |
| 517 | .prepare( | |
| 518 | "UPDATE pulls | |
| 519 | SET land_requested = ?, land_requested_at = ?, stalled = NULL, | |
| 520 | working_on = 'catch_up', working_until = ? | |
| 521 | WHERE id = ?", | |
| 522 | ) | |
| 523 | .bind(&[ | |
| 524 | request.into(), | |
| 525 | rfc3339(now).into(), | |
| 526 | rfc3339(now + CATCH_UP_MINUTES * 60 * 1000).into(), | |
| 527 | pull.id.as_str().into(), | |
| 528 | ])? | |
| 529 | .run() | |
| 530 | .await?; | |
| 531 | self.publish( | |
| 532 | "pull.merge_requested", | |
| 533 | &pull.repo_id, | |
| 534 | actor, | |
| 535 | Self::pull_event(pull), | |
| 536 | ) | |
| 537 | .await | |
| 538 | } | |
| 539 | ||
| 540 | /// The merge waiting on a pull request, if one was asked for recently | |
| 541 | /// enough to still stand. | |
| 542 | async fn land_request(&self, pull_id: &str) -> Result<Option<LandRequest>> { | |
| 543 | let row = self.land_row(pull_id).await?; | |
| 544 | let oldest = rfc3339(now_ms().saturating_sub(CATCH_UP_MINUTES * 60 * 1000)); | |
| 545 | Ok(row | |
| 546 | .filter(|row| { | |
| 547 | row.land_requested_at | |
| 548 | .as_deref() | |
| 549 | .is_some_and(|at| at >= oldest.as_str()) | |
| 550 | }) | |
| 551 | .and_then(|row| row.land_requested) | |
| 552 | .and_then(|request| serde_json::from_str(&request).ok())) | |
| 553 | } | |
| 554 | ||
| 555 | /// Whether a merge is waiting on a pull request, and why g1t stopped | |
| 556 | /// working on it if it did. | |
| 557 | pub(crate) async fn landing_state(&self, pull_id: &str) -> Result<(bool, Option<String>)> { | |
| 558 | let stalled = self.land_row(pull_id).await?.and_then(|row| row.stalled); | |
| 559 | Ok((self.land_request(pull_id).await?.is_some(), stalled)) | |
| 560 | } | |
| 561 | ||
| 562 | /// The merge waiting on a pull request and why g1t stopped, as read | |
| 563 | /// for this request (prefetch.rs) or now. | |
| 564 | async fn land_row(&self, pull_id: &str) -> Result<Option<LandRow>> { | |
| 565 | if let Some(found) = self.prefetched_pull(pull_id) { | |
| 566 | return found.first::<LandRow>(Slot::Pull); | |
| 567 | } | |
| 568 | self.db | |
| 569 | .prepare("SELECT land_requested, land_requested_at, stalled FROM pulls WHERE id = ?") | |
| 570 | .bind(&[pull_id.into()])? | |
| 571 | .first::<LandRow>(None) | |
| 572 | .await | |
| 573 | } | |
| 574 | ||
| The work, events, webhooks, actions, repos and packages services no longer run g1t's own agents. Work drops the 35 calls that queued issues for g1t, delegated them, messaged and reviewed through the agent, planned, captured memories automatically, rated confidence and capped agent spend, and its pull request lifecycle no longer revises or reviews: a pull request only advances to a merge by the repository's auto-merge setting. Merging a pull request that is behind now catches it up itself, through the repository's branch update, and lands it when the push arrives; when it can't be caught up cleanly the merge says to merge the base in and try again. Recording a session on a pull request, memory recall and remember, overlaps, the merge queue and required checks stay as they were. | 575 | /// Forgets a merge that was waiting on a pull request, and the step |
| 576 | /// that was bringing it up to date. | |
| 577 | pub(crate) async fn forget_landing(&self, pull_id: &str) -> Result<()> { | |
| Project settings say who created the project, by name | 578 | self.db |
| 579 | .prepare( | |
| The work, events, webhooks, actions, repos and packages services no longer run g1t's own agents. Work drops the 35 calls that queued issues for g1t, delegated them, messaged and reviewed through the agent, planned, captured memories automatically, rated confidence and capped agent spend, and its pull request lifecycle no longer revises or reviews: a pull request only advances to a merge by the repository's auto-merge setting. Merging a pull request that is behind now catches it up itself, through the repository's branch update, and lands it when the push arrives; when it can't be caught up cleanly the merge says to merge the base in and try again. Recording a session on a pull request, memory recall and remember, overlaps, the merge queue and required checks stay as they were. | 580 | "UPDATE pulls SET land_requested = NULL, land_requested_at = NULL, |
| 581 | working_on = CASE WHEN working_on = 'catch_up' THEN NULL ELSE working_on END, | |
| 582 | working_until = CASE WHEN working_on = 'catch_up' THEN NULL ELSE working_until END | |
| 583 | WHERE id = ?", | |
| Project settings say who created the project, by name | 584 | ) |
| 585 | .bind(&[pull_id.into()])? | |
| 586 | .run() | |
| 587 | .await?; | |
| 588 | Ok(()) | |
| 589 | } | |
| 590 | ||
| 591 | /// Lands a pull request whose head has just moved, if a merge of it was | |
| 592 | /// waiting for exactly that. What it was caught up to was already | |
| 593 | /// checked and reviewed apart from the merge, so the checks are not | |
| 594 | /// waited for again; a repository that wants them rerun turns on | |
| 595 | /// "require up to date", and then nothing is landed this way. | |
| 596 | pub(crate) async fn land_if_requested(&self, pull_id: &str) -> Result<()> { | |
| 597 | let Some(request) = self.land_request(pull_id).await? else { | |
| 598 | return Ok(()); | |
| 599 | }; | |
| 600 | self.forget_landing(pull_id).await?; | |
| 601 | let Some(pull) = self.pull_by_id(pull_id).await? else { | |
| 602 | return Ok(()); | |
| 603 | }; | |
| 604 | let repo: Outcome<Repo> = g1t_kit::call( | |
| 605 | &self.repos, | |
| 606 | "get_by_id", | |
| 607 | &GetByIdArgs { | |
| 608 | id: pull.repo_id.clone(), | |
| 609 | viewer: Some(request.actor.clone()), | |
| 610 | }, | |
| 611 | ) | |
| 612 | .await?; | |
| 613 | let Outcome::Ok(repo) = crate::retired::unless_archived(repo) else { | |
| 614 | return Ok(()); | |
| 615 | }; | |
| 616 | let merged = self | |
| 617 | .merge_pull(PullActionArgs { | |
| 618 | actor: request.actor, | |
| 619 | repo: RepoPath { | |
| 620 | namespace: repo.namespace, | |
| 621 | name: repo.name, | |
| 622 | }, | |
| 623 | number: pull.number, | |
| 624 | summary: String::new(), | |
| 625 | keep_issue_open: request.keep_issue_open, | |
| 626 | ignore_checks: true, | |
| Merge rulesets: branch and tag rules, agent-first, enforced on push and merge | 627 | bypass_rules: false, |
| Project settings say who created the project, by name | 628 | }) |
| 629 | .await?; | |
| 630 | if let Outcome::Fail(failure) = merged { | |
| 631 | self.stall(StallArgs { | |
| 632 | pull_id: pull.id, | |
| 633 | reason: format!( | |
| 634 | "It was brought up to date but could not be merged: {}", | |
| 635 | failure.message | |
| 636 | ), | |
| Events: review requests, assignments, stops and deployments are published | 637 | by: None, |
| Project settings say who created the project, by name | 638 | }) |
| 639 | .await?; | |
| 640 | } | |
| 641 | Ok(()) | |
| 642 | } | |
| 643 | ||
| Events: review requests, assignments, stops and deployments are published | 644 | /// Stops seeing a pull request through until a person steps in. The |
| 645 | /// first stop is published (`pull.stalled`), which tells its people | |
| 646 | /// that it needs them; a stop on one already stopped only says why. | |
| Project settings say who created the project, by name | 647 | pub(crate) async fn stall(&self, a: StallArgs) -> Result<bool> { |
| Events: review requests, assignments, stops and deployments are published | 648 | let before = self.pull_by_id(&a.pull_id).await?; |
| 649 | let was_stalled = self.is_stalled(&a.pull_id).await?; | |
| Project settings say who created the project, by name | 650 | self.db |
| 651 | .prepare( | |
| 652 | "UPDATE pulls | |
| 653 | SET stalled = ?, working_on = NULL, working_until = NULL, | |
| 654 | land_requested = NULL, land_requested_at = NULL | |
| 655 | WHERE id = ? AND status = 'open'", | |
| 656 | ) | |
| 657 | .bind(&[a.reason.trim().into(), a.pull_id.as_str().into()])? | |
| 658 | .run() | |
| 659 | .await?; | |
| 660 | self.db | |
| 661 | .prepare("UPDATE pulls SET stage = 'needs_you', stage_detail = ? WHERE id = ?") | |
| Events: review requests, assignments, stops and deployments are published | 662 | .bind(&[a.reason.trim().into(), a.pull_id.as_str().into()])? |
| Project settings say who created the project, by name | 663 | .run() |
| 664 | .await?; | |
| Events: review requests, assignments, stops and deployments are published | 665 | if let Some(pull) = before.filter(|pull| !was_stalled && pull.status == PullStatus::Open) { |
| 666 | self.publish_as( | |
| 667 | "pull.stalled", | |
| 668 | &pull.repo_id, | |
| 669 | a.by.clone(), | |
| 670 | g1t_contracts::events::PullEvent { | |
| 671 | detail: Some(a.reason.trim().to_owned()), | |
| 672 | ..Self::pull_event(&pull) | |
| 673 | }, | |
| 674 | ) | |
| 675 | .await?; | |
| 676 | } | |
| Project settings say who created the project, by name | 677 | Ok(true) |
| 678 | } | |
| 679 | ||
| Events: review requests, assignments, stops and deployments are published | 680 | /// Whether g1t has stopped seeing the pull request through. |
| 681 | pub(crate) async fn is_stalled(&self, pull_id: &str) -> Result<bool> { | |
| 682 | Ok(self | |
| 683 | .db | |
| 684 | .prepare("SELECT 1 AS value FROM pulls WHERE id = ? AND stalled IS NOT NULL") | |
| 685 | .bind(&[pull_id.into()])? | |
| 686 | .first::<u32>(Some("value")) | |
| 687 | .await? | |
| 688 | .is_some()) | |
| 689 | } | |
| 690 | ||
| 691 | /// Says that a pull request g1t had stopped on is going again, which | |
| 692 | /// closes what it was waiting on a person for. | |
| 693 | pub(crate) async fn announce_resumed(&self, pull_id: &str, actor: Option<String>) -> Result<()> { | |
| 694 | if let Some(pull) = self.pull_by_id(pull_id).await? { | |
| 695 | self.publish_as("pull.resumed", &pull.repo_id, actor, Self::pull_event(&pull)).await?; | |
| 696 | } | |
| 697 | Ok(()) | |
| 698 | } | |
| 699 | ||
| Project settings say who created the project, by name | 700 | pub(crate) async fn managed_pulls(&self, a: ManagedPullsArgs) -> Result<Vec<String>> { |
| 701 | let rows = self | |
| 702 | .db | |
| 703 | .prepare( | |
| 704 | "SELECT id AS value FROM pulls | |
| 705 | WHERE status = 'open' AND managed = 1 AND stalled IS NULL | |
| 706 | AND (?1 IS NULL OR repo_id = ?1) | |
| 707 | ORDER BY updated_at DESC LIMIT ?2", | |
| 708 | ) | |
| 709 | .bind(&[ | |
| 710 | a.repo_id.map_or(JsValue::NULL, JsValue::from), | |
| 711 | MANAGED_PAGE.into(), | |
| 712 | ])? | |
| 713 | .all() | |
| 714 | .await? | |
| 715 | .results::<ValueRow>()?; | |
| 716 | Ok(rows.into_iter().map(|row| row.value).collect()) | |
| 717 | } | |
| 718 | } | |
| 719 | ||
| 720 | #[cfg(test)] | |
| 721 | mod tests { | |
| 722 | use super::*; | |
| 723 | ||
| 724 | #[test] | |
| 725 | fn a_verdict_from_whoever_asked_for_g1t_s_change_is_not_someone_else_s() { | |
| 726 | use crate::rows::stored::{ASKER, G1T, pull}; | |
| 727 | let made = pull(G1T, Some(ASKER)); | |
| 728 | assert!(made_by_g1t(&made)); | |
| 729 | assert!(!from_someone_else(&made, ASKER.0)); | |
| 730 | assert!(!from_someone_else(&made, AGENT_ID)); | |
| 731 | assert!(from_someone_else(&made, "usr_reviewer")); | |
| 732 | let own = pull(ASKER, None); | |
| 733 | assert!(!from_someone_else(&own, ASKER.0)); | |
| 734 | assert!(from_someone_else(&own, "usr_reviewer")); | |
| 735 | } | |
| 736 | ||
| 737 | /// Checks on a head commit: `(context, state)` statuses, against the | |
| 738 | /// required check names. | |
| 739 | fn checks(statuses: &[(&str, &str)], required: &[&str]) -> WorkflowFacts { | |
| 740 | let statuses: Vec<CommitStatus> = statuses | |
| 741 | .iter() | |
| 742 | .map(|(context, state)| CommitStatus { | |
| 743 | context: (*context).to_owned(), | |
| 744 | state: (*state).to_owned(), | |
| 745 | description: None, | |
| 746 | target_url: None, | |
| 747 | updated_at: String::new(), | |
| Merge rulesets: branch and tag rules, agent-first, enforced on push and merge | 748 | source: None, |
| Merge checks: statuses and check runs on every commit | 749 | check_run_id: None, |
| Project settings say who created the project, by name | 750 | }) |
| 751 | .collect(); | |
| 752 | let required: Vec<String> = required.iter().map(|name| (*name).to_owned()).collect(); | |
| 753 | WorkflowFacts::of(&statuses, &required) | |
| 754 | } | |
| 755 | ||
| 756 | /// A pull request that is ready for review, whose required check | |
| 757 | /// passed, and nothing else yet. | |
| 758 | fn facts() -> Facts { | |
| 759 | Facts { | |
| 760 | draft: false, | |
| 761 | stalled: None, | |
| 762 | working_on: None, | |
| 763 | check_status: None, | |
| 764 | revisions: 0, | |
| 765 | behind: false, | |
| 766 | conflicting: false, | |
| 767 | auto_merge: false, | |
| 768 | require_up_to_date: false, | |
| 769 | approvals_missing: None, | |
| 770 | person_request: None, | |
| 771 | queued: None, | |
| 772 | workflows: checks(&[("CI / pull_request", "success")], &["CI"]), | |
| 773 | } | |
| 774 | } | |
| 775 | ||
| 776 | /// The stage, and a word for the step to take. | |
| 777 | fn outcome(facts: Facts) -> (Stage, &'static str) { | |
| 778 | let (lifecycle, next) = decide(facts); | |
| 779 | let step = match next { | |
| 780 | Next::Wait => "wait", | |
| 781 | Next::Merge => "merge", | |
| 782 | }; | |
| 783 | (lifecycle.stage, step) | |
| 784 | } | |
| 785 | ||
| 786 | #[test] | |
| 787 | fn a_ready_change_lands_by_itself_only_where_the_repository_says_so() { | |
| The work, events, webhooks, actions, repos and packages services no longer run g1t's own agents. Work drops the 35 calls that queued issues for g1t, delegated them, messaged and reviewed through the agent, planned, captured memories automatically, rated confidence and capped agent spend, and its pull request lifecycle no longer revises or reviews: a pull request only advances to a merge by the repository's auto-merge setting. Merging a pull request that is behind now catches it up itself, through the repository's branch update, and lands it when the push arrives; when it can't be caught up cleanly the merge says to merge the base in and try again. Recording a session on a pull request, memory recall and remember, overlaps, the merge queue and required checks stay as they were. | 788 | assert_eq!(outcome(facts()), (Stage::Ready, "wait")); |
| 789 | assert_eq!(outcome(Facts { auto_merge: true, ..facts() }), (Stage::Ready, "merge")); | |
| Project settings say who created the project, by name | 790 | // One that is behind is merged too: merging brings it up to date. |
| The work, events, webhooks, actions, repos and packages services no longer run g1t's own agents. Work drops the 35 calls that queued issues for g1t, delegated them, messaged and reviewed through the agent, planned, captured memories automatically, rated confidence and capped agent spend, and its pull request lifecycle no longer revises or reviews: a pull request only advances to a merge by the repository's auto-merge setting. Merging a pull request that is behind now catches it up itself, through the repository's branch update, and lands it when the push arrives; when it can't be caught up cleanly the merge says to merge the base in and try again. Recording a session on a pull request, memory recall and remember, overlaps, the merge queue and required checks stay as they were. | 791 | assert_eq!(outcome(Facts { auto_merge: true, behind: true, ..facts() }), (Stage::Ready, "merge")); |
| 792 | // Unless the repository wants it caught up first. | |
| 793 | let strict = Facts { auto_merge: true, behind: true, require_up_to_date: true, ..facts() }; | |
| 794 | assert_eq!(outcome(strict), (Stage::NeedsYou, "wait")); | |
| Project settings say who created the project, by name | 795 | } |
| 796 | ||
| 797 | #[test] | |
| The work, events, webhooks, actions, repos and packages services no longer run g1t's own agents. Work drops the 35 calls that queued issues for g1t, delegated them, messaged and reviewed through the agent, planned, captured memories automatically, rated confidence and capped agent spend, and its pull request lifecycle no longer revises or reviews: a pull request only advances to a merge by the repository's auto-merge setting. Merging a pull request that is behind now catches it up itself, through the repository's branch update, and lands it when the push arrives; when it can't be caught up cleanly the merge says to merge the base in and try again. Recording a session on a pull request, memory recall and remember, overlaps, the merge queue and required checks stay as they were. | 798 | fn approvals_the_repository_wants_are_waited_for() { |
| 799 | let short = Facts { | |
| 800 | approvals_missing: Some("This repository requires 1 approving review.".to_owned()), | |
| Project settings say who created the project, by name | 801 | auto_merge: true, |
| 802 | ..facts() | |
| 803 | }; | |
| The work, events, webhooks, actions, repos and packages services no longer run g1t's own agents. Work drops the 35 calls that queued issues for g1t, delegated them, messaged and reviewed through the agent, planned, captured memories automatically, rated confidence and capped agent spend, and its pull request lifecycle no longer revises or reviews: a pull request only advances to a merge by the repository's auto-merge setting. Merging a pull request that is behind now catches it up itself, through the repository's branch update, and lands it when the push arrives; when it can't be caught up cleanly the merge says to merge the base in and try again. Recording a session on a pull request, memory recall and remember, overlaps, the merge queue and required checks stay as they were. | 804 | let (lifecycle, next) = decide(short); |
| 805 | assert_eq!(lifecycle.stage, Stage::NeedsYou); | |
| 806 | assert_eq!(lifecycle.detail, "This repository requires 1 approving review."); | |
| 807 | assert!(matches!(next, Next::Wait)); | |
| Project settings say who created the project, by name | 808 | } |
| 809 | ||
| 810 | #[test] | |
| The work, events, webhooks, actions, repos and packages services no longer run g1t's own agents. Work drops the 35 calls that queued issues for g1t, delegated them, messaged and reviewed through the agent, planned, captured memories automatically, rated confidence and capped agent spend, and its pull request lifecycle no longer revises or reviews: a pull request only advances to a merge by the repository's auto-merge setting. Merging a pull request that is behind now catches it up itself, through the repository's branch update, and lands it when the push arrives; when it can't be caught up cleanly the merge says to merge the base in and try again. Recording a session on a pull request, memory recall and remember, overlaps, the merge queue and required checks stay as they were. | 811 | fn checks_are_waited_for_and_a_failing_required_one_needs_a_person() { |
| 812 | let running = Facts { workflows: checks(&[("CI / pull_request", "pending")], &["CI"]), auto_merge: true, ..facts() }; | |
| 813 | let (lifecycle, next) = decide(running); | |
| 814 | assert_eq!(lifecycle.stage, Stage::Checking); | |
| 815 | assert!(lifecycle.detail.contains("Waiting for CI / pull_request")); | |
| 816 | assert!(matches!(next, Next::Wait)); | |
| 817 | let failed = Facts { workflows: checks(&[("CI / pull_request", "failure")], &["CI"]), ..facts() }; | |
| 818 | let (lifecycle, _) = decide(failed); | |
| 819 | assert_eq!(lifecycle.stage, Stage::NeedsYou); | |
| 820 | assert_eq!(lifecycle.detail, "The required check CI fails."); | |
| 821 | // A check the branch does not require does not hold it. | |
| 822 | let lint = checks(&[("CI / pull_request", "success"), ("Lint / pull_request", "failure")], &["CI"]); | |
| 823 | assert_eq!(outcome(Facts { workflows: lint, ..facts() }), (Stage::Ready, "wait")); | |
| 824 | let missing = Facts { workflows: checks(&[("CI / pull_request", "success")], &["CI", "Deploy"]), auto_merge: true, ..facts() }; | |
| 825 | let (lifecycle, next) = decide(missing); | |
| 826 | assert_eq!(lifecycle.detail, "Waiting for the required check Deploy to report on its latest commit."); | |
| 827 | assert!(matches!(next, Next::Wait), "auto-merge must not land it"); | |
| Project settings say who created the project, by name | 828 | } |
| 829 | ||
| 830 | #[test] | |
| The work, events, webhooks, actions, repos and packages services no longer run g1t's own agents. Work drops the 35 calls that queued issues for g1t, delegated them, messaged and reviewed through the agent, planned, captured memories automatically, rated confidence and capped agent spend, and its pull request lifecycle no longer revises or reviews: a pull request only advances to a merge by the repository's auto-merge setting. Merging a pull request that is behind now catches it up itself, through the repository's branch update, and lands it when the push arrives; when it can't be caught up cleanly the merge says to merge the base in and try again. Recording a session on a pull request, memory recall and remember, overlaps, the merge queue and required checks stay as they were. | 831 | fn the_merge_queue_conflicts_and_people_hold_it() { |
| 832 | let queued = Facts { queued: Some((QueueState::Testing, vec![12, 14])), auto_merge: true, ..facts() }; | |
| 833 | let (lifecycle, _) = decide(queued); | |
| 834 | assert_eq!(lifecycle.stage, Stage::Queued); | |
| 835 | assert!(lifecycle.detail.contains("#12, #14")); | |
| 836 | assert_eq!(outcome(Facts { check_status: Some(CheckStatus::Failed), auto_merge: true, ..facts() }), (Stage::NeedsYou, "wait")); | |
| 837 | assert_eq!(outcome(Facts { check_status: Some(CheckStatus::Errored), ..facts() }), (Stage::NeedsYou, "wait")); | |
| 838 | assert_eq!(outcome(Facts { conflicting: true, auto_merge: true, ..facts() }), (Stage::NeedsYou, "wait")); | |
| 839 | let asked = Facts { person_request: Some("ana".to_owned()), auto_merge: true, ..facts() }; | |
| 840 | let (lifecycle, next) = decide(asked); | |
| 841 | assert_eq!(lifecycle.detail, "ana asked for changes."); | |
| 842 | assert!(matches!(next, Next::Wait)); | |
| Project settings say who created the project, by name | 843 | } |
| 844 | ||
| 845 | #[test] | |
| The work, events, webhooks, actions, repos and packages services no longer run g1t's own agents. Work drops the 35 calls that queued issues for g1t, delegated them, messaged and reviewed through the agent, planned, captured memories automatically, rated confidence and capped agent spend, and its pull request lifecycle no longer revises or reviews: a pull request only advances to a merge by the repository's auto-merge setting. Merging a pull request that is behind now catches it up itself, through the repository's branch update, and lands it when the push arrives; when it can't be caught up cleanly the merge says to merge the base in and try again. Recording a session on a pull request, memory recall and remember, overlaps, the merge queue and required checks stay as they were. | 846 | fn drafts_steps_under_way_and_stops_wait() { |
| 847 | assert_eq!(outcome(Facts { draft: true, ..facts() }), (Stage::Working, "wait")); | |
| 848 | assert_eq!(outcome(Facts { working_on: Some("catch_up".into()), auto_merge: true, ..facts() }), (Stage::CatchingUp, "wait")); | |
| 849 | assert_eq!(outcome(Facts { working_on: Some("merge".into()), auto_merge: true, ..facts() }), (Stage::Ready, "wait")); | |
| 850 | let (lifecycle, next) = decide(Facts { stalled: Some("It could not be merged.".to_owned()), auto_merge: true, ..facts() }); | |
| Project settings say who created the project, by name | 851 | assert_eq!(lifecycle.stage, Stage::NeedsYou); |
| The work, events, webhooks, actions, repos and packages services no longer run g1t's own agents. Work drops the 35 calls that queued issues for g1t, delegated them, messaged and reviewed through the agent, planned, captured memories automatically, rated confidence and capped agent spend, and its pull request lifecycle no longer revises or reviews: a pull request only advances to a merge by the repository's auto-merge setting. Merging a pull request that is behind now catches it up itself, through the repository's branch update, and lands it when the push arrives; when it can't be caught up cleanly the merge says to merge the base in and try again. Recording a session on a pull request, memory recall and remember, overlaps, the merge queue and required checks stay as they were. | 852 | assert_eq!(lifecycle.detail, "It could not be merged."); |
| Project settings say who created the project, by name | 853 | assert!(matches!(next, Next::Wait)); |
| 854 | } | |
| 855 | } |
This file's history is long; its oldest lines are credited to the oldest commit read.