Skip to content

g1t/crates/contracts/src/work.rs

2,678 lines95,255 bytesCodeBlame

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.

Issues and pull requests replace intents and attempts1//! The work service: issues, pull requests, comments and sessions.
Work service in Rust, with RFC 3339 timestamps2//!
3//! Each `*Args` struct is the argument of the method of the same name,
4//! served at `POST /rpc/<method>`.
Issues and pull requests replace intents and attempts5//!
6//! Issues and pull requests share one sequence of numbers per repository,
7//! so `#12` names exactly one of them.
Work service in Rust, with RFC 3339 timestamps8
9use serde::{Deserialize, Serialize};
10
Agents as a team: lifecycle, merge queue, billing and a new shell11use crate::repos::{CompareArgs, RepoPath};
Work service in Rust, with RFC 3339 timestamps12use crate::{User, Viewer};
13
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar14/// The labels a new repository starts with, and that "Add the default
15/// labels" adds to one that is missing some: `(name, color, description)`.
16/// Colors are six hex digits, without `#`.
17pub const DEFAULT_LABELS: [(&str, &str, &str); 11] = [
18 ("bug", "d73a4a", "Something isn't working"),
19 ("documentation", "0075ca", "Improvements or additions to documentation"),
20 ("duplicate", "cfd3d7", "This issue or pull request already exists"),
21 ("enhancement", "a2eeef", "New feature or request"),
22 ("good first issue", "7057ff", "Good for newcomers"),
23 ("help wanted", "008672", "Extra attention is needed"),
24 ("invalid", "e4e669", "This doesn't seem right"),
25 ("question", "d876e3", "Further information is requested"),
26 ("wontfix", "ffffff", "This will not be worked on"),
27 ("dependencies", "0366d6", "Updates a dependency"),
28 ("security", "ee0701", "A security fix or a vulnerability"),
29];
30
31/// The color a label gets when none is given: chosen from its name, so
32/// the same name always gets the same color.
33pub fn label_color_for(name: &str) -> String {
34 const PALETTE: [&str; 12] = [
35 "b60205", "d93f0b", "fbca04", "0e8a16", "006b75", "1d76db", "0052cc", "5319e7", "e99695", "f9d0c4",
36 "c2e0c6", "bfdadc",
37 ];
38 if let Some((_, color, _)) = DEFAULT_LABELS.iter().find(|(known, _, _)| *known == name) {
39 return (*color).to_owned();
40 }
41 let hash = name.bytes().fold(0u32, |hash, byte| hash.wrapping_mul(31).wrapping_add(u32::from(byte)));
42 PALETTE[(hash as usize) % PALETTE.len()].to_owned()
43}
44
45/// A color as six lowercase hex digits, from `#A1B2C3`, `a1b2c3` or `abc`.
46pub fn tidy_color(color: &str) -> Option<String> {
47 let hex = color.trim().trim_start_matches('#').to_ascii_lowercase();
48 if !hex.chars().all(|c| c.is_ascii_hexdigit()) {
49 return None;
50 }
51 match hex.len() {
52 6 => Some(hex),
53 3 => Some(hex.chars().flat_map(|c| [c, c]).collect()),
54 _ => None,
55 }
56}
57
58/// The most labels one issue or pull request can carry, and the longest
59/// name and description of one.
60pub const MAX_LABELS: usize = 20;
61pub const MAX_LABEL_CHARS: usize = 50;
62pub const MAX_LABEL_DESCRIPTION_CHARS: usize = 100;
Issues and pull requests replace intents and attempts63
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar64/// A label of a repository: a name, a color and what it means. Issues and
65/// pull requests carry labels by name. Names are lowercase and unique in
66/// a repository.
67#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
68#[serde(rename_all = "camelCase")]
69pub struct Label {
70 pub name: String,
71 /// Six hex digits, without `#`.
72 pub color: String,
73 #[serde(default)]
74 pub description: String,
75 /// How many issues carry it, open or closed.
76 #[serde(default)]
77 pub issues: u32,
78 /// How many pull requests carry it, in any state.
79 #[serde(default)]
80 pub pulls: u32,
81}
82
83/// A milestone, as an issue or pull request names it.
84#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
85#[serde(rename_all = "camelCase")]
86pub struct MilestoneRef {
87 pub number: u32,
88 pub title: String,
89}
90
91/// A milestone: a goal, with an optional due date, that issues and pull
92/// requests are gathered under. Its progress is how many of them are
93/// closed (a merged pull request counts as closed).
94#[derive(Clone, Debug, Serialize, Deserialize)]
95#[serde(rename_all = "camelCase")]
96pub struct Milestone {
97 /// Numbered from 1 in each repository, apart from issues.
98 pub number: u32,
99 pub title: String,
100 /// Markdown.
101 #[serde(default)]
102 pub description: String,
103 /// The day it is due, `YYYY-MM-DD`.
104 #[serde(default)]
105 pub due_on: Option<String>,
106 pub state: State,
107 /// Open issues and pull requests in it (drafts count as open).
108 #[serde(default)]
109 pub open_items: u32,
110 /// Closed issues, and merged or closed pull requests, in it.
111 #[serde(default)]
112 pub closed_items: u32,
113 /// RFC 3339.
114 pub created_at: String,
115 /// RFC 3339.
116 pub updated_at: String,
117 /// RFC 3339.
118 #[serde(default)]
119 pub closed_at: Option<String>,
120}
121
122impl Milestone {
123 /// How far along it is, from 0 to 100: closed items over all of them.
124 pub fn percent(&self) -> u32 {
125 let total = self.open_items + self.closed_items;
126 (self.closed_items * 100).checked_div(total).unwrap_or(0)
127 }
128}
129
130/// The most characters a milestone's title and description may have.
131pub const MAX_MILESTONE_TITLE_CHARS: usize = 100;
132pub const MAX_MILESTONE_DESCRIPTION_CHARS: usize = 4000;
133
134/// A due date as `YYYY-MM-DD`, from that or from an RFC 3339 time.
135pub fn tidy_due_on(value: &str) -> Option<String> {
136 let day = value.trim().get(..10)?;
137 let bytes = day.as_bytes();
138 let digits = |range: std::ops::Range<usize>| bytes[range].iter().all(u8::is_ascii_digit);
139 if !(digits(0..4) && bytes[4] == b'-' && digits(5..7) && bytes[7] == b'-' && digits(8..10)) {
140 return None;
141 }
142 let month: u32 = day[5..7].parse().ok()?;
143 let date: u32 = day[8..10].parse().ok()?;
144 ((1..=12).contains(&month) && (1..=31).contains(&date)).then(|| day.to_owned())
145}
146
147/// `list_labels`: a repository's labels, by name, each with how many
148/// issues and pull requests carry it. Takes `ViewArgs`; returns
149/// `Outcome<Vec<Label>>`.
150///
151/// `save_label`: creates a label, or with `name` changes one (renaming it
152/// renames it on every issue and pull request). Needs the Triage role.
153/// Returns `Outcome<Label>`.
154#[derive(Debug, Serialize, Deserialize)]
155#[serde(rename_all = "camelCase")]
156pub struct SaveLabelArgs {
157 pub actor: User,
158 pub repo: RepoPath,
159 /// The label to change; absent to create one.
160 #[serde(default)]
161 pub name: Option<String>,
162 /// The name it should have: required to create one.
163 #[serde(default)]
164 pub new_name: Option<String>,
165 /// Six hex digits; one is chosen from the name when creating without.
166 #[serde(default)]
167 pub color: Option<String>,
168 #[serde(default)]
169 pub description: Option<String>,
170}
171
172/// `delete_label`: removes a label from the repository and from every
173/// issue and pull request that carries it. Needs the Triage role. Returns
174/// `Outcome<bool>`.
175#[derive(Debug, Serialize, Deserialize)]
176pub struct DeleteLabelArgs {
177 pub actor: User,
178 pub repo: RepoPath,
179 pub name: String,
180}
181
182/// `add_default_labels`: adds those of [`DEFAULT_LABELS`] the repository
183/// does not have yet. Needs the Triage role. Returns
184/// `Outcome<Vec<Label>>`, every label it has now.
185#[derive(Debug, Serialize, Deserialize)]
186pub struct RepoActorArgs {
187 pub actor: User,
188 pub repo: RepoPath,
189}
190
191/// How `set_labels` changes an issue's or pull request's labels.
192#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
193#[serde(rename_all = "lowercase")]
194pub enum LabelChange {
195 /// Replace them all.
196 #[default]
197 Set,
198 /// Add these to the ones it has.
199 Add,
200 /// Take these off.
201 Remove,
202}
203
204/// `set_labels`: changes the labels of an issue or a pull request (they
205/// share numbers). A label the repository does not have yet is created
206/// when the actor has the Triage role; anyone else may only use existing
207/// ones, on what they opened. Returns `Outcome<Vec<String>>`, its labels
208/// now.
209#[derive(Debug, Serialize, Deserialize)]
210#[serde(rename_all = "camelCase")]
211pub struct SetLabelsArgs {
212 pub actor: User,
213 pub repo: RepoPath,
214 pub number: u32,
215 pub labels: Vec<String>,
216 #[serde(default)]
217 pub change: LabelChange,
218}
219
220/// `list_milestones`: a repository's milestones, open ones by due date
221/// (soonest first, those without one last), then closed ones most recently
222/// closed first. Returns `Outcome<Vec<Milestone>>`.
223#[derive(Debug, Serialize, Deserialize)]
224pub struct ListMilestonesArgs {
225 pub repo: RepoPath,
226 pub viewer: Viewer,
227 /// Both when absent.
228 #[serde(default)]
229 pub state: Option<State>,
230}
231
232/// `get_milestone`: one milestone and what is in it. Takes `ViewArgs`,
233/// whose `number` is the milestone's. Returns `Outcome<MilestoneDetail>`.
234#[derive(Clone, Debug, Serialize, Deserialize)]
235pub struct MilestoneDetail {
236 pub milestone: Milestone,
237 /// Newest first, open and closed.
238 pub issues: Vec<Issue>,
239 /// Newest first, in any state.
240 pub pulls: Vec<Pull>,
241}
242
243/// `save_milestone`: creates a milestone, or with `number` changes the
244/// fields given. Needs the Triage role. Returns `Outcome<Milestone>`.
245#[derive(Debug, Serialize, Deserialize)]
246#[serde(rename_all = "camelCase")]
247pub struct SaveMilestoneArgs {
248 pub actor: User,
249 pub repo: RepoPath,
250 /// The milestone to change; absent to create one.
251 #[serde(default)]
252 pub number: Option<u32>,
253 /// Required to create one.
254 #[serde(default)]
255 pub title: Option<String>,
256 #[serde(default)]
257 pub description: Option<String>,
258 /// `YYYY-MM-DD`; an empty string clears it.
259 #[serde(default)]
260 pub due_on: Option<String>,
261 #[serde(default)]
262 pub state: Option<State>,
263}
264
265/// `delete_milestone`: removes a milestone; what was in it is in none.
266/// Needs the Triage role. Returns `Outcome<bool>`.
267#[derive(Debug, Serialize, Deserialize)]
268pub struct DeleteMilestoneArgs {
269 pub actor: User,
270 pub repo: RepoPath,
271 pub number: u32,
272}
273
Issues and pull requests replace intents and attempts274/// `open` or `closed`: the filter on lists of issues and pull requests. An
275/// open pull request is a draft or one ready for review; a closed one was
276/// merged or closed without merging.
Work service in Rust, with RFC 3339 timestamps277#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
278#[serde(rename_all = "lowercase")]
Issues and pull requests replace intents and attempts279pub enum State {
Work service in Rust, with RFC 3339 timestamps280 Open,
Issues and pull requests replace intents and attempts281 Closed,
282}
283
284/// Why an issue was closed.
285#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
286#[serde(rename_all = "snake_case")]
287pub enum IssueReason {
288 /// The work was done. If a pull request did it, `resolved_by` names it.
289 Completed,
290 NotPlanned,
Work service in Rust, with RFC 3339 timestamps291}
292
Issues and pull requests replace intents and attempts293impl IssueReason {
294 pub fn as_str(self) -> &'static str {
295 match self {
296 IssueReason::Completed => "completed",
297 IssueReason::NotPlanned => "not_planned",
298 }
299 }
300}
301
302/// Something that should change in a repository: a bug, a feature, a
303/// question. Opened by a person, an agent or an integration. Pull requests
304/// are made against it; the one that is merged resolves it.
Work service in Rust, with RFC 3339 timestamps305#[derive(Clone, Debug, Serialize, Deserialize)]
306#[serde(rename_all = "camelCase")]
Issues and pull requests replace intents and attempts307pub struct Issue {
Work service in Rust, with RFC 3339 timestamps308 pub id: String,
309 pub repo_id: String,
Issues and pull requests replace intents and attempts310 /// Shown as `#12`.
Work service in Rust, with RFC 3339 timestamps311 pub number: u32,
312 pub title: String,
Issues and pull requests replace intents and attempts313 /// Markdown. Also what an agent is given to work from.
314 pub body: String,
315 pub labels: Vec<String>,
316 pub state: State,
317 /// Set when closed.
318 pub reason: Option<IssueReason>,
319 /// The number of the pull request whose merge closed this issue.
320 pub resolved_by: Option<u32>,
g1t is the stored author of what it opens; the person who asked is requested_by and keeps the author's rights321 /// Who opened it: a person, an integration, or g1t (`kind` `agent`)
322 /// for one its agent filed while at work.
Work service in Rust, with RFC 3339 timestamps323 pub author: User,
g1t is the stored author of what it opens; the person who asked is requested_by and keeps the author's rights324 /// For an issue g1t's agent filed: the person it was working for. They
325 /// may manage it as its author could. See [`owner`](Issue::owner).
326 #[serde(default)]
327 pub requested_by: Option<User>,
Work service in Rust, with RFC 3339 timestamps328 /// RFC 3339.
329 pub created_at: String,
Issues and pull requests replace intents and attempts330 /// RFC 3339.
331 pub updated_at: String,
332 /// RFC 3339.
333 pub closed_at: Option<String>,
334 /// Pull requests made against this issue, in any state.
335 pub pull_count: u32,
336 pub comment_count: u32,
Agents as a team: lifecycle, merge queue, billing and a new shell337 /// Usernames of the people it is assigned to.
338 #[serde(default)]
339 pub assignees: Vec<String>,
340 /// The numbers of the issues that have to be merged before this one is
341 /// worked on.
342 #[serde(default)]
343 pub blocked_by: Vec<u32>,
344 /// Whether a g1t agent takes it as soon as it can: at once, or when
345 /// what it is blocked by has merged.
346 #[serde(default)]
347 pub queued: bool,
348 /// The agent working on it now: the one behind its newest pull request
g1t is one name: its agent's work, commits and comments show as @g1t, and nobody can claim g1t or g1t-agent349 /// that is still in progress in a fork, such as `g1t`.
Agents as a team: lifecycle, merge queue, billing and a new shell350 #[serde(default)]
351 pub agent: Option<String>,
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar352 /// The milestone it is in, if any.
353 #[serde(default)]
354 pub milestone: Option<MilestoneRef>,
Work service in Rust, with RFC 3339 timestamps355}
356
357#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
358#[serde(rename_all = "lowercase")]
Issues and pull requests replace intents and attempts359pub enum PullStatus {
360 /// Still being worked on.
361 Draft,
362 /// Ready for review.
363 Open,
364 Merged,
365 /// Closed without merging.
366 Closed,
Work service in Rust, with RFC 3339 timestamps367}
368
Issues and pull requests replace intents and attempts369impl PullStatus {
Work service in Rust, with RFC 3339 timestamps370 pub fn as_str(self) -> &'static str {
371 match self {
Issues and pull requests replace intents and attempts372 PullStatus::Draft => "draft",
373 PullStatus::Open => "open",
374 PullStatus::Merged => "merged",
375 PullStatus::Closed => "closed",
Work service in Rust, with RFC 3339 timestamps376 }
377 }
378
Issues and pull requests replace intents and attempts379 /// Whether the pull request can still be changed or merged.
Work service in Rust, with RFC 3339 timestamps380 pub fn is_active(self) -> bool {
Issues and pull requests replace intents and attempts381 matches!(self, PullStatus::Draft | PullStatus::Open)
Work service in Rust, with RFC 3339 timestamps382 }
383}
384
385/// Where the agent runs: on g1t's sandboxes, or in someone's own session.
386#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
387#[serde(rename_all = "lowercase")]
Issues and pull requests replace intents and attempts388pub enum Runtime {
Work service in Rust, with RFC 3339 timestamps389 Hosted,
390 External,
391}
392
Pull requests from branches393/// A proposed change. It is made either in a fork created for it, which is
394/// how agents work, or on a branch pushed to the repository itself.
Work service in Rust, with RFC 3339 timestamps395#[derive(Clone, Debug, Serialize, Deserialize)]
396#[serde(rename_all = "camelCase")]
Issues and pull requests replace intents and attempts397pub struct Pull {
Work service in Rust, with RFC 3339 timestamps398 pub id: String,
399 pub repo_id: String,
Issues and pull requests replace intents and attempts400 /// Shown as `#12`.
Work service in Rust, with RFC 3339 timestamps401 pub number: u32,
Issues and pull requests replace intents and attempts402 /// The number of the issue this is for, if any.
403 pub issue: Option<u32>,
404 pub title: String,
405 /// Markdown: what changed and why. Set when marked ready.
406 pub body: Option<String>,
Work service in Rust, with RFC 3339 timestamps407 /// A label for the agent doing the work, e.g. `claude-code`.
408 pub agent: String,
Issues and pull requests replace intents and attempts409 pub runtime: Runtime,
410 pub status: PullStatus,
Pull requests from branches411 /// The fork holding the change, unless it is on a branch.
412 pub fork: Option<RepoPath>,
Work service in Rust, with RFC 3339 timestamps413 /// The fork's repository id.
Pull requests from branches414 pub fork_repo_id: Option<String>,
415 /// The branch of the repository holding the change, unless it is in a
416 /// fork.
417 pub branch: Option<String>,
Work service in Rust, with RFC 3339 timestamps418 pub head_commit: Option<String>,
Issues and pull requests replace intents and attempts419 /// For a merged pull request, what the branch pointed to before the
420 /// merge. Comparing against it shows what the pull request changed.
421 pub merge_base: Option<String>,
422 /// Username of whoever merged it.
423 pub merged_by: Option<String>,
424 /// RFC 3339.
425 pub merged_at: Option<String>,
426 /// Set on a pull request closed because another one for the same issue
427 /// was merged: that one's number.
428 pub superseded_by: Option<u32>,
Fast pages, required checks on the branch, self-hosted runners, honest incidents429 /// `failed` when the merge queue took it out because its combined
430 /// state failed, until its head moves. Its checks are the statuses
431 /// workflows report on its head: see `PullDetail::statuses` and
432 /// `PullDetail::required_checks`.
Acceptance checks in sandboxes, line comments and review verdicts433 pub check_status: Option<CheckStatus>,
Agents as a team: lifecycle, merge queue, billing and a new shell434 /// The files it changes, as of its latest push.
435 #[serde(default)]
436 pub files: Vec<ChangedFile>,
437 /// Usernames of the people it is assigned to.
438 #[serde(default)]
439 pub assignees: Vec<String>,
g1t is one name: its agent's work, commits and comments show as @g1t, and nobody can claim g1t or g1t-agent440 /// Those whose review was asked for: usernames, and `g1t` when a
Agents as a team: lifecycle, merge queue, billing and a new shell441 /// g1t agent was asked.
442 #[serde(default)]
443 pub reviewers: Vec<String>,
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar444 /// Teams whose review was asked for, as `workspace/slug`. A team stays
445 /// here after review assignment picks people from it, who are listed
446 /// in `reviewers`.
447 #[serde(default)]
448 pub team_reviewers: Vec<String>,
449 /// The labels it carries, by name.
450 #[serde(default)]
451 pub labels: Vec<String>,
452 /// The milestone it is in, if any.
453 #[serde(default)]
454 pub milestone: Option<MilestoneRef>,
455 /// The branch it merges into. Stored as null for the repository's
456 /// default branch, so that it follows a change of default; lists and
457 /// `get_pull` fill in the name. See [`base_branch`](Pull::base_branch).
458 #[serde(default)]
459 pub base: Option<String>,
g1t is the stored author of what it opens; the person who asked is requested_by and keeps the author's rights460 /// Who opened it: a person, or g1t (`kind` `agent`, username `g1t`)
461 /// for a change g1t made.
Issues and pull requests replace intents and attempts462 pub author: User,
g1t is the stored author of what it opens; the person who asked is requested_by and keeps the author's rights463 /// For a change g1t made: the person who asked for it, by assigning
464 /// an issue or handing g1t the work. They answer for it as its author
465 /// would. See [`owner`](Pull::owner).
466 #[serde(default)]
467 pub requested_by: Option<User>,
Work service in Rust, with RFC 3339 timestamps468 /// RFC 3339.
469 pub created_at: String,
470 /// RFC 3339.
471 pub updated_at: String,
Thirteen MCP tools and classic token scopes; agents rate their confidence and can be put on an issue in one step472 /// How sure g1t is of a g1t agent's change, from what it can observe,
473 /// once the agent has finished it. Absent before then, and on changes
474 /// g1t is not seeing through.
475 #[serde(default)]
476 pub confidence: Option<Confidence>,
Merge rulesets: branch and tag rules, agent-first, enforced on push and merge477 /// Who last moved its head (a user id), and when, RFC 3339: for rules
478 /// about the most recent push. Absent until a push after rulesets
479 /// arrived.
480 #[serde(default, skip_serializing_if = "Option::is_none")]
481 pub head_pushed_by: Option<String>,
482 #[serde(default, skip_serializing_if = "Option::is_none")]
483 pub head_pushed_at: Option<String>,
Thirteen MCP tools and classic token scopes; agents rate their confidence and can be put on an issue in one step484}
485
486/// How sure g1t is that an agent's change is right. Low is below medium,
487/// which is below high, so the lower of two is their minimum.
488#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
489#[serde(rename_all = "lowercase")]
490pub enum ConfidenceLevel {
491 Low,
492 Medium,
493 High,
Work service in Rust, with RFC 3339 timestamps494}
495
Thirteen MCP tools and classic token scopes; agents rate their confidence and can be put on an issue in one step496impl ConfidenceLevel {
497 pub fn as_str(self) -> &'static str {
498 match self {
499 ConfidenceLevel::Low => "low",
500 ConfidenceLevel::Medium => "medium",
501 ConfidenceLevel::High => "high",
502 }
503 }
504
505 pub fn parse(value: &str) -> Option<ConfidenceLevel> {
506 match value.trim().to_ascii_lowercase().as_str() {
507 "low" => Some(ConfidenceLevel::Low),
508 "medium" => Some(ConfidenceLevel::Medium),
509 "high" => Some(ConfidenceLevel::High),
510 _ => None,
511 }
512 }
513}
514
515/// How sure g1t is of a change an agent made, worked out from what can be
516/// observed: its checks, how often it was sent back, the reviewer agent's
517/// verdict, whether it touched tests, its size, where it reached, how close
518/// it came to its guardrails, and what it asked and was not answered. The
519/// agent may say how sure it is too; what g1t observes can only lower that.
520#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
521#[serde(rename_all = "camelCase")]
522pub struct Confidence {
523 pub level: ConfidenceLevel,
524 /// A few words each, most telling first: what lowered it, or for
525 /// `high`, what it rests on.
526 pub reasons: Vec<String>,
527 /// What the agent said of its own change, if it said.
528 #[serde(default)]
529 pub self_reported: Option<ConfidenceLevel>,
530 /// What the agent said it was unsure about.
531 #[serde(default)]
532 pub uncertain_about: Vec<String>,
533 /// The agent run it was worked out after.
534 #[serde(default)]
535 pub run_id: Option<String>,
536 /// RFC 3339.
537 pub assessed_at: String,
538}
539
Agents as a team: lifecycle, merge queue, billing and a new shell540/// One file a pull request changes, and by how much.
541#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
542pub struct ChangedFile {
543 pub path: String,
544 pub additions: u32,
545 pub deletions: u32,
546}
547
548/// Another pull request in progress that changes some of the same files.
549/// Two for the same issue are alternatives; two for different issues are
550/// heading for a conflict.
551#[derive(Clone, Debug, Serialize, Deserialize)]
552pub struct Overlap {
553 pub number: u32,
554 pub title: String,
555 /// The number of the issue the other pull request is for.
556 pub issue: Option<u32>,
557 /// The files both change.
558 pub paths: Vec<String>,
559}
560
g1t is the stored author of what it opens; the person who asked is requested_by and keeps the author's rights561impl Issue {
562 /// Who the issue is theirs to manage: the person g1t's agent filed it
563 /// for, or its author. They may edit, close and reopen it without the
564 /// Triage role.
565 pub fn owner(&self) -> &User {
566 self.requested_by.as_ref().unwrap_or(&self.author)
567 }
568}
569
Agents as a team: lifecycle, merge queue, billing and a new shell570impl Pull {
g1t is the stored author of what it opens; the person who asked is requested_by and keeps the author's rights571 /// Who the pull request is theirs to answer for: whoever asked g1t to
572 /// make it, or its author. Every rule that once read "its author" reads
573 /// this: they may update, close and steer it, are never asked to review
574 /// it and cannot approve it, see it as theirs, and a sandbox at work on
575 /// it acts as them.
576 pub fn owner(&self) -> &User {
577 self.requested_by.as_ref().unwrap_or(&self.author)
578 }
579
580 /// Whether `id` is its [`owner`](Pull::owner)'s.
581 pub fn is_owned_by(&self, id: &str) -> bool {
582 self.owner().id == id
583 }
584
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar585 /// The branch it merges into: its own base, or the repository's
586 /// default branch when it has none.
587 pub fn base_branch<'a>(&'a self, default_branch: &'a str) -> &'a str {
588 self.base.as_deref().filter(|base| !base.is_empty()).unwrap_or(default_branch)
589 }
590
591 /// Whether it merges into the repository's default branch.
592 pub fn targets_default(&self, default_branch: &str) -> bool {
593 self.base_branch(default_branch) == default_branch
594 }
595
Agents as a team: lifecycle, merge queue, billing and a new shell596 /// What to ask the repos service to see what this pull request changes.
597 ///
598 /// A fork is compared as a whole. A branch is compared by name while
599 /// the pull request is open, and by the commit it was merged or closed
600 /// at afterwards, so later pushes to the branch do not change the record.
601 pub fn comparison(&self, viewer: &Viewer) -> CompareArgs {
602 let settled = matches!(self.status, PullStatus::Merged | PullStatus::Closed);
603 let (repo_id, head) = match &self.fork_repo_id {
604 Some(fork) => (fork.clone(), None),
605 None => (
606 self.repo_id.clone(),
607 self.head_commit
608 .clone()
609 .filter(|_| settled)
610 .or_else(|| self.branch.clone()),
611 ),
612 };
613 CompareArgs {
614 repo_id,
615 viewer: viewer.clone(),
616 base: self.merge_base.clone(),
617 head,
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar618 base_branch: self.base.clone().filter(|base| !base.is_empty()),
Agents as a team: lifecycle, merge queue, billing and a new shell619 }
620 }
621}
622
g1t is the stored author of what it opens; the person who asked is requested_by and keeps the author's rights623/// g1t's agent, as the author of what it opens: stored by
624/// [`AGENT_ID`](crate::identity::AGENT_ID), shown as `g1t`.
625pub fn g1t_author() -> User {
626 User {
627 id: crate::identity::AGENT_ID.to_owned(),
628 username: crate::identity::AGENT_NAME.to_owned(),
629 kind: crate::PrincipalKind::Agent,
630 ..User::default()
631 }
632}
633
634/// Who is recorded as opening an issue or a pull request that `actor`
635/// opens, and who asked for it: `(author, requested_by)`.
636///
637/// - g1t's agent, acting for someone through its token (an issue it files
638/// while at work): g1t, requested by that person.
639/// - A person having g1t make the change (`by_g1t`: a hosted g1t agent in
640/// a fork of its own): g1t, requested by them.
641/// - Anyone else, and g1t's own work that nobody asked for: the actor, and
642/// nobody asking.
643pub fn authorship(actor: &User, by_g1t: bool) -> (User, Option<User>) {
644 let person = |id: &str, username: &str| User {
645 id: id.to_owned(),
646 username: username.to_owned(),
647 kind: crate::PrincipalKind::User,
648 ..User::default()
649 };
650 if actor.kind == crate::PrincipalKind::Agent {
651 let asked = actor
652 .acting
653 .as_ref()
654 .map(|acting| &acting.on_behalf_of)
655 .filter(|on_behalf_of| !crate::system::is_system_id(&on_behalf_of.id))
656 .map(|on_behalf_of| person(&on_behalf_of.id, &on_behalf_of.username));
657 return (g1t_author(), asked);
658 }
659 if by_g1t && actor.kind != crate::PrincipalKind::System {
660 return (g1t_author(), Some(person(&actor.id, &actor.username)));
661 }
662 let author = User {
663 id: actor.id.clone(),
664 username: actor.username.clone(),
665 kind: actor.kind,
666 ..User::default()
667 };
668 (author, None)
669}
670
Acceptance checks in sandboxes, line comments and review verdicts671#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
672#[serde(rename_all = "lowercase")]
673pub enum CheckStatus {
674 /// Waiting for a sandbox.
675 Queued,
676 Running,
677 Passed,
678 Failed,
679 /// The checks could not be run at all.
680 Errored,
681}
682
683impl CheckStatus {
684 pub fn as_str(self) -> &'static str {
685 match self {
686 CheckStatus::Queued => "queued",
687 CheckStatus::Running => "running",
688 CheckStatus::Passed => "passed",
689 CheckStatus::Failed => "failed",
690 CheckStatus::Errored => "errored",
691 }
692 }
693}
694
Fast pages, required checks on the branch, self-hosted runners, honest incidents695/// How one command went. Recorded by earlier runs of commands written on
696/// issues, which g1t no longer runs; kept so their history still reads.
Acceptance checks in sandboxes, line comments and review verdicts697#[derive(Clone, Debug, Serialize, Deserialize)]
698#[serde(rename_all = "camelCase")]
699pub struct CheckResult {
700 pub command: String,
701 pub passed: bool,
702 /// Absent when the command was stopped for taking too long.
703 #[serde(default)]
704 pub exit_code: Option<i32>,
705 /// What the command printed, standard output and error together. The
706 /// end of it, when there was a lot.
707 #[serde(default)]
708 pub output: String,
709 #[serde(default)]
710 pub duration_ms: u64,
711}
712
Fast pages, required checks on the branch, self-hosted runners, honest incidents713/// A record against a pull request's head: the merge queue taking it out,
714/// with why, or an earlier run of commands written on its issue.
Acceptance checks in sandboxes, line comments and review verdicts715#[derive(Clone, Debug, Serialize, Deserialize)]
716#[serde(rename_all = "camelCase")]
717pub struct CheckRun {
718 pub id: String,
719 /// The commit that was checked.
720 pub head_commit: String,
721 pub status: CheckStatus,
722 pub results: Vec<CheckResult>,
723 /// Why the checks could not be run, when `status` is `errored`.
724 pub error: Option<String>,
725 /// RFC 3339.
726 pub created_at: String,
727 /// RFC 3339.
728 pub finished_at: Option<String>,
729}
730
731/// A reviewer's decision on a pull request.
732#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
733#[serde(rename_all = "snake_case")]
734pub enum Verdict {
735 Approve,
736 RequestChanges,
737}
738
739impl Verdict {
740 pub fn as_str(self) -> &'static str {
741 match self {
742 Verdict::Approve => "approve",
743 Verdict::RequestChanges => "request_changes",
744 }
745 }
746}
747
Agents as a team: lifecycle, merge queue, billing and a new shell748/// What an entry in a conversation is.
749#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
750#[serde(rename_all = "lowercase")]
751pub enum CommentKind {
752 /// Something a person or an agent wrote.
753 #[default]
754 Comment,
755 /// Something that happened: an assignment, a review asked for, a close.
756 Event,
757}
758
Acceptance checks in sandboxes, line comments and review verdicts759/// A comment on an issue or a pull request. On a pull request it can sit
760/// on one line of the change, and it can carry a reviewer's verdict.
Issues and pull requests replace intents and attempts761#[derive(Clone, Debug, Serialize, Deserialize)]
762#[serde(rename_all = "camelCase")]
763pub struct Comment {
764 pub id: String,
Agents as a team: lifecycle, merge queue, billing and a new shell765 /// Something a person wrote, or something that happened.
766 #[serde(default)]
767 pub kind: CommentKind,
Issues and pull requests replace intents and attempts768 pub author: User,
Agents as a team: lifecycle, merge queue, billing and a new shell769 /// Markdown. For an event, what its author did, as the rest of a
770 /// sentence that starts with their name: "assigned ana".
Issues and pull requests replace intents and attempts771 pub body: String,
Acceptance checks in sandboxes, line comments and review verdicts772 /// The file commented on, for a comment on a line.
773 pub path: Option<String>,
774 /// The line of that file, as numbered after the change.
775 pub line: Option<u32>,
776 pub verdict: Option<Verdict>,
Issues and pull requests replace intents and attempts777 /// RFC 3339.
778 pub created_at: String,
779}
780
Work service in Rust, with RFC 3339 timestamps781#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
782#[serde(rename_all = "snake_case")]
783pub enum SessionEntryKind {
784 Prompt,
785 Message,
786 ToolCall,
787 ToolResult,
788 Note,
789}
790
Issues and pull requests replace intents and attempts791/// One step of an agent's session: the "why" behind a pull request's commits.
Work service in Rust, with RFC 3339 timestamps792#[derive(Clone, Debug, Serialize, Deserialize)]
793pub struct SessionEntry {
794 pub seq: u32,
795 pub kind: SessionEntryKind,
796 pub text: String,
797 /// For tool calls and results.
798 pub tool: Option<String>,
799 /// The fork's head commit when this entry was recorded, if known.
800 pub commit: Option<String>,
801 /// RFC 3339.
802 pub at: String,
803}
804
805#[derive(Clone, Debug, Serialize, Deserialize)]
806pub struct NewSessionEntry {
807 pub kind: SessionEntryKind,
808 pub text: String,
809 #[serde(default)]
810 pub tool: Option<String>,
811 #[serde(default)]
812 pub commit: Option<String>,
813}
814
815#[derive(Clone, Debug, Serialize, Deserialize)]
Issues and pull requests replace intents and attempts816pub struct IssueDetail {
817 pub issue: Issue,
818 /// Every pull request made against it, oldest first.
819 pub pulls: Vec<Pull>,
820 pub comments: Vec<Comment>,
Work service in Rust, with RFC 3339 timestamps821}
822
823#[derive(Clone, Debug, Serialize, Deserialize)]
Issues and pull requests replace intents and attempts824pub struct PullDetail {
825 pub pull: Pull,
826 /// The issue it is for, if any.
827 pub issue: Option<Issue>,
828 pub comments: Vec<Comment>,
Fast pages, required checks on the branch, self-hosted runners, honest incidents829 /// The latest record against its head: the merge queue taking it out,
830 /// or (from before checks were workflows) a run of its issue's commands.
Acceptance checks in sandboxes, line comments and review verdicts831 pub checks: Option<CheckRun>,
Agents as a team: lifecycle, merge queue, billing and a new shell832 /// Other pull requests in progress that change the same files.
833 #[serde(default)]
834 pub overlaps: Vec<Overlap>,
835 /// Whether the branch it would merge into has moved on without it, so
836 /// that it has to catch up before it can merge.
837 #[serde(default)]
838 pub behind: bool,
839 /// Whether a g1t agent is reviewing it right now.
840 #[serde(default)]
841 pub review_pending: bool,
842 /// Where it stands on its way to being merged, for a pull request g1t
843 /// is seeing through. Absent on anyone else's.
844 #[serde(default)]
845 pub lifecycle: Option<Lifecycle>,
846 /// A merge was asked for while it was behind: g1t is bringing it up to
847 /// date and will then land it.
848 #[serde(default)]
849 pub landing: bool,
850 /// Why g1t stopped working on it, if it did: a catch-up that could not
851 /// be completed, for example.
852 #[serde(default)]
853 pub stalled: Option<String>,
Usage, like a hosting provider's: what agents cost, per day, task, repository and pull request854 /// Messages people sent the agent while it worked, oldest first.
855 #[serde(default)]
856 pub messages: Vec<AgentMessage>,
GitHub Actions on g1t, part two: running workflows857 /// What workflow runs said about its head commit, one per workflow.
858 #[serde(default)]
859 pub statuses: Vec<CommitStatus>,
Agents and memory, checks and conflicts, profiles, slug renames, custom domains860 /// Whether it merges cleanly into the branch it targets, worked out
861 /// ahead of time whenever either side moves.
862 #[serde(default)]
863 pub mergeable: Mergeable,
864 /// When `mergeable` is `conflicting`: the files that conflict.
865 #[serde(default)]
866 pub conflicts: Vec<String>,
Fast pages, required checks on the branch, self-hosted runners, honest incidents867 /// Earlier records like `checks`, newest first, without their output.
Agents and memory, checks and conflicts, profiles, slug renames, custom domains868 #[serde(default)]
869 pub earlier_checks: Vec<CheckRun>,
Fast pages, required checks on the branch, self-hosted runners, honest incidents870 /// The checks the default branch's protection requires, each as it
871 /// stands on the head commit. Empty when none are required.
872 #[serde(default, alias = "requiredChecks")]
873 pub required_checks: Vec<RequiredCheck>,
Merge rulesets: branch and tag rules, agent-first, enforced on push and merge874 /// The rules of the branch it merges into that it does not meet yet,
875 /// for whoever is looking. Absent while it is not open.
876 #[serde(default, skip_serializing_if = "Option::is_none")]
877 pub rules: Option<crate::rules::MergeRules>,
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar878 /// Who owns the files it changes, from the CODEOWNERS file of the
879 /// branch it merges into, and whose approval is still needed. Absent
880 /// when that branch has no CODEOWNERS file.
881 #[serde(default, alias = "codeOwners")]
882 pub code_owners: Option<crate::codeowners::PullCodeOwners>,
Fast pages, required checks on the branch, self-hosted runners, honest incidents883}
884
885/// Where a required check stands on a commit.
886#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
887#[serde(rename_all = "lowercase")]
888pub enum RequiredState {
889 Success,
890 Failure,
891 /// Reported and still running.
892 Pending,
893 /// Nothing has reported it on this commit yet.
894 Expected,
895}
896
897impl RequiredState {
898 pub fn as_str(self) -> &'static str {
899 match self {
900 RequiredState::Success => "success",
901 RequiredState::Failure => "failure",
902 RequiredState::Pending => "pending",
903 RequiredState::Expected => "expected",
904 }
905 }
906}
907
908/// One check a branch's protection requires, as it stands on a commit.
909#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
910#[serde(rename_all = "camelCase")]
911pub struct RequiredCheck {
912 /// The check's name, such as `CI` or `g1t / deploy`.
913 pub name: String,
914 pub state: RequiredState,
915 /// What the status that decided it says, if one did.
916 #[serde(default)]
917 pub description: Option<String>,
918 /// Where to see more: the workflow run, for one a workflow reported.
919 #[serde(default)]
920 pub target_url: Option<String>,
921}
922
923/// The events a workflow status's context can end in: `CI / pull_request`
924/// is the `CI` check, reported by a run for a `pull_request` event.
925const STATUS_EVENTS: &[&str] = &[
926 "push",
927 "pull_request",
928 "pull_request_target",
929 "pull_request_review",
930 "merge_group",
931 "workflow_dispatch",
932 "workflow_run",
933 "workflow_call",
934 "schedule",
935 "release",
936 "issues",
937 "issue_comment",
938 "repository_dispatch",
939];
940
941/// A status context's check name and the event it was reported for:
942/// `CI / pull_request` is `("CI", Some("pull_request"))`. A context that
943/// does not end in an event, such as `g1t / deploy`, is its own name.
944pub fn check_name(context: &str) -> (&str, Option<&str>) {
945 match context.rsplit_once(" / ") {
946 Some((name, event)) if STATUS_EVENTS.contains(&event) && !name.trim().is_empty() => (name, Some(event)),
947 _ => (context, None),
948 }
949}
950
951/// Where each required check stands among a commit's statuses. A check is
952/// met by any status of that name, for any event: one that failed fails
953/// it, one still running holds it, and with neither, one that passed
954/// passes it. Names compare without regard to case.
955pub fn required_checks(required: &[String], statuses: &[CommitStatus]) -> Vec<RequiredCheck> {
956 required
957 .iter()
958 .map(|name| {
959 let matching: Vec<&CommitStatus> = statuses
960 .iter()
961 .filter(|status| check_name(&status.context).0.eq_ignore_ascii_case(name.trim()))
962 .collect();
963 let failed = matching.iter().find(|s| s.state == "failure" || s.state == "error");
964 let pending = matching.iter().find(|s| s.state == "pending");
965 let passed = matching.iter().find(|s| s.state == "success");
966 let (state, decided) = match (failed, pending, passed) {
967 (Some(status), _, _) => (RequiredState::Failure, Some(*status)),
968 (None, Some(status), _) => (RequiredState::Pending, Some(*status)),
969 (None, None, Some(status)) => (RequiredState::Success, Some(*status)),
970 _ => (RequiredState::Expected, None),
971 };
972 RequiredCheck {
973 name: name.trim().to_owned(),
974 state,
975 description: decided.and_then(|status| status.description.clone()),
976 target_url: decided.and_then(|status| status.target_url.clone()),
977 }
978 })
979 .collect()
980}
981
982/// A repository's required check names, tidied: trimmed, without blanks
983/// or repeats (ignoring case), at most [`MAX_REQUIRED_CHECKS`].
984pub fn tidy_required(names: &[String]) -> Vec<String> {
985 let mut out: Vec<String> = Vec::new();
986 for name in names {
987 let name: String = name.trim().chars().take(MAX_CHECK_NAME_CHARS).collect();
988 if !name.is_empty() && !out.iter().any(|kept| kept.eq_ignore_ascii_case(&name)) {
989 out.push(name);
990 }
991 }
992 out.truncate(MAX_REQUIRED_CHECKS);
993 out
994}
995
996/// The most checks a branch can require, and the longest name of one.
997pub const MAX_REQUIRED_CHECKS: usize = 20;
998pub const MAX_CHECK_NAME_CHARS: usize = 100;
999
1000/// A check name seen on the repository's commits recently, for choosing
1001/// required checks: what reported it, and for which events.
1002#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1003#[serde(rename_all = "camelCase")]
1004pub struct SeenCheck {
1005 pub name: String,
1006 /// The events it was reported for, such as `pull_request`; empty for a
1007 /// status that names none, such as a deployment's.
1008 pub events: Vec<String>,
1009 /// RFC 3339. The latest report.
1010 pub last_seen: String,
Agents and memory, checks and conflicts, profiles, slug renames, custom domains1011}
1012
Fast pages, required checks on the branch, self-hosted runners, honest incidents1013/// `seen_checks`: the check names reported on a repository's commits in
1014/// the last 30 days, most recent first. Returns `Outcome<Vec<SeenCheck>>`.
1015#[derive(Debug, Serialize, Deserialize)]
1016pub struct SeenChecksArgs {
1017 pub repo: RepoPath,
1018 pub viewer: Viewer,
1019}
1020
1021/// The heading an issue's plain-words description of done goes under.
1022pub const DEFINITION_OF_DONE: &str = "## Definition of done";
1023
1024/// An issue's body with `items` added as a bulleted "Definition of done"
1025/// section, for the agent and reviewers to read. Nothing is added when
1026/// `items` is empty, or when the body already has the section, so folding
1027/// the same items twice changes nothing.
1028pub fn with_definition_of_done(body: &str, items: &[String]) -> String {
1029 let items: Vec<&str> = items.iter().map(|item| item.trim()).filter(|item| !item.is_empty()).collect();
1030 let body = body.trim();
1031 if items.is_empty() || body.contains(DEFINITION_OF_DONE) {
1032 return body.to_owned();
1033 }
1034 let list = items.iter().map(|item| format!("- {item}")).collect::<Vec<_>>().join("\n");
1035 if body.is_empty() {
1036 format!("{DEFINITION_OF_DONE}\n\n{list}")
1037 } else {
1038 format!("{body}\n\n{DEFINITION_OF_DONE}\n\n{list}")
1039 }
1040}
1041
1042/// Commands, as items of a definition of done: "`npm test` passes."
1043pub fn commands_pass(commands: &[String]) -> Vec<String> {
1044 commands
1045 .iter()
1046 .map(|command| command.trim())
1047 .filter(|command| !command.is_empty())
1048 .map(|command| format!("`{}` passes.", command.replace('`', "'")))
1049 .collect()
1050}
1051
Agents and memory, checks and conflicts, profiles, slug renames, custom domains1052/// Whether a pull request's change merges cleanly into the branch it
1053/// targets.
1054#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
1055#[serde(rename_all = "lowercase")]
1056pub enum Mergeable {
1057 /// It merges without conflicts.
1058 Clean,
1059 /// Some files conflict: see `PullDetail::conflicts`.
1060 Conflicting,
1061 /// Not known: never worked out, or it could not be.
1062 #[default]
1063 Unknown,
1064 /// Being worked out now.
1065 Checking,
GitHub Actions on g1t, part two: running workflows1066}
1067
Agents and memory, checks and conflicts, profiles, slug renames, custom domains1068impl Mergeable {
1069 pub fn as_str(self) -> &'static str {
1070 match self {
1071 Mergeable::Clean => "clean",
1072 Mergeable::Conflicting => "conflicting",
1073 Mergeable::Unknown => "unknown",
1074 Mergeable::Checking => "checking",
1075 }
1076 }
1077
1078 pub fn parse(value: Option<&str>) -> Mergeable {
1079 match value {
1080 Some("clean") => Mergeable::Clean,
1081 Some("conflicting") => Mergeable::Conflicting,
1082 Some("checking") => Mergeable::Checking,
1083 _ => Mergeable::Unknown,
1084 }
1085 }
1086}
1087
1088/// `start_mergecheck`: claims the probe of whether a pull request merges
1089/// cleanly, which the work service asked for with a `pull.mergecheck`
1090/// event. Called by the runner service, which starts the sandbox. Refused
1091/// when it is no longer wanted, or when the repository already has as many
1092/// probes running as it may. Returns `Outcome<MergecheckJob>`.
1093#[derive(Debug, Serialize, Deserialize)]
1094#[serde(rename_all = "camelCase")]
1095pub struct StartMergecheckArgs {
1096 pub pull_id: String,
1097}
1098
1099/// What a sandbox needs to find out whether a pull request merges cleanly.
1100#[derive(Debug, Serialize, Deserialize)]
1101#[serde(rename_all = "camelCase")]
1102pub struct MergecheckJob {
1103 pub pull_id: String,
1104 /// Lets the sandbox, and nothing else, report this probe.
1105 pub token: String,
1106 pub repo: RepoPath,
1107 pub number: u32,
1108 pub default_branch: String,
1109 /// The default branch's commit to merge into.
1110 pub base: String,
1111 /// The repository holding the change: its fork, or the repository.
1112 pub source: RepoPath,
1113 /// The branch of `source` holding it.
1114 pub branch: String,
1115 /// The change's commit.
1116 pub head: String,
g1t is the stored author of what it opens; the person who asked is requested_by and keeps the author's rights1117 /// Who the pull request is for ([`Pull::owner`]: whoever asked g1t for
1118 /// it, or its author), and so can read its source.
Agents and memory, checks and conflicts, profiles, slug renames, custom domains1119 pub author: User,
1120}
1121
1122/// `report_mergecheck`: what a sandbox found. Returns `Outcome<Mergeable>`.
1123#[derive(Debug, Serialize, Deserialize)]
1124#[serde(rename_all = "camelCase")]
1125pub struct ReportMergecheckArgs {
1126 pub pull_id: String,
1127 pub token: String,
1128 /// The files that conflict; empty when it merges cleanly.
1129 #[serde(default)]
1130 pub conflicts: Vec<String>,
1131 /// Why it could not be found out.
1132 #[serde(default)]
1133 pub error: Option<String>,
1134}
1135
GitHub Actions on g1t, part two: running workflows1136/// What a workflow run (or another tool) says about a commit.
1137#[derive(Clone, Debug, Serialize, Deserialize)]
1138#[serde(rename_all = "camelCase")]
1139pub struct CommitStatus {
1140 /// What reported it, such as `CI / push`.
1141 pub context: String,
1142 /// `pending`, `success`, `failure` or `error`.
1143 pub state: String,
1144 pub description: Option<String>,
1145 /// Where to see more, such as the run's page.
1146 pub target_url: Option<String>,
1147 pub updated_at: String,
Merge rulesets: branch and tag rules, agent-first, enforced on push and merge1148 /// The integration that reported it (`actions`, `deployments`,
1149 /// `security`, `g1t`), when recorded. A required check can insist on
1150 /// one (`rules::RequiredCheck::integration`).
1151 #[serde(default, skip_serializing_if = "Option::is_none")]
1152 pub source: Option<String>,
GitHub Actions on g1t, part two: running workflows1153}
1154
1155/// `set_commit_status`: for services only. Returns `Outcome<bool>`.
1156#[derive(Debug, Serialize, Deserialize)]
1157#[serde(rename_all = "camelCase")]
1158pub struct SetCommitStatusArgs {
1159 pub repo_id: String,
1160 pub sha: String,
1161 pub context: String,
1162 pub state: String,
1163 #[serde(default)]
1164 pub description: Option<String>,
1165 #[serde(default)]
1166 pub target_url: Option<String>,
Merge rulesets: branch and tag rules, agent-first, enforced on push and merge1167 /// The integration reporting it: `actions`, `deployments`, `security`
1168 /// or `g1t`.
1169 #[serde(default)]
1170 pub source: Option<String>,
Usage, like a hosting provider's: what agents cost, per day, task, repository and pull request1171}
1172
1173/// A message a person sent an agent at work on a pull request. The agent
1174/// receives it at its next step.
1175#[derive(Clone, Debug, Serialize, Deserialize)]
1176#[serde(rename_all = "camelCase")]
1177pub struct AgentMessage {
1178 pub id: String,
1179 pub author: String,
1180 pub body: String,
1181 /// RFC 3339.
1182 pub created_at: String,
1183 /// RFC 3339. When the agent received it; null until then.
1184 pub delivered_at: Option<String>,
Agents ask each other, hand each other work, and answer1185 /// `message` from a person, or from another pull request's agent a
1186 /// `question`, a `handoff` of work, or the `answer` to one.
1187 #[serde(default = "message_kind")]
1188 pub kind: String,
1189 /// The pull request whose agent sent it, when an agent did.
1190 #[serde(default)]
1191 pub from_number: Option<u32>,
1192 /// The pull request it was sent to.
1193 #[serde(default)]
1194 pub to_number: u32,
1195 /// For a question or handoff: the reply, once there is one.
1196 #[serde(default)]
1197 pub answer: Option<String>,
1198 /// For a handoff: whether it was declined.
1199 #[serde(default)]
1200 pub declined: bool,
1201 /// For the agent that sent it: what to expect, when the agent it asked
1202 /// is not at work and will not answer soon.
1203 #[serde(default, skip_serializing_if = "Option::is_none")]
1204 pub hint: Option<String>,
1205}
1206
1207fn message_kind() -> String {
1208 "message".to_owned()
Usage, like a hosting provider's: what agents cost, per day, task, repository and pull request1209}
1210
1211/// `message_agent`: sends the agent working on a pull request a message.
g1t is the stored author of what it opens; the person who asked is requested_by and keeps the author's rights1212/// The pull request's owner ([`Pull::owner`]: whoever asked g1t for it, or
1213/// its author) and members of the workspace may. Returns
Usage, like a hosting provider's: what agents cost, per day, task, repository and pull request1214/// `Outcome<AgentMessage>`.
1215#[derive(Debug, Serialize, Deserialize)]
1216pub struct MessageAgentArgs {
1217 pub actor: User,
1218 pub repo: RepoPath,
1219 pub number: u32,
1220 pub body: String,
Agents ask each other, hand each other work, and answer1221 /// For an agent: `question` or `handoff`; a person's is a `message`.
1222 #[serde(default)]
1223 pub kind: Option<String>,
1224 /// For an agent: the pull request it is working on, which the reply
1225 /// goes back to.
1226 #[serde(default)]
1227 pub from_number: Option<u32>,
1228}
1229
1230/// `answer_message`: replies to a question or a handoff an agent received,
1231/// accepting or declining a handoff. The reply reaches the asking agent at
1232/// its next step. Returns `Outcome<AgentMessage>`, the message answered.
1233#[derive(Debug, Serialize, Deserialize)]
1234pub struct AnswerMessageArgs {
1235 pub actor: User,
1236 pub repo: RepoPath,
1237 pub id: String,
1238 pub body: String,
1239 #[serde(default)]
1240 pub decline: bool,
Usage, like a hosting provider's: what agents cost, per day, task, repository and pull request1241}
1242
1243/// `take_messages`: the messages not yet delivered to the agent working on
1244/// a pull request, marked delivered. Only g1t's agents may. Returns
1245/// `Outcome<Vec<AgentMessage>>`.
1246#[derive(Debug, Serialize, Deserialize)]
1247pub struct TakeMessagesArgs {
1248 pub actor: User,
1249 pub repo: RepoPath,
1250 pub number: u32,
Agents as a team: lifecycle, merge queue, billing and a new shell1251}
1252
1253/// A step on the way from an assigned issue to a pull request that is ready
1254/// to merge. g1t takes each one without being asked.
1255#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
1256#[serde(rename_all = "snake_case")]
1257pub enum Stage {
1258 /// The agent is making the change.
1259 Working,
Fast pages, required checks on the branch, self-hosted runners, honest incidents1260 /// Waiting for the checks workflows report on its head.
Agents as a team: lifecycle, merge queue, billing and a new shell1261 Checking,
g1t is one name: its agent's work, commits and comments show as @g1t, and nobody can claim g1t or g1t-agent1262 /// g1t is reviewing it.
Agents as a team: lifecycle, merge queue, billing and a new shell1263 Reviewing,
1264 /// The agent is addressing failed checks or a review.
1265 Revising,
1266 /// The agent is merging in the branch it would land on, which moved.
1267 CatchingUp,
Agents asked while not at work are woken to answer1268 /// Woken to answer a question another agent asked it, or a handoff.
1269 Answering,
Agents as a team: lifecycle, merge queue, billing and a new shell1270 /// In the repository's merge queue, being tested with what is ahead of
1271 /// it before it lands.
1272 Queued,
Fast pages, required checks on the branch, self-hosted runners, honest incidents1273 /// Required checks passed, reviewed and approved, up to date. A
1274 /// person merges.
Agents as a team: lifecycle, merge queue, billing and a new shell1275 Ready,
1276 /// g1t has stopped and a person has to decide what happens next.
1277 NeedsYou,
1278}
1279
1280/// Where a pull request made by a g1t agent stands. See [`Stage`].
1281#[derive(Clone, Debug, Serialize, Deserialize)]
1282pub struct Lifecycle {
1283 pub stage: Stage,
1284 /// One sentence saying what is happening, or why it stopped.
1285 pub detail: String,
1286 /// How many times the agent has been sent back to revise it.
1287 pub revisions: u32,
1288}
1289
1290/// `advance`: works out the next step for a pull request g1t is seeing
1291/// through and, if there is one to take now, claims it, so that it is
1292/// taken once however many times this is called. Called by the runner
1293/// service, which carries the step out. Returns `Advance`.
1294#[derive(Debug, Serialize, Deserialize)]
1295#[serde(rename_all = "camelCase")]
1296pub struct AdvanceArgs {
1297 pub pull_id: String,
1298}
1299
1300#[derive(Debug, Serialize, Deserialize)]
1301#[serde(tag = "action", rename_all = "snake_case")]
1302pub enum Advance {
1303 /// Nothing to do now: a step is under way, or it is a person's turn.
1304 None,
1305 /// Have a g1t agent review it.
1306 Review { job: LifecycleJob },
1307 /// Send the agent back to address `job.feedback`.
1308 Revise { job: LifecycleJob },
1309 /// Merge in the branch it would land on.
1310 CatchUp { job: LifecycleJob },
Work service in Rust, with RFC 3339 timestamps1311}
1312
Agents as a team: lifecycle, merge queue, billing and a new shell1313/// What the runner needs to carry out a step of a pull request's lifecycle.
1314#[derive(Debug, Serialize, Deserialize)]
1315#[serde(rename_all = "camelCase")]
1316pub struct LifecycleJob {
1317 pub pull_id: String,
1318 pub repo: RepoPath,
1319 pub number: u32,
g1t is the stored author of what it opens; the person who asked is requested_by and keeps the author's rights1320 /// Who the pull request belongs to ([`Pull::owner`]: whoever asked g1t
1321 /// for it, or its author). Sandboxes act as them.
Agents as a team: lifecycle, merge queue, billing and a new shell1322 pub author: User,
1323 /// The repository holding the change: its fork, or the repository
1324 /// itself for one made on a branch.
1325 pub source: RepoPath,
1326 /// The branch of the source holding the change; its default branch
1327 /// when absent.
1328 #[serde(default)]
1329 pub branch: Option<String>,
1330 pub default_branch: String,
1331 pub title: String,
1332 pub description: String,
1333 pub issue: Option<Issue>,
1334 /// For a revision: the failed checks or the review to address.
1335 pub feedback: String,
1336 /// For a revision: which one this is, from 1.
1337 pub round: u32,
1338}
1339
1340/// How a repository wants its pull requests handled. A repository that has
1341/// changed nothing has the defaults.
1342#[derive(Clone, Debug, Serialize, Deserialize)]
1343#[serde(rename_all = "camelCase", default)]
1344pub struct RepoSettings {
1345 /// Land a g1t agent's pull request without a person once it is ready:
Fast pages, required checks on the branch, self-hosted runners, honest incidents1346 /// required checks passed and approved as the settings below require.
Agents as a team: lifecycle, merge queue, billing and a new shell1347 pub auto_merge: bool,
Fast pages, required checks on the branch, self-hosted runners, honest incidents1348 /// The checks that must pass on a pull request's head before it may
1349 /// merge into the default branch, by name: a workflow's name (`CI`), or
1350 /// the context of another status (`g1t / deploy`). The same for a
1351 /// person's pull request and an agent's, and for the merge queue.
1352 pub required_checks: Vec<String>,
Agents as a team: lifecycle, merge queue, billing and a new shell1353 /// Refuse to merge a pull request that does not contain the default
1354 /// branch's latest commits, so that what merges is what was checked.
1355 /// When off, merging one that is behind brings it up to date first.
1356 pub require_up_to_date: bool,
1357 /// How many approving reviews a pull request needs before it may
1358 /// merge. A reviewer who has since asked for changes blocks it.
1359 pub required_approvals: u32,
1360 /// Whether a g1t agent's approval counts towards `required_approvals`.
1361 pub count_agent_approvals: bool,
Fast pages, required checks on the branch, self-hosted runners, honest incidents1362 /// Whether someone who may merge can bypass required checks that have
1363 /// not passed, by saying so as they merge.
Agents as a team: lifecycle, merge queue, billing and a new shell1364 pub allow_ignoring_checks: bool,
1365 /// Whether a g1t agent's pull request is reviewed by a second agent
1366 /// without being asked.
1367 pub agent_review: bool,
1368 /// How many times a g1t agent is sent back to its pull request before
1369 /// a person is asked instead.
1370 pub max_revisions: u32,
1371 /// Merge through a queue: pull requests are tested together with those
1372 /// ahead of them, and only a combination that passed reaches the default
1373 /// branch.
1374 pub merge_queue: bool,
Thirteen MCP tools and classic token scopes; agents rate their confidence and can be put on an issue in one step1375 /// Ask a person before merging a g1t agent's change whose confidence is
1376 /// low: auto-merge and the merge queue leave it, and it needs someone,
1377 /// until a person approves it.
1378 pub hold_low_confidence: bool,
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar1379 /// Refuse to merge until the code owners of every file it changes
1380 /// (its CODEOWNERS file) have approved, as many as each section asks.
1381 /// Only people count, and `g1t` only where the file names `@g1t`.
1382 pub require_code_owner_review: bool,
Agents as a team: lifecycle, merge queue, billing and a new shell1383 /// Username of the member who last changed the settings, if anyone has.
1384 pub updated_by: Option<String>,
1385 /// RFC 3339.
1386 pub updated_at: Option<String>,
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar1387}
1388
Agents as a team: lifecycle, merge queue, billing and a new shell1389impl Default for RepoSettings {
1390 fn default() -> Self {
1391 RepoSettings {
1392 auto_merge: false,
Fast pages, required checks on the branch, self-hosted runners, honest incidents1393 required_checks: Vec::new(),
Agents as a team: lifecycle, merge queue, billing and a new shell1394 require_up_to_date: false,
1395 required_approvals: 0,
1396 count_agent_approvals: true,
1397 allow_ignoring_checks: true,
1398 agent_review: true,
1399 max_revisions: 2,
1400 merge_queue: false,
Thirteen MCP tools and classic token scopes; agents rate their confidence and can be put on an issue in one step1401 hold_low_confidence: true,
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar1402 require_code_owner_review: false,
Agents as a team: lifecycle, merge queue, billing and a new shell1403 updated_by: None,
1404 updated_at: None,
1405 }
1406 }
1407}
1408
1409/// `update_settings`: replaces a repository's settings. Members of its
1410/// workspace only. Returns `Outcome<RepoSettings>`. `get_settings` takes
1411/// `ViewArgs` and returns the same.
1412#[derive(Debug, Serialize, Deserialize)]
1413pub struct UpdateSettingsArgs {
1414 pub actor: User,
1415 pub repo: RepoPath,
1416 /// Who changed them and when are filled in by the service.
1417 pub settings: RepoSettings,
1418}
1419
1420/// `catch_up_job`: what the runner needs to bring a pull request up to date
1421/// because a merge of it was asked for. Null if none was. Returns
1422/// `Option<LifecycleJob>`.
1423#[derive(Debug, Serialize, Deserialize)]
1424#[serde(rename_all = "camelCase")]
1425pub struct CatchUpJobArgs {
1426 pub pull_id: String,
1427}
1428
Agents asked while not at work are woken to answer1429/// `wake_for_messages`: the agent on a pull request was asked a question
1430/// or handed work while it was not at work. Claims a short step for it to
1431/// answer, and hands over what it was sent, marked read. Null when there
1432/// is nothing waiting, or the pull request cannot take a step now.
1433/// Returns `Option<Wake>`.
1434#[derive(Debug, Serialize, Deserialize)]
1435#[serde(rename_all = "camelCase")]
1436pub struct WakeForMessagesArgs {
1437 pub pull_id: String,
1438}
1439
1440/// What an agent woken to answer needs: its pull request, and what it was
1441/// sent, oldest first.
1442#[derive(Debug, Serialize, Deserialize)]
1443#[serde(rename_all = "camelCase")]
1444pub struct Wake {
1445 pub job: LifecycleJob,
1446 pub messages: Vec<AgentMessage>,
1447}
1448
Agents as a team: lifecycle, merge queue, billing and a new shell1449/// `stall`: records that a step could not be carried out, so that g1t
1450/// stops and a person is asked. Returns `bool`.
1451#[derive(Debug, Serialize, Deserialize)]
1452#[serde(rename_all = "camelCase")]
1453pub struct StallArgs {
1454 pub pull_id: String,
1455 pub reason: String,
Events: review requests, assignments, stops and deployments are published1456 /// The person who stopped it, by id, when someone did: they are not
1457 /// told it needs them.
1458 #[serde(default)]
1459 pub by: Option<String>,
Agents as a team: lifecycle, merge queue, billing and a new shell1460}
1461
1462/// `managed_pulls`: ids of the open pull requests g1t is seeing through,
1463/// in one repository or in all of them. Returns `Vec<String>`.
1464#[derive(Debug, Default, Serialize, Deserialize)]
1465#[serde(rename_all = "camelCase")]
1466pub struct ManagedPullsArgs {
1467 #[serde(default)]
1468 pub repo_id: Option<String>,
1469}
1470
Issues and pull requests replace intents and attempts1471/// `open_issue`. Returns `Outcome<Issue>`.
Work service in Rust, with RFC 3339 timestamps1472#[derive(Debug, Serialize, Deserialize)]
Issues and pull requests replace intents and attempts1473pub struct OpenIssueArgs {
Work service in Rust, with RFC 3339 timestamps1474 pub actor: User,
1475 pub repo: RepoPath,
1476 pub title: String,
1477 #[serde(default)]
Issues and pull requests replace intents and attempts1478 pub body: String,
1479 #[serde(default)]
1480 pub labels: Vec<String>,
Fast pages, required checks on the branch, self-hosted runners, honest incidents1481 /// Deprecated: commands, added to the body under "Definition of done".
1482 /// Checks are the workflows the branch's protection requires.
Work service in Rust, with RFC 3339 timestamps1483 #[serde(default)]
1484 pub checks: Vec<String>,
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar1485 /// The number of the milestone to put it in. Needs the Triage role.
1486 #[serde(default)]
1487 pub milestone: Option<u32>,
Work service in Rust, with RFC 3339 timestamps1488}
1489
g1t is one name: its agent's work, commits and comments show as @g1t, and nobody can claim g1t or g1t-agent1490/// `delegate_issue`: opens an issue to put g1t on at once, refused
Thirteen MCP tools and classic token scopes; agents rate their confidence and can be put on an issue in one step1491/// before anything is opened unless `actor` may put agents to work in the
1492/// repository (Run, which the Write role has). The runner service's
1493/// `delegate` calls it and then starts the agent. Returns `Outcome<Issue>`.
1494#[derive(Debug, Serialize, Deserialize)]
1495pub struct DelegateIssueArgs {
1496 pub actor: User,
1497 pub repo: RepoPath,
1498 pub title: String,
1499 #[serde(default)]
1500 pub body: String,
1501 #[serde(default)]
1502 pub labels: Vec<String>,
Fast pages, required checks on the branch, self-hosted runners, honest incidents1503 /// Deprecated, as on `OpenIssueArgs`.
Thirteen MCP tools and classic token scopes; agents rate their confidence and can be put on an issue in one step1504 #[serde(default)]
1505 pub checks: Vec<String>,
1506}
1507
1508/// What became of the agent when an issue was opened and handed to it in
1509/// one step.
1510#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
1511#[serde(rename_all = "snake_case")]
1512pub enum AgentStartStatus {
1513 /// It is at work on the issue's pull request.
1514 Started,
1515 /// Every agent slot of the workspace is busy: it starts on its own when
1516 /// one frees up.
1517 Queued,
1518 /// It did not start, and will not until someone fixes what `code` says.
1519 NotStarted,
1520}
1521
1522/// Whether the agent started, and if not, why and what fixes it.
1523#[derive(Clone, Debug, Serialize, Deserialize)]
1524#[serde(rename_all = "camelCase")]
1525pub struct AgentStart {
1526 pub status: AgentStartStatus,
1527 /// Why it did not start: `not_paid`, `trial_used`, `limit`, `paused`,
1528 /// `issue_cap`, `billing_unavailable` or `no_model`; `waiting` when
1529 /// queued.
1530 #[serde(default)]
1531 pub code: Option<String>,
1532 /// What happened, in a sentence or two, with what to do.
1533 #[serde(default)]
1534 pub message: Option<String>,
1535 /// Where the fix is: the workspace's billing or model settings.
1536 #[serde(default)]
1537 pub fix_url: Option<String>,
1538}
1539
1540/// The runner service's `delegate`: the issue opened, and the agent put on
1541/// it. The issue exists whatever became of the agent.
1542#[derive(Clone, Debug, Serialize, Deserialize)]
1543pub struct Delegated {
1544 pub issue: Issue,
1545 /// The pull request the agent opened, when it started.
1546 #[serde(default)]
1547 pub pull: Option<Pull>,
1548 pub agent: AgentStart,
1549}
1550
1551/// `report_confidence`: what the agent of a run says of its own change,
1552/// with the run's own token. Kept with the run, and the pull request's
1553/// confidence is worked out again with it. Returns `Outcome<bool>`.
1554#[derive(Debug, Serialize, Deserialize)]
1555#[serde(rename_all = "camelCase")]
1556pub struct ReportConfidenceArgs {
1557 pub run_id: String,
1558 pub token: String,
1559 /// `high`, `medium` or `low`.
1560 pub confidence: String,
1561 #[serde(default)]
1562 pub uncertain_about: Vec<String>,
1563}
1564
Issues and pull requests replace intents and attempts1565/// `list_issues`, newest first. Returns `Outcome<Vec<Issue>>`.
Work service in Rust, with RFC 3339 timestamps1566#[derive(Debug, Serialize, Deserialize)]
Issues and pull requests replace intents and attempts1567pub struct ListIssuesArgs {
Work service in Rust, with RFC 3339 timestamps1568 pub repo: RepoPath,
1569 pub viewer: Viewer,
1570 #[serde(default)]
Issues and pull requests replace intents and attempts1571 pub state: Option<State>,
1572 /// Only issues carrying this label.
1573 #[serde(default)]
1574 pub label: Option<String>,
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar1575 /// Only issues in the milestone of this number.
1576 #[serde(default)]
1577 pub milestone: Option<u32>,
Work service in Rust, with RFC 3339 timestamps1578}
1579
Issues and pull requests replace intents and attempts1580/// `list_pulls`, newest first. Returns `Outcome<Vec<Pull>>`.
Work service in Rust, with RFC 3339 timestamps1581#[derive(Debug, Serialize, Deserialize)]
Issues and pull requests replace intents and attempts1582pub struct ListPullsArgs {
1583 pub repo: RepoPath,
1584 pub viewer: Viewer,
1585 #[serde(default)]
1586 pub state: Option<State>,
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar1587 /// Only pull requests carrying this label.
1588 #[serde(default)]
1589 pub label: Option<String>,
1590 /// Only pull requests in the milestone of this number.
1591 #[serde(default)]
1592 pub milestone: Option<u32>,
1593 /// Only pull requests into this branch.
1594 #[serde(default)]
1595 pub base: Option<String>,
Issues and pull requests replace intents and attempts1596}
1597
Fast pages, required checks on the branch, self-hosted runners, honest incidents1598/// `pulls_for_repos`: the newest open and the newest closed pull requests
1599/// of many repositories, in one call, for pages that show several projects
1600/// at once. Repositories the viewer cannot read are left out, as are forks
1601/// (ask those with `list_pulls`). Returns `Vec<RepoPulls>`.
1602#[derive(Debug, Serialize, Deserialize)]
1603#[serde(rename_all = "camelCase")]
1604pub struct PullsForReposArgs {
1605 /// At most [`MAX_PULLS_FOR_REPOS`] are looked at.
1606 pub repo_ids: Vec<String>,
1607 pub viewer: Viewer,
1608 /// How many of each, open and closed, per repository (at most 100).
1609 pub limit: u32,
1610}
1611
1612/// The most repositories one `pulls_for_repos` call looks at.
1613pub const MAX_PULLS_FOR_REPOS: usize = 50;
1614
1615/// One repository's pull requests from `pulls_for_repos`, newest first.
1616#[derive(Debug, Serialize, Deserialize)]
1617#[serde(rename_all = "camelCase")]
1618pub struct RepoPulls {
1619 pub repo_id: String,
1620 /// Draft and open.
1621 pub open: Vec<Pull>,
1622 /// Merged and closed.
1623 pub closed: Vec<Pull>,
1624}
1625
Issues and pull requests replace intents and attempts1626/// `get_issue` (`Outcome<IssueDetail>`), `get_pull` (`Outcome<PullDetail>`),
1627/// `read_session` (`Outcome<Vec<SessionEntry>>`), `list_labels`
1628/// (`Outcome<Vec<String>>`) and `counts` (`Outcome<Counts>`). The last two
1629/// ignore `number`.
1630#[derive(Debug, Serialize, Deserialize)]
1631#[serde(rename_all = "camelCase")]
1632pub struct ViewArgs {
Work service in Rust, with RFC 3339 timestamps1633 pub repo: RepoPath,
Issues and pull requests replace intents and attempts1634 #[serde(default)]
Work service in Rust, with RFC 3339 timestamps1635 pub number: u32,
1636 pub viewer: Viewer,
Issues and pull requests replace intents and attempts1637 /// For `read_session`: only entries after this sequence number.
1638 #[serde(default)]
1639 pub after_seq: u32,
1640}
1641
1642/// How many issues and pull requests are open on a repository.
1643#[derive(Debug, Serialize, Deserialize)]
1644pub struct Counts {
1645 pub issues: u32,
1646 pub pulls: u32,
1647}
1648
1649/// `update_issue`: changes whichever fields are given. The author or a
1650/// member of the workspace may. Returns `Outcome<Issue>`.
1651#[derive(Debug, Serialize, Deserialize)]
1652pub struct UpdateIssueArgs {
1653 pub actor: User,
1654 pub repo: RepoPath,
1655 pub number: u32,
1656 #[serde(default)]
1657 pub title: Option<String>,
1658 #[serde(default)]
1659 pub body: Option<String>,
1660 #[serde(default)]
1661 pub labels: Option<Vec<String>>,
Agents as a team: lifecycle, merge queue, billing and a new shell1662 /// Usernames of the people it is assigned to; replaces the whole set.
g1t is one name: its agent's work, commits and comments show as @g1t, and nobody can claim g1t or g1t-agent1663 /// Assigning it to g1t is the runner's `run`, not this.
Agents as a team: lifecycle, merge queue, billing and a new shell1664 #[serde(default)]
1665 pub assignees: Option<Vec<String>>,
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar1666 /// The number of the milestone to put it in; 0 takes it out of its
1667 /// milestone. Needs the Triage role.
1668 #[serde(default)]
1669 pub milestone: Option<u32>,
Work service in Rust, with RFC 3339 timestamps1670}
1671
Issues and pull requests replace intents and attempts1672/// `close_issue` and `reopen_issue`. Each returns `Outcome<Issue>`.
Work service in Rust, with RFC 3339 timestamps1673#[derive(Debug, Serialize, Deserialize)]
Issues and pull requests replace intents and attempts1674pub struct IssueActionArgs {
Work service in Rust, with RFC 3339 timestamps1675 pub actor: User,
Issues and pull requests replace intents and attempts1676 pub repo: RepoPath,
1677 pub number: u32,
1678 /// For `close_issue`; `completed` if left out.
1679 #[serde(default)]
1680 pub reason: Option<IssueReason>,
Work service in Rust, with RFC 3339 timestamps1681}
1682
Acceptance checks in sandboxes, line comments and review verdicts1683/// `add_comment`, on an issue or a pull request. On a pull request it may
1684/// name a line of the change, and may carry a verdict; nobody can give a
1685/// verdict on their own pull request. Returns `Outcome<Comment>`.
Work service in Rust, with RFC 3339 timestamps1686#[derive(Debug, Serialize, Deserialize)]
Issues and pull requests replace intents and attempts1687pub struct AddCommentArgs {
Work service in Rust, with RFC 3339 timestamps1688 pub actor: User,
Issues and pull requests replace intents and attempts1689 pub repo: RepoPath,
1690 pub number: u32,
Acceptance checks in sandboxes, line comments and review verdicts1691 /// May be empty when approving.
1692 #[serde(default)]
Issues and pull requests replace intents and attempts1693 pub body: String,
Acceptance checks in sandboxes, line comments and review verdicts1694 #[serde(default)]
1695 pub path: Option<String>,
1696 #[serde(default)]
1697 pub line: Option<u32>,
1698 #[serde(default)]
1699 pub verdict: Option<Verdict>,
Work service in Rust, with RFC 3339 timestamps1700}
1701
Pull requests from branches1702/// `open_pull`. Without `branch`, forks the repo and returns a draft pull
1703/// request to push to. With it, opens a pull request, ready for review,
1704/// for a branch already pushed to the repo. Returns `Outcome<Pull>`.
Work service in Rust, with RFC 3339 timestamps1705#[derive(Debug, Serialize, Deserialize)]
Issues and pull requests replace intents and attempts1706pub struct OpenPullArgs {
1707 pub actor: User,
1708 pub repo: RepoPath,
1709 /// The issue this is for.
Work service in Rust, with RFC 3339 timestamps1710 #[serde(default)]
Issues and pull requests replace intents and attempts1711 pub issue: Option<u32>,
1712 /// Defaults to the issue's title; required without an issue.
1713 #[serde(default)]
1714 pub title: String,
Pull requests from branches1715 /// What changed and why. Usually set later, when a draft is marked ready.
1716 #[serde(default)]
1717 pub body: String,
1718 /// A branch of the repository that already holds the change.
1719 #[serde(default)]
1720 pub branch: Option<String>,
Issues and pull requests replace intents and attempts1721 #[serde(default)]
1722 pub agent: String,
1723 pub runtime: Runtime,
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar1724 /// The branch to merge into: the default branch when absent.
1725 #[serde(default)]
1726 pub base: Option<String>,
Work service in Rust, with RFC 3339 timestamps1727}
1728
Issues and pull requests replace intents and attempts1729/// `ready_pull`, `close_pull` and `merge_pull`. Each returns `Outcome<Pull>`.
Catching up with main takes seconds when the two sides touched different files1730///
1731/// Also `catch_up_pull`: brings the pull request up to date with the
1732/// default branch without a sandbox where that is safe, as the repos
1733/// service's `update_pull_branch` does, after checking that `actor` may
g1t is the stored author of what it opens; the person who asked is requested_by and keeps the author's rights1734/// update it: its owner ([`Pull::owner`]) for a fork, any member for a
1735/// branch.
Catching up with main takes seconds when the two sides touched different files1736/// Returns `Outcome<repos::PullBranchUpdate>`; on `needs_agent` nothing was
1737/// pushed and the runner's `update` is the way on.
Work service in Rust, with RFC 3339 timestamps1738#[derive(Debug, Serialize, Deserialize)]
1739#[serde(rename_all = "camelCase")]
Issues and pull requests replace intents and attempts1740pub struct PullActionArgs {
Work service in Rust, with RFC 3339 timestamps1741 pub actor: User,
Issues and pull requests replace intents and attempts1742 pub repo: RepoPath,
1743 pub number: u32,
1744 /// For `ready_pull`: what changed and why.
Work service in Rust, with RFC 3339 timestamps1745 #[serde(default)]
1746 pub summary: String,
Issues and pull requests replace intents and attempts1747 /// For `merge_pull`: leave the issue open and the other pull requests
1748 /// for it untouched, because this one is only part of the work.
1749 #[serde(default)]
1750 pub keep_issue_open: bool,
Fast pages, required checks on the branch, self-hosted runners, honest incidents1751 /// For `merge_pull`: merge although required checks have not passed,
1752 /// where the repository lets members bypass them.
Acceptance checks in sandboxes, line comments and review verdicts1753 #[serde(default)]
1754 pub ignore_checks: bool,
Merge rulesets: branch and tag rules, agent-first, enforced on push and merge1755 /// For `merge_pull`: merge although rules are not met, where a ruleset
1756 /// lists the actor as one who may bypass it. Recorded as a bypass.
1757 #[serde(default)]
1758 pub bypass_rules: bool,
Acceptance checks in sandboxes, line comments and review verdicts1759}
1760
Agents as a team: lifecycle, merge queue, billing and a new shell1761/// Where a plan stands.
1762#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
1763#[serde(rename_all = "lowercase")]
1764pub enum PlanStatus {
1765 /// An agent is reading the repository and writing it.
1766 Planning,
1767 /// Written, and waiting for a person to read and apply it.
1768 Ready,
1769 /// It could not be written.
1770 Failed,
1771 /// Its issues have been opened.
1772 Applied,
1773}
1774
1775/// One issue a plan proposes.
1776#[derive(Clone, Debug, Default, Serialize, Deserialize)]
1777#[serde(rename_all = "camelCase", default)]
1778pub struct PlannedIssue {
1779 pub title: String,
1780 /// Markdown: what to change, where, and why.
1781 pub body: String,
1782 pub labels: Vec<String>,
Fast pages, required checks on the branch, self-hosted runners, honest incidents1783 /// What is true once it is done, in plain words. Added to the issue's
1784 /// body under "Definition of done". Plans written before this was
1785 /// called `done` named it `checks`.
1786 #[serde(alias = "checks")]
1787 pub done: Vec<String>,
Agents as a team: lifecycle, merge queue, billing and a new shell1788 /// The files it will most likely change.
1789 pub files: Vec<String>,
1790 /// The positions, counting from 1, of earlier issues in the plan that
1791 /// have to be merged first. An agent writes this as `depends_on`.
1792 #[serde(alias = "depends_on")]
1793 pub depends_on: Vec<u32>,
1794 /// Its number, once the plan has been applied and it was kept.
1795 pub number: Option<u32>,
1796}
1797
1798/// An outcome someone wrote, and the issues an agent proposes to get there.
1799#[derive(Clone, Debug, Serialize, Deserialize)]
1800#[serde(rename_all = "camelCase")]
1801pub struct Plan {
1802 pub id: String,
1803 pub repo_id: String,
1804 /// The outcome wanted, as written.
1805 pub brief: String,
1806 pub status: PlanStatus,
1807 /// The agent's account of how it split the work.
1808 pub summary: String,
1809 pub issues: Vec<PlannedIssue>,
1810 /// Why it could not be written, when `status` is `failed`.
1811 pub error: Option<String>,
1812 pub author: User,
1813 /// RFC 3339.
1814 pub created_at: String,
1815 /// RFC 3339.
1816 pub finished_at: Option<String>,
Dark gray base with lavender as an accent, and a live outcome view for plans1817 /// Once applied: where each issue it opened stands now, in plan order.
1818 /// Filled in by `get_plan` only.
1819 #[serde(default)]
1820 pub progress: Vec<IssueProgress>,
Agents ask each other, hand each other work, and answer1821 /// Questions and handoffs between the agents on its pull requests,
1822 /// newest first. Filled in by `get_plan` only.
1823 #[serde(default)]
1824 pub exchanges: Vec<AgentMessage>,
Dark gray base with lavender as an accent, and a live outcome view for plans1825}
1826
1827/// Where one issue of an applied plan stands.
1828#[derive(Clone, Debug, Serialize, Deserialize)]
1829#[serde(rename_all = "camelCase")]
1830pub struct IssueProgress {
1831 pub number: u32,
1832 pub title: String,
1833 /// `blocked` (waiting on issues it depends on), `waiting` (for an
1834 /// agent), `open` (nobody on it), one of the lifecycle stages
1835 /// (`working`, `checking`, `reviewing`, `revising`, `catching_up`,
Agents asked while not at work are woken to answer1836 /// `answering`, `queued`, `ready`, `needs_you`), `landed` or `closed`.
Dark gray base with lavender as an accent, and a live outcome view for plans1837 pub state: String,
1838 /// One sentence about where it stands.
1839 pub detail: String,
1840 /// The issues it is waiting on that are still open.
1841 pub blocked_by: Vec<u32>,
1842 /// The pull request carrying it, the newest if several.
1843 pub pull: Option<u32>,
g1t is one name: its agent's work, commits and comments show as @g1t, and nobody can claim g1t or g1t-agent1844 /// Who or what is working on it, e.g. `g1t`.
Dark gray base with lavender as an accent, and a live outcome view for plans1845 pub agent: Option<String>,
Agents as a team: lifecycle, merge queue, billing and a new shell1846}
1847
1848/// `start_plan`: records an outcome to plan for. Members of the
1849/// repository's workspace only. Called by the runner service, which starts
1850/// the sandbox. Returns `Outcome<PlanJob>`.
1851#[derive(Debug, Serialize, Deserialize)]
1852pub struct StartPlanArgs {
1853 pub actor: User,
1854 pub repo: RepoPath,
1855 pub brief: String,
1856}
1857
1858/// What a sandbox needs to write a plan.
1859#[derive(Debug, Serialize, Deserialize)]
1860#[serde(rename_all = "camelCase")]
1861pub struct PlanJob {
1862 pub plan_id: String,
1863 /// Lets the sandbox, and nothing else, report this plan.
1864 pub token: String,
1865 pub brief: String,
1866 pub repo: RepoPath,
1867}
1868
1869/// `report_plan`: the plan a sandbox's agent wrote, or why it could not
1870/// write one. Returns `Outcome<bool>`.
1871#[derive(Debug, Serialize, Deserialize)]
1872#[serde(rename_all = "camelCase")]
1873pub struct ReportPlanArgs {
1874 pub plan_id: String,
1875 pub token: String,
1876 #[serde(default)]
1877 pub summary: String,
1878 #[serde(default)]
1879 pub issues: Vec<PlannedIssue>,
1880 #[serde(default)]
1881 pub error: Option<String>,
1882}
1883
1884/// `get_plan`. Members only. Returns `Outcome<Plan>`. `list_plans` takes
1885/// `ViewArgs` and returns `Outcome<Vec<Plan>>`, newest first.
1886#[derive(Debug, Serialize, Deserialize)]
1887pub struct PlanArgs {
1888 pub repo: RepoPath,
1889 pub viewer: Viewer,
1890 pub id: String,
1891}
1892
1893/// `apply_plan`: opens a plan's issues, each blocked by the ones it depends
1894/// on. Members only, and once. Returns `Outcome<Plan>`, its issues now
1895/// carrying their numbers.
1896#[derive(Debug, Serialize, Deserialize)]
1897pub struct ApplyPlanArgs {
1898 pub actor: User,
1899 pub repo: RepoPath,
1900 pub id: String,
1901 /// Queue every issue for a g1t agent.
1902 #[serde(default)]
1903 pub assign: bool,
1904 /// The positions, counting from 1, of the issues to open. All of them
1905 /// when absent.
1906 #[serde(default)]
1907 pub keep: Option<Vec<u32>>,
1908}
1909
1910/// `queue_issue`: asks for a g1t agent to take an issue as soon as it can,
1911/// or withdraws that. The author or a member may. Returns `Outcome<bool>`.
1912#[derive(Debug, Serialize, Deserialize)]
1913pub struct QueueIssueArgs {
1914 pub actor: User,
1915 pub repo: RepoPath,
1916 pub number: u32,
1917 pub queued: bool,
1918}
1919
1920/// `ready_issues`: issues waiting for a g1t agent that can be given one
1921/// now, in one repository or in all. Called by the runner service. Returns
1922/// `Vec<ReadyIssue>`.
1923#[derive(Debug, Default, Serialize, Deserialize)]
1924#[serde(rename_all = "camelCase")]
1925pub struct ReadyIssuesArgs {
1926 #[serde(default)]
1927 pub repo_id: Option<String>,
1928}
1929
1930#[derive(Debug, Serialize, Deserialize)]
1931pub struct ReadyIssue {
1932 pub repo: RepoPath,
1933 pub number: u32,
1934 /// Who queued it, on whose say-so the agent works.
1935 pub actor: User,
1936}
1937
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar1938/// `update_pull`: changes who a pull request is assigned to, whose review
1939/// is asked for, its labels, its milestone and the branch it merges into.
1940/// Each list given replaces the whole set. Whoever opened it, or someone
1941/// with the Triage role, may; changing the base needs the Write role.
1942/// Returns `Outcome<Pull>`.
Agents as a team: lifecycle, merge queue, billing and a new shell1943#[derive(Debug, Serialize, Deserialize)]
1944pub struct UpdatePullArgs {
1945 pub actor: User,
1946 pub repo: RepoPath,
1947 pub number: u32,
1948 #[serde(default)]
1949 pub assignees: Option<Vec<String>>,
g1t is one name: its agent's work, commits and comments show as @g1t, and nobody can claim g1t or g1t-agent1950 /// May include `g1t`. Asking for its review does not by itself
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar1951 /// start one; the runner's `review` does. A team is named
1952 /// `workspace/team` (or `@workspace/team`): the whole list, people and
1953 /// teams, replaces who is asked.
Agents as a team: lifecycle, merge queue, billing and a new shell1954 #[serde(default)]
1955 pub reviewers: Option<Vec<String>>,
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar1956 /// Its labels; replaces the whole set, as `set_labels` does.
1957 #[serde(default)]
1958 pub labels: Option<Vec<String>>,
1959 /// The number of the milestone to put it in; 0 takes it out. Needs the
1960 /// Triage role.
1961 #[serde(default)]
1962 pub milestone: Option<u32>,
1963 /// The branch it merges into: an existing branch other than its own.
1964 /// Needs the Write role.
1965 #[serde(default)]
1966 pub base: Option<String>,
Agents as a team: lifecycle, merge queue, billing and a new shell1967}
1968
Fast pages, required checks on the branch, self-hosted runners, honest incidents1969/// `start_checks`: always refused now; a pull request's checks are the
1970/// workflows run on it. Kept so that a runner from before is answered.
1971/// Returns `Outcome<CheckJob>`.
Acceptance checks in sandboxes, line comments and review verdicts1972#[derive(Debug, Serialize, Deserialize)]
1973#[serde(rename_all = "camelCase")]
1974pub struct StartChecksArgs {
1975 pub pull_id: String,
1976}
1977
1978/// What a sandbox needs to carry out a check run.
1979#[derive(Debug, Serialize, Deserialize)]
1980#[serde(rename_all = "camelCase")]
1981pub struct CheckJob {
1982 pub run_id: String,
1983 /// Lets the sandbox, and nothing else, report this run's results.
1984 pub token: String,
1985 pub commands: Vec<String>,
1986 /// The repository holding the commit: the fork, or the repository itself.
1987 pub source: RepoPath,
1988 pub commit: String,
g1t is the stored author of what it opens; the person who asked is requested_by and keeps the author's rights1989 /// Who the pull request is for ([`Pull::owner`]: whoever asked g1t for
1990 /// it, or its author), and so can read its source.
Acceptance checks in sandboxes, line comments and review verdicts1991 pub author: User,
1992 /// Username of whoever wrote the checks: the issue's author.
1993 pub requested_by: String,
1994 pub repo: RepoPath,
1995 pub number: u32,
1996}
1997
1998/// `report_checks`: what a sandbox says about its run. With no results and
1999/// no error it has started. `skip` forgets the run, for one that will not
2000/// be carried out. Returns `Outcome<CheckRun>`.
2001#[derive(Debug, Serialize, Deserialize)]
2002#[serde(rename_all = "camelCase")]
2003pub struct ReportChecksArgs {
2004 pub run_id: String,
2005 pub token: String,
2006 #[serde(default)]
2007 pub results: Vec<CheckResult>,
2008 #[serde(default)]
2009 pub error: Option<String>,
2010 #[serde(default)]
2011 pub skip: bool,
Work service in Rust, with RFC 3339 timestamps2012}
2013
Issues and pull requests replace intents and attempts2014/// A pull request in progress, with where it lives.
2015#[derive(Debug, Serialize, Deserialize)]
2016pub struct ActivePull {
2017 pub pull: Pull,
2018 pub issue: Option<Issue>,
Agents as a team: lifecycle, merge queue, billing and a new shell2019 /// Where it stands, for one g1t is seeing through.
2020 #[serde(default)]
2021 pub lifecycle: Option<Lifecycle>,
Issues and pull requests replace intents and attempts2022}
2023
2024/// `list_active_pulls`: drafts and open pull requests the viewer started,
Agents as a team: lifecycle, merge queue, billing and a new shell2025/// most recently active first. Returns `Vec<ActivePull>`. Also
2026/// `list_assigned_issues`: open issues assigned to the viewer, most
2027/// recently changed first. Returns `Vec<Issue>`.
Work service in Rust, with RFC 3339 timestamps2028#[derive(Debug, Serialize, Deserialize)]
2029pub struct ViewerArgs {
2030 pub viewer: Viewer,
2031}
2032
2033/// `append_session`. Returns `Outcome<Appended>`.
2034#[derive(Debug, Serialize, Deserialize)]
2035pub struct AppendSessionArgs {
2036 pub actor: User,
Issues and pull requests replace intents and attempts2037 pub repo: RepoPath,
2038 pub number: u32,
Work service in Rust, with RFC 3339 timestamps2039 pub entries: Vec<NewSessionEntry>,
2040}
2041
2042#[derive(Debug, Serialize, Deserialize)]
2043pub struct Appended {
2044 pub count: u32,
2045}
Issues and pull requests replace intents and attempts2046
Agents as a team: lifecycle, merge queue, billing and a new shell2047/// `start_review`: begins a review of a pull request by a g1t agent. Called
2048/// by the runner service, which starts the sandbox. Returns
2049/// `Outcome<ReviewJob>`.
2050#[derive(Debug, Serialize, Deserialize)]
2051#[serde(rename_all = "camelCase")]
2052pub struct StartReviewArgs {
2053 pub pull_id: String,
2054}
2055
2056/// What a sandbox needs to review a pull request.
2057#[derive(Debug, Serialize, Deserialize)]
2058#[serde(rename_all = "camelCase")]
2059pub struct ReviewJob {
2060 pub run_id: String,
2061 /// Lets the sandbox, and nothing else, report this review.
2062 pub token: String,
2063 /// The repository holding the commit: the fork, or the repository itself.
2064 pub source: RepoPath,
2065 pub commit: String,
2066 pub repo: RepoPath,
2067 pub default_branch: String,
2068 pub number: u32,
2069 pub title: String,
2070 pub description: String,
2071 /// The issue the pull request is for, which says what it should achieve.
2072 pub issue: Option<Issue>,
g1t is the stored author of what it opens; the person who asked is requested_by and keeps the author's rights2073 /// Who the pull request is for ([`Pull::owner`]: whoever asked g1t for
2074 /// it, or its author), and so can read its source.
Agents as a team: lifecycle, merge queue, billing and a new shell2075 pub author: User,
Auto model routing: the cheapest tier that can do each piece of work, a retry goes up a tier, and each run records its tier2076 /// The files it changes, as of its latest push: how large the change
2077 /// is, which decides the model that reviews it.
2078 #[serde(default)]
2079 pub files: Vec<ChangedFile>,
2080 /// What among them runs, configures or guards things (CI workflows,
2081 /// secrets, infrastructure), once each. Any sends the review to the
2082 /// larger model.
2083 #[serde(default)]
2084 pub sensitive: Vec<String>,
Agents as a team: lifecycle, merge queue, billing and a new shell2085}
2086
2087/// A comment on one line, as a reviewing agent reports it.
2088#[derive(Debug, Serialize, Deserialize)]
2089pub struct ReviewComment {
2090 pub path: String,
2091 #[serde(default)]
2092 pub line: u32,
2093 pub body: String,
2094}
2095
2096/// `report_review`: the review a sandbox's agent wrote, or why it could not
2097/// write one. Returns `Outcome<bool>`.
2098#[derive(Debug, Serialize, Deserialize)]
2099#[serde(rename_all = "camelCase")]
2100pub struct ReportReviewArgs {
2101 pub run_id: String,
2102 pub token: String,
2103 #[serde(default)]
2104 pub verdict: Option<Verdict>,
2105 #[serde(default)]
2106 pub body: String,
2107 #[serde(default)]
2108 pub comments: Vec<ReviewComment>,
2109 /// The model that wrote it, by its public name.
2110 #[serde(default)]
2111 pub model: Option<String>,
2112 #[serde(default)]
2113 pub error: Option<String>,
2114}
2115
Issues and pull requests replace intents and attempts2116/// Lowercases, trims and de-duplicates labels, dropping empty ones.
2117/// Returns `None` if there are too many or one is too long.
2118pub fn normalize_labels(labels: &[String]) -> Option<Vec<String>> {
2119 let mut normalized: Vec<String> = Vec::new();
2120 for label in labels {
2121 let label = label
2122 .split_whitespace()
2123 .collect::<Vec<_>>()
2124 .join(" ")
2125 .to_lowercase();
2126 if label.is_empty() || normalized.contains(&label) {
2127 continue;
2128 }
2129 if label.chars().count() > MAX_LABEL_CHARS {
2130 return None;
2131 }
2132 normalized.push(label);
2133 }
2134 (normalized.len() <= MAX_LABELS).then_some(normalized)
2135}
2136
2137#[cfg(test)]
2138mod tests {
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar2139 use super::*;
Issues and pull requests replace intents and attempts2140
2141 fn labels(names: &[&str]) -> Vec<String> {
2142 names.iter().map(|name| (*name).to_owned()).collect()
2143 }
2144
2145 #[test]
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar2146 fn colors_and_due_dates_are_tidied() {
2147 assert_eq!(tidy_color("#A1B2C3").as_deref(), Some("a1b2c3"));
2148 assert_eq!(tidy_color("fc0").as_deref(), Some("ffcc00"));
2149 assert_eq!(tidy_color("red"), None);
2150 assert_eq!(tidy_color("12345"), None);
2151 assert_eq!(label_color_for("bug"), "d73a4a");
2152 assert_eq!(label_color_for("area: web"), label_color_for("area: web"));
2153 assert_eq!(tidy_due_on("2026-10-14").as_deref(), Some("2026-10-14"));
2154 assert_eq!(tidy_due_on("2026-10-14T00:00:00Z").as_deref(), Some("2026-10-14"));
2155 assert_eq!(tidy_due_on("2026-13-01"), None);
2156 assert_eq!(tidy_due_on("soon"), None);
2157 }
2158
2159 #[test]
2160 fn a_milestone_is_as_far_along_as_its_closed_items() {
2161 let mut milestone: Milestone = serde_json::from_value(serde_json::json!({
2162 "number": 1, "title": "Launch", "state": "open", "createdAt": "", "updatedAt": ""
2163 }))
2164 .unwrap();
2165 assert_eq!(milestone.percent(), 0);
2166 milestone.open_items = 3;
2167 milestone.closed_items = 1;
2168 assert_eq!(milestone.percent(), 25);
2169 }
2170
2171 #[test]
Issues and pull requests replace intents and attempts2172 fn labels_are_lowercased_trimmed_and_unique() {
2173 assert_eq!(
2174 normalize_labels(&labels(&[" Bug ", "bug", "", "Good First Issue"])),
2175 Some(labels(&["bug", "good first issue"]))
2176 );
2177 }
2178
2179 #[test]
2180 fn too_long_or_too_many_labels_are_refused() {
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar2181 assert_eq!(normalize_labels(&["x".repeat(51)]), None);
2182 assert!(normalize_labels(&["x".repeat(50)]).is_some());
2183 let many: Vec<String> = (0..21).map(|i| format!("label-{i}")).collect();
Issues and pull requests replace intents and attempts2184 assert_eq!(normalize_labels(&many), None);
2185 }
2186}
Agents as a team: lifecycle, merge queue, billing and a new shell2187
2188
2189// --- Merge queue ----------------------------------------------------------
2190
2191/// Where a pull request in a merge queue stands.
2192#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
2193#[serde(rename_all = "snake_case")]
2194pub enum QueueState {
2195 /// Waiting for its turn to be tested.
2196 Waiting,
2197 /// Its combined state is being built and checked.
2198 Testing,
2199 /// Its combined state passed; it lands once everything ahead has.
2200 Passed,
2201 /// Its combined state failed, or would not merge. It left the queue.
2202 Failed,
2203 /// On the default branch.
2204 Landed,
2205 /// Taken out of the queue by a person, or closed.
2206 Removed,
2207}
2208
2209impl QueueState {
2210 pub fn as_str(self) -> &'static str {
2211 match self {
2212 QueueState::Waiting => "waiting",
2213 QueueState::Testing => "testing",
2214 QueueState::Passed => "passed",
2215 QueueState::Failed => "failed",
2216 QueueState::Landed => "landed",
2217 QueueState::Removed => "removed",
2218 }
2219 }
2220
2221 /// Still in the queue.
2222 pub fn is_active(self) -> bool {
2223 matches!(
2224 self,
2225 QueueState::Waiting | QueueState::Testing | QueueState::Passed
2226 )
2227 }
2228}
2229
2230/// One pull request's place in a merge queue.
2231#[derive(Clone, Debug, Serialize, Deserialize)]
2232#[serde(rename_all = "camelCase")]
2233pub struct QueueEntry {
2234 pub id: String,
2235 pub number: u32,
2236 pub title: String,
g1t is one name: its agent's work, commits and comments show as @g1t, and nobody can claim g1t or g1t-agent2237 /// Who or what made the pull request, e.g. `g1t`.
Agents as a team: lifecycle, merge queue, billing and a new shell2238 pub agent: String,
2239 pub state: QueueState,
2240 /// The pull requests merged ahead of it in the state being tested, in
2241 /// queue order. Empty when it was tested on the default branch alone.
2242 pub ahead: Vec<u32>,
2243 /// The default branch's commit the tested state was built on.
2244 pub base_commit: Option<String>,
2245 /// The tested state: the default branch with everything ahead and this.
2246 pub combined_commit: Option<String>,
2247 /// Why it failed: a merge conflict or what could not be run.
2248 pub error: Option<String>,
Fast pages, required checks on the branch, self-hosted runners, honest incidents2249 /// Commands run against the tested state by queues from before its
2250 /// checks were workflows. Empty since.
Agents as a team: lifecycle, merge queue, billing and a new shell2251 pub results: Vec<CheckResult>,
2252 /// Username of whoever merged it into the queue: a person, or `g1t`.
2253 pub enqueued_by: String,
2254 /// RFC 3339.
2255 pub created_at: String,
2256 /// RFC 3339. When it landed or left.
2257 pub finished_at: Option<String>,
2258}
2259
2260/// A repository's merge queue: what is in it, in order, and what recently
2261/// left it.
2262#[derive(Clone, Debug, Serialize, Deserialize)]
2263#[serde(rename_all = "camelCase")]
2264pub struct QueueView {
2265 /// Whether the repository merges through the queue.
2266 pub enabled: bool,
2267 pub active: Vec<QueueEntry>,
2268 /// Newest first.
2269 pub recent: Vec<QueueEntry>,
2270}
2271
2272/// `queue`: a repository's merge queue. Returns `Outcome<QueueView>`.
2273#[derive(Debug, Serialize, Deserialize)]
2274pub struct QueueArgs {
2275 pub repo: RepoPath,
2276 pub viewer: Viewer,
2277}
2278
2279/// `queue_build`: the next batch to test for a repository, if nothing is
2280/// being tested now. Returns `Vec<QueueJob>`, one per entry, each testing
2281/// the default branch with that entry and everything ahead of it.
2282#[derive(Debug, Serialize, Deserialize)]
2283#[serde(rename_all = "camelCase")]
2284pub struct QueueBuildArgs {
2285 pub repo_id: String,
2286}
2287
2288/// One pull request in a state being tested: where its change is.
2289#[derive(Clone, Debug, Serialize, Deserialize)]
2290#[serde(rename_all = "camelCase")]
2291pub struct QueueStackItem {
2292 pub number: u32,
2293 pub title: String,
2294 /// The repository holding the change: its fork, or the repository.
2295 pub source: RepoPath,
2296 /// The branch of `source` holding it.
2297 pub branch: String,
2298 pub commit: String,
2299}
2300
2301/// What a sandbox needs to build and check one combined state.
2302#[derive(Clone, Debug, Serialize, Deserialize)]
2303#[serde(rename_all = "camelCase")]
2304pub struct QueueJob {
2305 pub entry_id: String,
2306 /// Lets the sandbox, and nothing else, report this state's result.
2307 pub token: String,
2308 pub repo: RepoPath,
2309 pub default_branch: String,
2310 /// The default branch's commit to build on.
2311 pub base_commit: String,
2312 /// Where to push the tested state, in the repository itself.
2313 pub branch: String,
2314 /// The pull requests to merge in, in order; the last is the entry.
2315 pub stack: Vec<QueueStackItem>,
Fast pages, required checks on the branch, self-hosted runners, honest incidents2316 /// Commands to run on the built state. Always empty: the state is
2317 /// checked by the `merge_group` workflows run on it, and the default
2318 /// branch's required checks must pass there.
Agents as a team: lifecycle, merge queue, billing and a new shell2319 pub checks: Vec<String>,
Fast pages, required checks on the branch, self-hosted runners, honest incidents2320 /// Always empty, as `checks`.
Agents as a team: lifecycle, merge queue, billing and a new shell2321 #[serde(default)]
2322 pub contract_checks: Vec<String>,
2323 /// Who the sandbox acts as: a member who can push the tested state.
2324 pub actor: User,
2325}
2326
2327/// `report_queue`: a sandbox's result for one combined state. Returns
2328/// `Outcome<QueueState>`.
2329#[derive(Debug, Serialize, Deserialize)]
2330#[serde(rename_all = "camelCase")]
2331pub struct ReportQueueArgs {
2332 pub entry_id: String,
2333 pub token: String,
2334 #[serde(default)]
2335 pub combined_commit: Option<String>,
2336 #[serde(default)]
2337 pub results: Vec<CheckResult>,
2338 /// Set when the state could not be built or checked.
2339 #[serde(default)]
2340 pub error: Option<String>,
2341 /// For a merge conflict: the pull request whose change it collided with.
2342 #[serde(default)]
2343 pub conflict_with: Option<u32>,
Agents and memory, checks and conflicts, profiles, slug renames, custom domains2344 /// For a merge conflict: the files that conflicted.
2345 #[serde(default)]
2346 pub conflicts: Vec<String>,
Agents as a team: lifecycle, merge queue, billing and a new shell2347}
Record your own agent's sessions automatically2348
2349
2350/// `locate_pull`: where a pull request lives, by its id, for a tool that
2351/// knows only the fork it is working in (`g1t.sh/pulls/<id>`). Returns
2352/// `Outcome<LocatedPull>`; not found for anyone who cannot see it.
2353#[derive(Debug, Serialize, Deserialize)]
2354pub struct LocatePullArgs {
2355 pub id: String,
2356 pub viewer: Viewer,
2357}
2358
2359#[derive(Clone, Debug, Serialize, Deserialize)]
2360#[serde(rename_all = "camelCase")]
2361pub struct LocatedPull {
2362 pub repo: RepoPath,
2363 pub number: u32,
2364 pub title: String,
2365 pub status: PullStatus,
2366}
Agents and memory, checks and conflicts, profiles, slug renames, custom domains2367
2368// --- A person's work -------------------------------------------------------
2369
2370/// Issues or pull requests, on a person's profile.
2371#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
2372#[serde(rename_all = "lowercase")]
2373pub enum AuthoredKind {
2374 Issue,
2375 Pull,
2376}
2377
2378/// The state filter on a person's work. `Closed` takes in merged pull
2379/// requests too; `Merged` is only those.
2380#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
2381#[serde(rename_all = "lowercase")]
2382pub enum AuthoredState {
2383 Open,
2384 Closed,
2385 Merged,
2386}
2387
2388/// How a person's work is ordered.
2389#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
2390#[serde(rename_all = "lowercase")]
2391pub enum AuthoredSort {
2392 /// Newest first.
2393 #[default]
2394 Created,
2395 /// Most recently changed first.
2396 Updated,
2397 /// Oldest first.
2398 Oldest,
2399}
2400
2401/// The most items one `by_author` page holds.
2402pub const AUTHORED_PAGE: u32 = 25;
2403
2404/// `by_author`: the issues and pull requests a person opened, only on
2405/// repositories `viewer` may read, so a private title never reaches anyone
2406/// who could not open it. Returns `Outcome<Authored>`; not found for an
2407/// account that does not exist.
2408#[derive(Debug, Serialize, Deserialize)]
2409pub struct ByAuthorArgs {
2410 pub username: String,
2411 pub viewer: Viewer,
2412 #[serde(default)]
2413 pub kind: Option<AuthoredKind>,
2414 #[serde(default)]
2415 pub state: Option<AuthoredState>,
2416 /// Only work on this repository: `namespace/name`.
2417 #[serde(default)]
2418 pub repo: Option<String>,
2419 #[serde(default)]
2420 pub sort: AuthoredSort,
2421 /// The `next` of the page before, to read on from there.
2422 #[serde(default)]
2423 pub before: Option<String>,
2424 /// At most [`AUTHORED_PAGE`]; that when absent.
2425 #[serde(default)]
2426 pub limit: Option<u32>,
2427}
2428
2429/// One issue or pull request a person opened.
2430#[derive(Clone, Debug, Serialize, Deserialize)]
2431#[serde(rename_all = "camelCase")]
2432pub struct AuthoredItem {
2433 pub kind: AuthoredKind,
2434 pub repo: RepoPath,
2435 pub number: u32,
2436 pub title: String,
2437 /// Open or closed; a merged pull request is closed.
2438 pub state: State,
2439 /// A pull request's own status.
2440 pub status: Option<PullStatus>,
2441 /// Why an issue was closed.
2442 pub reason: Option<IssueReason>,
2443 pub draft: bool,
2444 pub merged: bool,
2445 /// RFC 3339.
2446 pub created_at: String,
2447 /// RFC 3339.
2448 pub updated_at: String,
2449 /// When a pull request was merged. RFC 3339.
2450 pub merged_at: Option<String>,
2451}
2452
2453/// What a person has done, as far as the viewer may see.
2454#[derive(Clone, Debug, Default, Serialize, Deserialize)]
2455#[serde(rename_all = "camelCase")]
2456pub struct AuthoredCounts {
2457 pub pulls_merged: u32,
2458 pub pulls_open: u32,
2459 pub pulls: u32,
2460 pub issues: u32,
2461 pub issues_open: u32,
2462}
2463
2464/// A repository a person has opened work on, with how much.
2465#[derive(Clone, Debug, Serialize, Deserialize)]
2466pub struct AuthoredRepo {
2467 pub repo: RepoPath,
2468 pub count: u32,
2469}
2470
2471/// A page of a person's work.
2472#[derive(Clone, Debug, Default, Serialize, Deserialize)]
2473#[serde(rename_all = "camelCase")]
2474pub struct Authored {
2475 pub items: Vec<AuthoredItem>,
2476 /// Pass as `before` for the next page; null on the last.
2477 pub next: Option<String>,
2478 /// Over every repository the viewer may read, whatever the filters.
2479 pub counts: AuthoredCounts,
2480 /// Those repositories, most work first.
2481 pub repos: Vec<AuthoredRepo>,
2482}
Fast pages, required checks on the branch, self-hosted runners, honest incidents2483
2484#[cfg(test)]
2485mod required_tests {
2486 use super::*;
2487
2488 fn status(context: &str, state: &str) -> CommitStatus {
2489 CommitStatus {
2490 context: context.into(),
2491 state: state.into(),
2492 description: Some(format!("{context} {state}")),
2493 target_url: None,
2494 updated_at: String::new(),
Merge rulesets: branch and tag rules, agent-first, enforced on push and merge2495 source: None,
Fast pages, required checks on the branch, self-hosted runners, honest incidents2496 }
2497 }
2498
2499 #[test]
2500 fn a_context_names_its_check_and_event() {
2501 assert_eq!(check_name("CI / pull_request"), ("CI", Some("pull_request")));
2502 assert_eq!(check_name("Build and test / merge_group"), ("Build and test", Some("merge_group")));
2503 assert_eq!(check_name("g1t / deploy"), ("g1t / deploy", None));
2504 assert_eq!(check_name("g1t / deploy (docs)"), ("g1t / deploy (docs)", None));
2505 assert_eq!(check_name("lint"), ("lint", None));
2506 }
2507
2508 #[test]
2509 fn required_checks_are_missing_pending_failed_or_passed() {
2510 let required = vec!["CI".to_owned(), "Lint".to_owned(), "g1t / deploy".to_owned(), "Docs".to_owned()];
2511 let statuses = [
2512 status("CI / pull_request", "success"),
2513 status("Lint / pull_request", "pending"),
2514 status("g1t / deploy", "failure"),
2515 ];
2516 let states: Vec<RequiredState> = required_checks(&required, &statuses).into_iter().map(|c| c.state).collect();
2517 assert_eq!(
2518 states,
2519 [RequiredState::Success, RequiredState::Pending, RequiredState::Failure, RequiredState::Expected]
2520 );
2521 }
2522
2523 #[test]
2524 fn any_event_reports_a_check_and_a_failure_wins() {
2525 let required = vec!["ci".to_owned()];
2526 let both = [status("CI / push", "failure"), status("CI / pull_request", "success")];
2527 let check = &required_checks(&required, &both)[0];
2528 assert_eq!(check.state, RequiredState::Failure);
2529 assert_eq!(check.description.as_deref(), Some("CI / push failure"));
2530 let queue = [status("CI / merge_group", "success")];
2531 assert_eq!(required_checks(&required, &queue)[0].state, RequiredState::Success);
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar2532 }
2533
2534 #[test]
2535 fn a_pull_request_merges_into_its_base_or_the_default_branch() {
2536 let mut pull: Pull = serde_json::from_value(serde_json::json!({
2537 "id": "pr_1", "repoId": "rep_1", "number": 14, "issue": null, "title": "Fix it", "body": null,
2538 "agent": "agent", "runtime": "external", "status": "open", "fork": null, "forkRepoId": null,
2539 "branch": "fix", "headCommit": null, "mergeBase": null, "mergedBy": null, "mergedAt": null,
2540 "supersededBy": null, "checkStatus": null, "author": { "id": "x", "username": "x" },
2541 "createdAt": "", "updatedAt": ""
2542 }))
2543 .unwrap();
2544 assert_eq!(pull.base_branch("main"), "main");
2545 assert!(pull.targets_default("main"));
2546 assert_eq!(pull.comparison(&None).base_branch, None);
2547 pull.base = Some("release".into());
2548 assert_eq!(pull.base_branch("main"), "release");
2549 assert!(!pull.targets_default("main"));
2550 assert_eq!(pull.comparison(&None).base_branch.as_deref(), Some("release"));
2551 }
2552
2553 #[test]
Fast pages, required checks on the branch, self-hosted runners, honest incidents2554 fn required_names_are_tidied() {
2555 let names = vec![" CI ".to_owned(), "ci".to_owned(), String::new(), "Lint".to_owned()];
2556 assert_eq!(tidy_required(&names), ["CI", "Lint"]);
2557 }
2558
2559 #[test]
2560 fn a_definition_of_done_is_added_once() {
2561 let items = commands_pass(&["cargo test".to_owned(), " ".to_owned()]);
2562 assert_eq!(items, ["`cargo test` passes."]);
2563 let body = with_definition_of_done("Fix the greeting.", &items);
2564 assert_eq!(body, "Fix the greeting.\n\n## Definition of done\n\n- `cargo test` passes.");
2565 assert_eq!(with_definition_of_done(&body, &items), body);
2566 assert_eq!(with_definition_of_done("", &items), "## Definition of done\n\n- `cargo test` passes.");
2567 assert_eq!(with_definition_of_done(" Text ", &[]), "Text");
2568 }
2569}
g1t is the stored author of what it opens; the person who asked is requested_by and keeps the author's rights2570
2571#[cfg(test)]
2572mod authorship_tests {
2573 use super::*;
2574 use crate::PrincipalKind;
2575 use crate::credentials::{Acting, Principal};
2576 use crate::identity::{AGENT_ID, AgentScope};
2577
2578 fn person() -> User {
2579 User {
2580 id: "usr_1".into(),
2581 username: "syntaqx".into(),
2582 verified: true,
2583 ..User::default()
2584 }
2585 }
2586
2587 fn agent_for(id: &str, username: &str) -> User {
2588 User {
2589 id: AGENT_ID.into(),
2590 username: "g1t".into(),
2591 kind: PrincipalKind::Agent,
2592 acting: Some(Box::new(Acting {
2593 credential_id: "tok_1".into(),
2594 agent: "g1t".into(),
2595 on_behalf_of: Principal { id: id.into(), username: username.into() },
2596 scope: AgentScope {
2597 repo: RepoPath { namespace: "acme".into(), name: "web".into() },
2598 operations: Vec::new(),
2599 run: None,
2600 },
2601 })),
2602 ..User::default()
2603 }
2604 }
2605
2606 fn pull(author: User, requested_by: Option<User>) -> Pull {
2607 let mut pull: Pull = serde_json::from_value(serde_json::json!({
2608 "id": "pr_1", "repoId": "rep_1", "number": 14, "issue": 12, "title": "Fix it", "body": null,
2609 "agent": "g1t", "runtime": "hosted", "status": "open", "fork": null, "forkRepoId": null,
2610 "branch": null, "headCommit": null, "mergeBase": null, "mergedBy": null, "mergedAt": null,
2611 "supersededBy": null, "checkStatus": null,
2612 "author": { "id": "x", "username": "x" },
2613 "createdAt": "", "updatedAt": ""
2614 }))
2615 .unwrap();
2616 pull.author = author;
2617 pull.requested_by = requested_by;
2618 pull
2619 }
2620
2621 #[test]
2622 fn a_change_a_person_has_g1t_make_is_g1t_s_requested_by_them() {
2623 let (author, asked) = authorship(&person(), true);
2624 assert_eq!((author.id.as_str(), author.username.as_str(), author.kind), (AGENT_ID, "g1t", PrincipalKind::Agent));
2625 let asked = asked.expect("the person asked for it");
2626 assert_eq!((asked.id.as_str(), asked.username.as_str(), asked.kind), ("usr_1", "syntaqx", PrincipalKind::User));
2627 }
2628
2629 #[test]
2630 fn what_g1t_s_agent_files_at_work_is_g1t_s_requested_by_whoever_it_works_for() {
2631 let (author, asked) = authorship(&agent_for("usr_1", "syntaqx"), false);
2632 assert_eq!(author.id, AGENT_ID);
2633 assert_eq!(asked.map(|user| user.username), Some("syntaqx".into()));
2634 }
2635
2636 #[test]
2637 fn nobody_asked_for_g1t_s_own_work() {
2638 // A run g1t started itself acts for g1t, not for a person.
2639 let (author, asked) = authorship(&agent_for(crate::system::ID, "g1t"), false);
2640 assert_eq!(author.id, AGENT_ID);
2641 assert!(asked.is_none());
2642 // A security update g1t opens is g1t's own, as before.
2643 let (author, asked) = authorship(&User::system("acme"), true);
2644 assert_eq!((author.id.as_str(), author.kind), (crate::system::ID, PrincipalKind::System));
2645 assert!(asked.is_none());
2646 }
2647
2648 #[test]
2649 fn anyone_else_opens_their_own() {
2650 let (author, asked) = authorship(&person(), false);
2651 assert_eq!((author.id.as_str(), author.kind), ("usr_1", PrincipalKind::User));
2652 assert!(asked.is_none());
2653 // Nothing but who they are is kept.
2654 assert!(author.workspaces.is_empty() && author.acting.is_none());
2655 }
2656
2657 #[test]
2658 fn the_requester_owns_g1t_s_pull_request_and_an_author_their_own() {
2659 let made = pull(g1t_author(), Some(person()));
2660 assert_eq!(made.owner().id, "usr_1");
2661 assert!(made.is_owned_by("usr_1"));
2662 assert!(!made.is_owned_by(AGENT_ID), "g1t's agent does not answer for its own change");
2663 let own = pull(person(), None);
2664 assert_eq!(own.owner().id, "usr_1");
2665 assert!(own.is_owned_by("usr_1"));
2666 }
2667
2668 #[test]
2669 fn requested_by_is_null_when_nobody_asked_and_read_as_absent_from_older_senders() {
2670 let own = pull(person(), None);
2671 let sent = serde_json::to_value(&own).unwrap();
2672 assert!(sent["requestedBy"].is_null());
2673 let mut older = sent.clone();
2674 older.as_object_mut().unwrap().remove("requestedBy");
2675 let read: Pull = serde_json::from_value(older).unwrap();
2676 assert!(read.requested_by.is_none());
2677 }
2678}

This file's history is long; its oldest lines are credited to the oldest commit read.