Skip to content
2,789 linesCodeBlameRaw

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,
Merge Actions: cross-repo workflows and actions, release and deployment triggers, step timeouts779 /// When its text was last edited, RFC 3339; absent if it never was.
780 #[serde(default, skip_serializing_if = "Option::is_none")]
781 pub edited_at: Option<String>,
Issues and pull requests replace intents and attempts782}
783
Work service in Rust, with RFC 3339 timestamps784#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
785#[serde(rename_all = "snake_case")]
786pub enum SessionEntryKind {
787 Prompt,
788 Message,
789 ToolCall,
790 ToolResult,
791 Note,
792}
793
Issues and pull requests replace intents and attempts794/// One step of an agent's session: the "why" behind a pull request's commits.
Work service in Rust, with RFC 3339 timestamps795#[derive(Clone, Debug, Serialize, Deserialize)]
796pub struct SessionEntry {
797 pub seq: u32,
798 pub kind: SessionEntryKind,
799 pub text: String,
800 /// For tool calls and results.
801 pub tool: Option<String>,
802 /// The fork's head commit when this entry was recorded, if known.
803 pub commit: Option<String>,
804 /// RFC 3339.
805 pub at: String,
806}
807
808#[derive(Clone, Debug, Serialize, Deserialize)]
809pub struct NewSessionEntry {
810 pub kind: SessionEntryKind,
811 pub text: String,
812 #[serde(default)]
813 pub tool: Option<String>,
814 #[serde(default)]
815 pub commit: Option<String>,
816}
817
818#[derive(Clone, Debug, Serialize, Deserialize)]
Issues and pull requests replace intents and attempts819pub struct IssueDetail {
820 pub issue: Issue,
821 /// Every pull request made against it, oldest first.
822 pub pulls: Vec<Pull>,
823 pub comments: Vec<Comment>,
Work service in Rust, with RFC 3339 timestamps824}
825
826#[derive(Clone, Debug, Serialize, Deserialize)]
Issues and pull requests replace intents and attempts827pub struct PullDetail {
828 pub pull: Pull,
829 /// The issue it is for, if any.
830 pub issue: Option<Issue>,
831 pub comments: Vec<Comment>,
Fast pages, required checks on the branch, self-hosted runners, honest incidents832 /// The latest record against its head: the merge queue taking it out,
833 /// or (from before checks were workflows) a run of its issue's commands.
Acceptance checks in sandboxes, line comments and review verdicts834 pub checks: Option<CheckRun>,
Agents as a team: lifecycle, merge queue, billing and a new shell835 /// Other pull requests in progress that change the same files.
836 #[serde(default)]
837 pub overlaps: Vec<Overlap>,
838 /// Whether the branch it would merge into has moved on without it, so
839 /// that it has to catch up before it can merge.
840 #[serde(default)]
841 pub behind: bool,
842 /// Whether a g1t agent is reviewing it right now.
843 #[serde(default)]
844 pub review_pending: bool,
845 /// Where it stands on its way to being merged, for a pull request g1t
846 /// is seeing through. Absent on anyone else's.
847 #[serde(default)]
848 pub lifecycle: Option<Lifecycle>,
849 /// A merge was asked for while it was behind: g1t is bringing it up to
850 /// date and will then land it.
851 #[serde(default)]
852 pub landing: bool,
853 /// Why g1t stopped working on it, if it did: a catch-up that could not
854 /// be completed, for example.
855 #[serde(default)]
856 pub stalled: Option<String>,
Usage, like a hosting provider's: what agents cost, per day, task, repository and pull request857 /// Messages people sent the agent while it worked, oldest first.
858 #[serde(default)]
859 pub messages: Vec<AgentMessage>,
GitHub Actions on g1t, part two: running workflows860 /// What workflow runs said about its head commit, one per workflow.
861 #[serde(default)]
862 pub statuses: Vec<CommitStatus>,
Agents and memory, checks and conflicts, profiles, slug renames, custom domains863 /// Whether it merges cleanly into the branch it targets, worked out
864 /// ahead of time whenever either side moves.
865 #[serde(default)]
866 pub mergeable: Mergeable,
867 /// When `mergeable` is `conflicting`: the files that conflict.
868 #[serde(default)]
869 pub conflicts: Vec<String>,
Fast pages, required checks on the branch, self-hosted runners, honest incidents870 /// Earlier records like `checks`, newest first, without their output.
Agents and memory, checks and conflicts, profiles, slug renames, custom domains871 #[serde(default)]
872 pub earlier_checks: Vec<CheckRun>,
Fast pages, required checks on the branch, self-hosted runners, honest incidents873 /// The checks the default branch's protection requires, each as it
874 /// stands on the head commit. Empty when none are required.
875 #[serde(default, alias = "requiredChecks")]
876 pub required_checks: Vec<RequiredCheck>,
Merge rulesets: branch and tag rules, agent-first, enforced on push and merge877 /// The rules of the branch it merges into that it does not meet yet,
878 /// for whoever is looking. Absent while it is not open.
879 #[serde(default, skip_serializing_if = "Option::is_none")]
880 pub rules: Option<crate::rules::MergeRules>,
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar881 /// Who owns the files it changes, from the CODEOWNERS file of the
882 /// branch it merges into, and whose approval is still needed. Absent
883 /// when that branch has no CODEOWNERS file.
884 #[serde(default, alias = "codeOwners")]
885 pub code_owners: Option<crate::codeowners::PullCodeOwners>,
Fast pages, required checks on the branch, self-hosted runners, honest incidents886}
887
888/// Where a required check stands on a commit.
889#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
890#[serde(rename_all = "lowercase")]
891pub enum RequiredState {
892 Success,
893 Failure,
894 /// Reported and still running.
895 Pending,
896 /// Nothing has reported it on this commit yet.
897 Expected,
898}
899
900impl RequiredState {
901 pub fn as_str(self) -> &'static str {
902 match self {
903 RequiredState::Success => "success",
904 RequiredState::Failure => "failure",
905 RequiredState::Pending => "pending",
906 RequiredState::Expected => "expected",
907 }
908 }
909}
910
911/// One check a branch's protection requires, as it stands on a commit.
912#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
913#[serde(rename_all = "camelCase")]
914pub struct RequiredCheck {
915 /// The check's name, such as `CI` or `g1t / deploy`.
916 pub name: String,
917 pub state: RequiredState,
918 /// What the status that decided it says, if one did.
919 #[serde(default)]
920 pub description: Option<String>,
921 /// Where to see more: the workflow run, for one a workflow reported.
922 #[serde(default)]
923 pub target_url: Option<String>,
924}
925
926/// The events a workflow status's context can end in: `CI / pull_request`
927/// is the `CI` check, reported by a run for a `pull_request` event.
928const STATUS_EVENTS: &[&str] = &[
929 "push",
930 "pull_request",
931 "pull_request_target",
932 "pull_request_review",
933 "merge_group",
934 "workflow_dispatch",
935 "workflow_run",
936 "workflow_call",
937 "schedule",
938 "release",
939 "issues",
940 "issue_comment",
941 "repository_dispatch",
942];
943
944/// A status context's check name and the event it was reported for:
945/// `CI / pull_request` is `("CI", Some("pull_request"))`. A context that
946/// does not end in an event, such as `g1t / deploy`, is its own name.
947pub fn check_name(context: &str) -> (&str, Option<&str>) {
948 match context.rsplit_once(" / ") {
949 Some((name, event)) if STATUS_EVENTS.contains(&event) && !name.trim().is_empty() => (name, Some(event)),
950 _ => (context, None),
951 }
952}
953
954/// Where each required check stands among a commit's statuses. A check is
955/// met by any status of that name, for any event: one that failed fails
956/// it, one still running holds it, and with neither, one that passed
957/// passes it. Names compare without regard to case.
958pub fn required_checks(required: &[String], statuses: &[CommitStatus]) -> Vec<RequiredCheck> {
959 required
960 .iter()
961 .map(|name| {
962 let matching: Vec<&CommitStatus> = statuses
963 .iter()
964 .filter(|status| check_name(&status.context).0.eq_ignore_ascii_case(name.trim()))
965 .collect();
966 let failed = matching.iter().find(|s| s.state == "failure" || s.state == "error");
967 let pending = matching.iter().find(|s| s.state == "pending");
968 let passed = matching.iter().find(|s| s.state == "success");
969 let (state, decided) = match (failed, pending, passed) {
970 (Some(status), _, _) => (RequiredState::Failure, Some(*status)),
971 (None, Some(status), _) => (RequiredState::Pending, Some(*status)),
972 (None, None, Some(status)) => (RequiredState::Success, Some(*status)),
973 _ => (RequiredState::Expected, None),
974 };
975 RequiredCheck {
976 name: name.trim().to_owned(),
977 state,
978 description: decided.and_then(|status| status.description.clone()),
979 target_url: decided.and_then(|status| status.target_url.clone()),
980 }
981 })
982 .collect()
983}
984
985/// A repository's required check names, tidied: trimmed, without blanks
986/// or repeats (ignoring case), at most [`MAX_REQUIRED_CHECKS`].
987pub fn tidy_required(names: &[String]) -> Vec<String> {
988 let mut out: Vec<String> = Vec::new();
989 for name in names {
990 let name: String = name.trim().chars().take(MAX_CHECK_NAME_CHARS).collect();
991 if !name.is_empty() && !out.iter().any(|kept| kept.eq_ignore_ascii_case(&name)) {
992 out.push(name);
993 }
994 }
995 out.truncate(MAX_REQUIRED_CHECKS);
996 out
997}
998
999/// The most checks a branch can require, and the longest name of one.
1000pub const MAX_REQUIRED_CHECKS: usize = 20;
1001pub const MAX_CHECK_NAME_CHARS: usize = 100;
1002
1003/// A check name seen on the repository's commits recently, for choosing
1004/// required checks: what reported it, and for which events.
1005#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1006#[serde(rename_all = "camelCase")]
1007pub struct SeenCheck {
1008 pub name: String,
1009 /// The events it was reported for, such as `pull_request`; empty for a
1010 /// status that names none, such as a deployment's.
1011 pub events: Vec<String>,
1012 /// RFC 3339. The latest report.
1013 pub last_seen: String,
Agents and memory, checks and conflicts, profiles, slug renames, custom domains1014}
1015
Fast pages, required checks on the branch, self-hosted runners, honest incidents1016/// `seen_checks`: the check names reported on a repository's commits in
1017/// the last 30 days, most recent first. Returns `Outcome<Vec<SeenCheck>>`.
1018#[derive(Debug, Serialize, Deserialize)]
1019pub struct SeenChecksArgs {
1020 pub repo: RepoPath,
1021 pub viewer: Viewer,
1022}
1023
1024/// The heading an issue's plain-words description of done goes under.
1025pub const DEFINITION_OF_DONE: &str = "## Definition of done";
1026
1027/// An issue's body with `items` added as a bulleted "Definition of done"
1028/// section, for the agent and reviewers to read. Nothing is added when
1029/// `items` is empty, or when the body already has the section, so folding
1030/// the same items twice changes nothing.
1031pub fn with_definition_of_done(body: &str, items: &[String]) -> String {
1032 let items: Vec<&str> = items.iter().map(|item| item.trim()).filter(|item| !item.is_empty()).collect();
1033 let body = body.trim();
1034 if items.is_empty() || body.contains(DEFINITION_OF_DONE) {
1035 return body.to_owned();
1036 }
1037 let list = items.iter().map(|item| format!("- {item}")).collect::<Vec<_>>().join("\n");
1038 if body.is_empty() {
1039 format!("{DEFINITION_OF_DONE}\n\n{list}")
1040 } else {
1041 format!("{body}\n\n{DEFINITION_OF_DONE}\n\n{list}")
1042 }
1043}
1044
1045/// Commands, as items of a definition of done: "`npm test` passes."
1046pub fn commands_pass(commands: &[String]) -> Vec<String> {
1047 commands
1048 .iter()
1049 .map(|command| command.trim())
1050 .filter(|command| !command.is_empty())
1051 .map(|command| format!("`{}` passes.", command.replace('`', "'")))
1052 .collect()
1053}
1054
Agents and memory, checks and conflicts, profiles, slug renames, custom domains1055/// Whether a pull request's change merges cleanly into the branch it
1056/// targets.
1057#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
1058#[serde(rename_all = "lowercase")]
1059pub enum Mergeable {
1060 /// It merges without conflicts.
1061 Clean,
1062 /// Some files conflict: see `PullDetail::conflicts`.
1063 Conflicting,
1064 /// Not known: never worked out, or it could not be.
1065 #[default]
1066 Unknown,
1067 /// Being worked out now.
1068 Checking,
GitHub Actions on g1t, part two: running workflows1069}
1070
Agents and memory, checks and conflicts, profiles, slug renames, custom domains1071impl Mergeable {
1072 pub fn as_str(self) -> &'static str {
1073 match self {
1074 Mergeable::Clean => "clean",
1075 Mergeable::Conflicting => "conflicting",
1076 Mergeable::Unknown => "unknown",
1077 Mergeable::Checking => "checking",
1078 }
1079 }
1080
1081 pub fn parse(value: Option<&str>) -> Mergeable {
1082 match value {
1083 Some("clean") => Mergeable::Clean,
1084 Some("conflicting") => Mergeable::Conflicting,
1085 Some("checking") => Mergeable::Checking,
1086 _ => Mergeable::Unknown,
1087 }
1088 }
1089}
1090
1091/// `start_mergecheck`: claims the probe of whether a pull request merges
1092/// cleanly, which the work service asked for with a `pull.mergecheck`
1093/// event. Called by the runner service, which starts the sandbox. Refused
1094/// when it is no longer wanted, or when the repository already has as many
1095/// probes running as it may. Returns `Outcome<MergecheckJob>`.
1096#[derive(Debug, Serialize, Deserialize)]
1097#[serde(rename_all = "camelCase")]
1098pub struct StartMergecheckArgs {
1099 pub pull_id: String,
1100}
1101
1102/// What a sandbox needs to find out whether a pull request merges cleanly.
1103#[derive(Debug, Serialize, Deserialize)]
1104#[serde(rename_all = "camelCase")]
1105pub struct MergecheckJob {
1106 pub pull_id: String,
1107 /// Lets the sandbox, and nothing else, report this probe.
1108 pub token: String,
1109 pub repo: RepoPath,
1110 pub number: u32,
1111 pub default_branch: String,
1112 /// The default branch's commit to merge into.
1113 pub base: String,
1114 /// The repository holding the change: its fork, or the repository.
1115 pub source: RepoPath,
1116 /// The branch of `source` holding it.
1117 pub branch: String,
1118 /// The change's commit.
1119 pub head: String,
g1t is the stored author of what it opens; the person who asked is requested_by and keeps the author's rights1120 /// Who the pull request is for ([`Pull::owner`]: whoever asked g1t for
1121 /// it, or its author), and so can read its source.
Agents and memory, checks and conflicts, profiles, slug renames, custom domains1122 pub author: User,
1123}
1124
1125/// `report_mergecheck`: what a sandbox found. Returns `Outcome<Mergeable>`.
1126#[derive(Debug, Serialize, Deserialize)]
1127#[serde(rename_all = "camelCase")]
1128pub struct ReportMergecheckArgs {
1129 pub pull_id: String,
1130 pub token: String,
1131 /// The files that conflict; empty when it merges cleanly.
1132 #[serde(default)]
1133 pub conflicts: Vec<String>,
1134 /// Why it could not be found out.
1135 #[serde(default)]
1136 pub error: Option<String>,
1137}
1138
GitHub Actions on g1t, part two: running workflows1139/// What a workflow run (or another tool) says about a commit.
Merge checks: statuses and check runs on every commit1140#[derive(Clone, Debug, Default, Serialize, Deserialize)]
GitHub Actions on g1t, part two: running workflows1141#[serde(rename_all = "camelCase")]
1142pub struct CommitStatus {
1143 /// What reported it, such as `CI / push`.
1144 pub context: String,
1145 /// `pending`, `success`, `failure` or `error`.
1146 pub state: String,
1147 pub description: Option<String>,
1148 /// Where to see more, such as the run's page.
1149 pub target_url: Option<String>,
1150 pub updated_at: String,
Merge rulesets: branch and tag rules, agent-first, enforced on push and merge1151 /// The integration that reported it (`actions`, `deployments`,
1152 /// `security`, `g1t`), when recorded. A required check can insist on
1153 /// one (`rules::RequiredCheck::integration`).
1154 #[serde(default, skip_serializing_if = "Option::is_none")]
1155 pub source: Option<String>,
Merge checks: statuses and check runs on every commit1156 /// Set on the status a check run stands as (see `checks`): the check
1157 /// run's id. Such a status is listed as its check run, not again.
1158 #[serde(default, skip_serializing_if = "Option::is_none")]
1159 pub check_run_id: Option<String>,
GitHub Actions on g1t, part two: running workflows1160}
1161
1162/// `set_commit_status`: for services only. Returns `Outcome<bool>`.
1163#[derive(Debug, Serialize, Deserialize)]
1164#[serde(rename_all = "camelCase")]
1165pub struct SetCommitStatusArgs {
1166 pub repo_id: String,
1167 pub sha: String,
1168 pub context: String,
1169 pub state: String,
1170 #[serde(default)]
1171 pub description: Option<String>,
1172 #[serde(default)]
1173 pub target_url: Option<String>,
Merge rulesets: branch and tag rules, agent-first, enforced on push and merge1174 /// The integration reporting it: `actions`, `deployments`, `security`
1175 /// or `g1t`.
1176 #[serde(default)]
1177 pub source: Option<String>,
Usage, like a hosting provider's: what agents cost, per day, task, repository and pull request1178}
1179
1180/// A message a person sent an agent at work on a pull request. The agent
1181/// receives it at its next step.
1182#[derive(Clone, Debug, Serialize, Deserialize)]
1183#[serde(rename_all = "camelCase")]
1184pub struct AgentMessage {
1185 pub id: String,
1186 pub author: String,
1187 pub body: String,
1188 /// RFC 3339.
1189 pub created_at: String,
1190 /// RFC 3339. When the agent received it; null until then.
1191 pub delivered_at: Option<String>,
Agents ask each other, hand each other work, and answer1192 /// `message` from a person, or from another pull request's agent a
1193 /// `question`, a `handoff` of work, or the `answer` to one.
1194 #[serde(default = "message_kind")]
1195 pub kind: String,
1196 /// The pull request whose agent sent it, when an agent did.
1197 #[serde(default)]
1198 pub from_number: Option<u32>,
1199 /// The pull request it was sent to.
1200 #[serde(default)]
1201 pub to_number: u32,
1202 /// For a question or handoff: the reply, once there is one.
1203 #[serde(default)]
1204 pub answer: Option<String>,
1205 /// For a handoff: whether it was declined.
1206 #[serde(default)]
1207 pub declined: bool,
1208 /// For the agent that sent it: what to expect, when the agent it asked
1209 /// is not at work and will not answer soon.
1210 #[serde(default, skip_serializing_if = "Option::is_none")]
1211 pub hint: Option<String>,
1212}
1213
1214fn message_kind() -> String {
1215 "message".to_owned()
Usage, like a hosting provider's: what agents cost, per day, task, repository and pull request1216}
1217
1218/// `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 rights1219/// The pull request's owner ([`Pull::owner`]: whoever asked g1t for it, or
1220/// its author) and members of the workspace may. Returns
Usage, like a hosting provider's: what agents cost, per day, task, repository and pull request1221/// `Outcome<AgentMessage>`.
1222#[derive(Debug, Serialize, Deserialize)]
1223pub struct MessageAgentArgs {
1224 pub actor: User,
1225 pub repo: RepoPath,
1226 pub number: u32,
1227 pub body: String,
Agents ask each other, hand each other work, and answer1228 /// For an agent: `question` or `handoff`; a person's is a `message`.
1229 #[serde(default)]
1230 pub kind: Option<String>,
1231 /// For an agent: the pull request it is working on, which the reply
1232 /// goes back to.
1233 #[serde(default)]
1234 pub from_number: Option<u32>,
1235}
1236
1237/// `answer_message`: replies to a question or a handoff an agent received,
1238/// accepting or declining a handoff. The reply reaches the asking agent at
1239/// its next step. Returns `Outcome<AgentMessage>`, the message answered.
1240#[derive(Debug, Serialize, Deserialize)]
1241pub struct AnswerMessageArgs {
1242 pub actor: User,
1243 pub repo: RepoPath,
1244 pub id: String,
1245 pub body: String,
1246 #[serde(default)]
1247 pub decline: bool,
Usage, like a hosting provider's: what agents cost, per day, task, repository and pull request1248}
1249
1250/// `take_messages`: the messages not yet delivered to the agent working on
1251/// a pull request, marked delivered. Only g1t's agents may. Returns
1252/// `Outcome<Vec<AgentMessage>>`.
1253#[derive(Debug, Serialize, Deserialize)]
1254pub struct TakeMessagesArgs {
1255 pub actor: User,
1256 pub repo: RepoPath,
1257 pub number: u32,
Agents as a team: lifecycle, merge queue, billing and a new shell1258}
1259
1260/// A step on the way from an assigned issue to a pull request that is ready
1261/// to merge. g1t takes each one without being asked.
1262#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
1263#[serde(rename_all = "snake_case")]
1264pub enum Stage {
1265 /// The agent is making the change.
1266 Working,
Fast pages, required checks on the branch, self-hosted runners, honest incidents1267 /// Waiting for the checks workflows report on its head.
Agents as a team: lifecycle, merge queue, billing and a new shell1268 Checking,
g1t is one name: its agent's work, commits and comments show as @g1t, and nobody can claim g1t or g1t-agent1269 /// g1t is reviewing it.
Agents as a team: lifecycle, merge queue, billing and a new shell1270 Reviewing,
1271 /// The agent is addressing failed checks or a review.
1272 Revising,
1273 /// The agent is merging in the branch it would land on, which moved.
1274 CatchingUp,
Agents asked while not at work are woken to answer1275 /// Woken to answer a question another agent asked it, or a handoff.
1276 Answering,
Agents as a team: lifecycle, merge queue, billing and a new shell1277 /// In the repository's merge queue, being tested with what is ahead of
1278 /// it before it lands.
1279 Queued,
Fast pages, required checks on the branch, self-hosted runners, honest incidents1280 /// Required checks passed, reviewed and approved, up to date. A
1281 /// person merges.
Agents as a team: lifecycle, merge queue, billing and a new shell1282 Ready,
1283 /// g1t has stopped and a person has to decide what happens next.
1284 NeedsYou,
1285}
1286
1287/// Where a pull request made by a g1t agent stands. See [`Stage`].
1288#[derive(Clone, Debug, Serialize, Deserialize)]
1289pub struct Lifecycle {
1290 pub stage: Stage,
1291 /// One sentence saying what is happening, or why it stopped.
1292 pub detail: String,
1293 /// How many times the agent has been sent back to revise it.
1294 pub revisions: u32,
1295}
1296
1297/// `advance`: works out the next step for a pull request g1t is seeing
1298/// through and, if there is one to take now, claims it, so that it is
1299/// taken once however many times this is called. Called by the runner
1300/// service, which carries the step out. Returns `Advance`.
1301#[derive(Debug, Serialize, Deserialize)]
1302#[serde(rename_all = "camelCase")]
1303pub struct AdvanceArgs {
1304 pub pull_id: String,
1305}
1306
1307#[derive(Debug, Serialize, Deserialize)]
1308#[serde(tag = "action", rename_all = "snake_case")]
1309pub enum Advance {
1310 /// Nothing to do now: a step is under way, or it is a person's turn.
1311 None,
1312 /// Have a g1t agent review it.
1313 Review { job: LifecycleJob },
1314 /// Send the agent back to address `job.feedback`.
1315 Revise { job: LifecycleJob },
1316 /// Merge in the branch it would land on.
1317 CatchUp { job: LifecycleJob },
Work service in Rust, with RFC 3339 timestamps1318}
1319
Agents as a team: lifecycle, merge queue, billing and a new shell1320/// What the runner needs to carry out a step of a pull request's lifecycle.
1321#[derive(Debug, Serialize, Deserialize)]
1322#[serde(rename_all = "camelCase")]
1323pub struct LifecycleJob {
1324 pub pull_id: String,
1325 pub repo: RepoPath,
1326 pub number: u32,
g1t is the stored author of what it opens; the person who asked is requested_by and keeps the author's rights1327 /// Who the pull request belongs to ([`Pull::owner`]: whoever asked g1t
1328 /// for it, or its author). Sandboxes act as them.
Agents as a team: lifecycle, merge queue, billing and a new shell1329 pub author: User,
1330 /// The repository holding the change: its fork, or the repository
1331 /// itself for one made on a branch.
1332 pub source: RepoPath,
1333 /// The branch of the source holding the change; its default branch
1334 /// when absent.
1335 #[serde(default)]
1336 pub branch: Option<String>,
1337 pub default_branch: String,
1338 pub title: String,
1339 pub description: String,
1340 pub issue: Option<Issue>,
1341 /// For a revision: the failed checks or the review to address.
1342 pub feedback: String,
1343 /// For a revision: which one this is, from 1.
1344 pub round: u32,
1345}
1346
1347/// How a repository wants its pull requests handled. A repository that has
1348/// changed nothing has the defaults.
1349#[derive(Clone, Debug, Serialize, Deserialize)]
1350#[serde(rename_all = "camelCase", default)]
1351pub struct RepoSettings {
1352 /// 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 incidents1353 /// required checks passed and approved as the settings below require.
Agents as a team: lifecycle, merge queue, billing and a new shell1354 pub auto_merge: bool,
Fast pages, required checks on the branch, self-hosted runners, honest incidents1355 /// The checks that must pass on a pull request's head before it may
1356 /// merge into the default branch, by name: a workflow's name (`CI`), or
1357 /// the context of another status (`g1t / deploy`). The same for a
1358 /// person's pull request and an agent's, and for the merge queue.
1359 pub required_checks: Vec<String>,
Agents as a team: lifecycle, merge queue, billing and a new shell1360 /// Refuse to merge a pull request that does not contain the default
1361 /// branch's latest commits, so that what merges is what was checked.
1362 /// When off, merging one that is behind brings it up to date first.
1363 pub require_up_to_date: bool,
1364 /// How many approving reviews a pull request needs before it may
1365 /// merge. A reviewer who has since asked for changes blocks it.
1366 pub required_approvals: u32,
1367 /// Whether a g1t agent's approval counts towards `required_approvals`.
1368 pub count_agent_approvals: bool,
Fast pages, required checks on the branch, self-hosted runners, honest incidents1369 /// Whether someone who may merge can bypass required checks that have
1370 /// not passed, by saying so as they merge.
Agents as a team: lifecycle, merge queue, billing and a new shell1371 pub allow_ignoring_checks: bool,
1372 /// Whether a g1t agent's pull request is reviewed by a second agent
1373 /// without being asked.
1374 pub agent_review: bool,
1375 /// How many times a g1t agent is sent back to its pull request before
1376 /// a person is asked instead.
1377 pub max_revisions: u32,
1378 /// Merge through a queue: pull requests are tested together with those
1379 /// ahead of them, and only a combination that passed reaches the default
1380 /// branch.
1381 pub merge_queue: bool,
Thirteen MCP tools and classic token scopes; agents rate their confidence and can be put on an issue in one step1382 /// Ask a person before merging a g1t agent's change whose confidence is
1383 /// low: auto-merge and the merge queue leave it, and it needs someone,
1384 /// until a person approves it.
1385 pub hold_low_confidence: bool,
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar1386 /// Refuse to merge until the code owners of every file it changes
1387 /// (its CODEOWNERS file) have approved, as many as each section asks.
1388 /// Only people count, and `g1t` only where the file names `@g1t`.
1389 pub require_code_owner_review: bool,
Agents as a team: lifecycle, merge queue, billing and a new shell1390 /// Username of the member who last changed the settings, if anyone has.
1391 pub updated_by: Option<String>,
1392 /// RFC 3339.
1393 pub updated_at: Option<String>,
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar1394}
1395
Agents as a team: lifecycle, merge queue, billing and a new shell1396impl Default for RepoSettings {
1397 fn default() -> Self {
1398 RepoSettings {
1399 auto_merge: false,
Fast pages, required checks on the branch, self-hosted runners, honest incidents1400 required_checks: Vec::new(),
Agents as a team: lifecycle, merge queue, billing and a new shell1401 require_up_to_date: false,
1402 required_approvals: 0,
1403 count_agent_approvals: true,
1404 allow_ignoring_checks: true,
1405 agent_review: true,
1406 max_revisions: 2,
1407 merge_queue: false,
Thirteen MCP tools and classic token scopes; agents rate their confidence and can be put on an issue in one step1408 hold_low_confidence: true,
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar1409 require_code_owner_review: false,
Agents as a team: lifecycle, merge queue, billing and a new shell1410 updated_by: None,
1411 updated_at: None,
1412 }
1413 }
1414}
1415
1416/// `update_settings`: replaces a repository's settings. Members of its
1417/// workspace only. Returns `Outcome<RepoSettings>`. `get_settings` takes
1418/// `ViewArgs` and returns the same.
1419#[derive(Debug, Serialize, Deserialize)]
1420pub struct UpdateSettingsArgs {
1421 pub actor: User,
1422 pub repo: RepoPath,
1423 /// Who changed them and when are filled in by the service.
1424 pub settings: RepoSettings,
1425}
1426
1427/// `catch_up_job`: what the runner needs to bring a pull request up to date
1428/// because a merge of it was asked for. Null if none was. Returns
1429/// `Option<LifecycleJob>`.
1430#[derive(Debug, Serialize, Deserialize)]
1431#[serde(rename_all = "camelCase")]
1432pub struct CatchUpJobArgs {
1433 pub pull_id: String,
1434}
1435
Agents asked while not at work are woken to answer1436/// `wake_for_messages`: the agent on a pull request was asked a question
1437/// or handed work while it was not at work. Claims a short step for it to
1438/// answer, and hands over what it was sent, marked read. Null when there
1439/// is nothing waiting, or the pull request cannot take a step now.
1440/// Returns `Option<Wake>`.
1441#[derive(Debug, Serialize, Deserialize)]
1442#[serde(rename_all = "camelCase")]
1443pub struct WakeForMessagesArgs {
1444 pub pull_id: String,
1445}
1446
1447/// What an agent woken to answer needs: its pull request, and what it was
1448/// sent, oldest first.
1449#[derive(Debug, Serialize, Deserialize)]
1450#[serde(rename_all = "camelCase")]
1451pub struct Wake {
1452 pub job: LifecycleJob,
1453 pub messages: Vec<AgentMessage>,
1454}
1455
Agents as a team: lifecycle, merge queue, billing and a new shell1456/// `stall`: records that a step could not be carried out, so that g1t
1457/// stops and a person is asked. Returns `bool`.
1458#[derive(Debug, Serialize, Deserialize)]
1459#[serde(rename_all = "camelCase")]
1460pub struct StallArgs {
1461 pub pull_id: String,
1462 pub reason: String,
Events: review requests, assignments, stops and deployments are published1463 /// The person who stopped it, by id, when someone did: they are not
1464 /// told it needs them.
1465 #[serde(default)]
1466 pub by: Option<String>,
Agents as a team: lifecycle, merge queue, billing and a new shell1467}
1468
1469/// `managed_pulls`: ids of the open pull requests g1t is seeing through,
1470/// in one repository or in all of them. Returns `Vec<String>`.
1471#[derive(Debug, Default, Serialize, Deserialize)]
1472#[serde(rename_all = "camelCase")]
1473pub struct ManagedPullsArgs {
1474 #[serde(default)]
1475 pub repo_id: Option<String>,
1476}
1477
Issues and pull requests replace intents and attempts1478/// `open_issue`. Returns `Outcome<Issue>`.
Work service in Rust, with RFC 3339 timestamps1479#[derive(Debug, Serialize, Deserialize)]
Issues and pull requests replace intents and attempts1480pub struct OpenIssueArgs {
Work service in Rust, with RFC 3339 timestamps1481 pub actor: User,
1482 pub repo: RepoPath,
1483 pub title: String,
1484 #[serde(default)]
Issues and pull requests replace intents and attempts1485 pub body: String,
1486 #[serde(default)]
1487 pub labels: Vec<String>,
Fast pages, required checks on the branch, self-hosted runners, honest incidents1488 /// Deprecated: commands, added to the body under "Definition of done".
1489 /// Checks are the workflows the branch's protection requires.
Work service in Rust, with RFC 3339 timestamps1490 #[serde(default)]
1491 pub checks: Vec<String>,
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar1492 /// The number of the milestone to put it in. Needs the Triage role.
1493 #[serde(default)]
1494 pub milestone: Option<u32>,
Work service in Rust, with RFC 3339 timestamps1495}
1496
g1t is one name: its agent's work, commits and comments show as @g1t, and nobody can claim g1t or g1t-agent1497/// `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 step1498/// before anything is opened unless `actor` may put agents to work in the
1499/// repository (Run, which the Write role has). The runner service's
1500/// `delegate` calls it and then starts the agent. Returns `Outcome<Issue>`.
1501#[derive(Debug, Serialize, Deserialize)]
1502pub struct DelegateIssueArgs {
1503 pub actor: User,
1504 pub repo: RepoPath,
1505 pub title: String,
1506 #[serde(default)]
1507 pub body: String,
1508 #[serde(default)]
1509 pub labels: Vec<String>,
Fast pages, required checks on the branch, self-hosted runners, honest incidents1510 /// Deprecated, as on `OpenIssueArgs`.
Thirteen MCP tools and classic token scopes; agents rate their confidence and can be put on an issue in one step1511 #[serde(default)]
1512 pub checks: Vec<String>,
1513}
1514
1515/// What became of the agent when an issue was opened and handed to it in
1516/// one step.
1517#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
1518#[serde(rename_all = "snake_case")]
1519pub enum AgentStartStatus {
1520 /// It is at work on the issue's pull request.
1521 Started,
1522 /// Every agent slot of the workspace is busy: it starts on its own when
1523 /// one frees up.
1524 Queued,
1525 /// It did not start, and will not until someone fixes what `code` says.
1526 NotStarted,
1527}
1528
1529/// Whether the agent started, and if not, why and what fixes it.
1530#[derive(Clone, Debug, Serialize, Deserialize)]
1531#[serde(rename_all = "camelCase")]
1532pub struct AgentStart {
1533 pub status: AgentStartStatus,
1534 /// Why it did not start: `not_paid`, `trial_used`, `limit`, `paused`,
1535 /// `issue_cap`, `billing_unavailable` or `no_model`; `waiting` when
1536 /// queued.
1537 #[serde(default)]
1538 pub code: Option<String>,
1539 /// What happened, in a sentence or two, with what to do.
1540 #[serde(default)]
1541 pub message: Option<String>,
1542 /// Where the fix is: the workspace's billing or model settings.
1543 #[serde(default)]
1544 pub fix_url: Option<String>,
1545}
1546
1547/// The runner service's `delegate`: the issue opened, and the agent put on
1548/// it. The issue exists whatever became of the agent.
1549#[derive(Clone, Debug, Serialize, Deserialize)]
1550pub struct Delegated {
1551 pub issue: Issue,
1552 /// The pull request the agent opened, when it started.
1553 #[serde(default)]
1554 pub pull: Option<Pull>,
1555 pub agent: AgentStart,
1556}
1557
1558/// `report_confidence`: what the agent of a run says of its own change,
1559/// with the run's own token. Kept with the run, and the pull request's
1560/// confidence is worked out again with it. Returns `Outcome<bool>`.
1561#[derive(Debug, Serialize, Deserialize)]
1562#[serde(rename_all = "camelCase")]
1563pub struct ReportConfidenceArgs {
1564 pub run_id: String,
1565 pub token: String,
1566 /// `high`, `medium` or `low`.
1567 pub confidence: String,
1568 #[serde(default)]
1569 pub uncertain_about: Vec<String>,
1570}
1571
Issues and pull requests replace intents and attempts1572/// `list_issues`, newest first. Returns `Outcome<Vec<Issue>>`.
Work service in Rust, with RFC 3339 timestamps1573#[derive(Debug, Serialize, Deserialize)]
Issues and pull requests replace intents and attempts1574pub struct ListIssuesArgs {
Work service in Rust, with RFC 3339 timestamps1575 pub repo: RepoPath,
1576 pub viewer: Viewer,
1577 #[serde(default)]
Issues and pull requests replace intents and attempts1578 pub state: Option<State>,
1579 /// Only issues carrying this label.
1580 #[serde(default)]
1581 pub label: Option<String>,
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar1582 /// Only issues in the milestone of this number.
1583 #[serde(default)]
1584 pub milestone: Option<u32>,
Work service in Rust, with RFC 3339 timestamps1585}
1586
Issues and pull requests replace intents and attempts1587/// `list_pulls`, newest first. Returns `Outcome<Vec<Pull>>`.
Work service in Rust, with RFC 3339 timestamps1588#[derive(Debug, Serialize, Deserialize)]
Issues and pull requests replace intents and attempts1589pub struct ListPullsArgs {
1590 pub repo: RepoPath,
1591 pub viewer: Viewer,
1592 #[serde(default)]
1593 pub state: Option<State>,
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar1594 /// Only pull requests carrying this label.
1595 #[serde(default)]
1596 pub label: Option<String>,
1597 /// Only pull requests in the milestone of this number.
1598 #[serde(default)]
1599 pub milestone: Option<u32>,
1600 /// Only pull requests into this branch.
1601 #[serde(default)]
1602 pub base: Option<String>,
Issues and pull requests replace intents and attempts1603}
1604
Fast pages, required checks on the branch, self-hosted runners, honest incidents1605/// `pulls_for_repos`: the newest open and the newest closed pull requests
1606/// of many repositories, in one call, for pages that show several projects
1607/// at once. Repositories the viewer cannot read are left out, as are forks
1608/// (ask those with `list_pulls`). Returns `Vec<RepoPulls>`.
1609#[derive(Debug, Serialize, Deserialize)]
1610#[serde(rename_all = "camelCase")]
1611pub struct PullsForReposArgs {
1612 /// At most [`MAX_PULLS_FOR_REPOS`] are looked at.
1613 pub repo_ids: Vec<String>,
1614 pub viewer: Viewer,
1615 /// How many of each, open and closed, per repository (at most 100).
1616 pub limit: u32,
1617}
1618
1619/// The most repositories one `pulls_for_repos` call looks at.
1620pub const MAX_PULLS_FOR_REPOS: usize = 50;
1621
1622/// One repository's pull requests from `pulls_for_repos`, newest first.
1623#[derive(Debug, Serialize, Deserialize)]
1624#[serde(rename_all = "camelCase")]
1625pub struct RepoPulls {
1626 pub repo_id: String,
1627 /// Draft and open.
1628 pub open: Vec<Pull>,
1629 /// Merged and closed.
1630 pub closed: Vec<Pull>,
1631}
1632
Issues and pull requests replace intents and attempts1633/// `get_issue` (`Outcome<IssueDetail>`), `get_pull` (`Outcome<PullDetail>`),
1634/// `read_session` (`Outcome<Vec<SessionEntry>>`), `list_labels`
1635/// (`Outcome<Vec<String>>`) and `counts` (`Outcome<Counts>`). The last two
1636/// ignore `number`.
1637#[derive(Debug, Serialize, Deserialize)]
1638#[serde(rename_all = "camelCase")]
1639pub struct ViewArgs {
Work service in Rust, with RFC 3339 timestamps1640 pub repo: RepoPath,
Issues and pull requests replace intents and attempts1641 #[serde(default)]
Work service in Rust, with RFC 3339 timestamps1642 pub number: u32,
1643 pub viewer: Viewer,
Issues and pull requests replace intents and attempts1644 /// For `read_session`: only entries after this sequence number.
1645 #[serde(default)]
1646 pub after_seq: u32,
1647}
1648
1649/// How many issues and pull requests are open on a repository.
1650#[derive(Debug, Serialize, Deserialize)]
1651pub struct Counts {
1652 pub issues: u32,
1653 pub pulls: u32,
1654}
1655
1656/// `update_issue`: changes whichever fields are given. The author or a
1657/// member of the workspace may. Returns `Outcome<Issue>`.
1658#[derive(Debug, Serialize, Deserialize)]
1659pub struct UpdateIssueArgs {
1660 pub actor: User,
1661 pub repo: RepoPath,
1662 pub number: u32,
1663 #[serde(default)]
1664 pub title: Option<String>,
1665 #[serde(default)]
1666 pub body: Option<String>,
1667 #[serde(default)]
1668 pub labels: Option<Vec<String>>,
Agents as a team: lifecycle, merge queue, billing and a new shell1669 /// 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-agent1670 /// Assigning it to g1t is the runner's `run`, not this.
Agents as a team: lifecycle, merge queue, billing and a new shell1671 #[serde(default)]
1672 pub assignees: Option<Vec<String>>,
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar1673 /// The number of the milestone to put it in; 0 takes it out of its
1674 /// milestone. Needs the Triage role.
1675 #[serde(default)]
1676 pub milestone: Option<u32>,
Work service in Rust, with RFC 3339 timestamps1677}
1678
Issues and pull requests replace intents and attempts1679/// `close_issue` and `reopen_issue`. Each returns `Outcome<Issue>`.
Work service in Rust, with RFC 3339 timestamps1680#[derive(Debug, Serialize, Deserialize)]
Issues and pull requests replace intents and attempts1681pub struct IssueActionArgs {
Work service in Rust, with RFC 3339 timestamps1682 pub actor: User,
Issues and pull requests replace intents and attempts1683 pub repo: RepoPath,
1684 pub number: u32,
1685 /// For `close_issue`; `completed` if left out.
1686 #[serde(default)]
1687 pub reason: Option<IssueReason>,
Work service in Rust, with RFC 3339 timestamps1688}
1689
Acceptance checks in sandboxes, line comments and review verdicts1690/// `add_comment`, on an issue or a pull request. On a pull request it may
1691/// name a line of the change, and may carry a verdict; nobody can give a
1692/// verdict on their own pull request. Returns `Outcome<Comment>`.
Work service in Rust, with RFC 3339 timestamps1693#[derive(Debug, Serialize, Deserialize)]
Issues and pull requests replace intents and attempts1694pub struct AddCommentArgs {
Work service in Rust, with RFC 3339 timestamps1695 pub actor: User,
Issues and pull requests replace intents and attempts1696 pub repo: RepoPath,
1697 pub number: u32,
Acceptance checks in sandboxes, line comments and review verdicts1698 /// May be empty when approving.
1699 #[serde(default)]
Issues and pull requests replace intents and attempts1700 pub body: String,
Acceptance checks in sandboxes, line comments and review verdicts1701 #[serde(default)]
1702 pub path: Option<String>,
1703 #[serde(default)]
1704 pub line: Option<u32>,
1705 #[serde(default)]
1706 pub verdict: Option<Verdict>,
Work service in Rust, with RFC 3339 timestamps1707}
1708
Merge Actions: cross-repo workflows and actions, release and deployment triggers, step timeouts1709/// `edit_comment` and `delete_comment`: change the text of a comment on an
1710/// issue or a pull request, or delete it. Its author may, and so may anyone
1711/// with the Maintain role or higher ([`may_change_comment`]). Notes of what
1712/// happened (`CommentKind::Event`) cannot be changed, and a review that
1713/// gave a verdict cannot be deleted, only edited. `edit_comment` returns
1714/// `Outcome<Comment>`; `delete_comment` returns `Outcome<()>`.
1715#[derive(Debug, Serialize, Deserialize)]
1716#[serde(rename_all = "camelCase")]
1717pub struct CommentActionArgs {
1718 pub actor: User,
1719 pub repo: RepoPath,
1720 pub comment_id: String,
1721 /// For `edit_comment`: the new text, in Markdown.
1722 #[serde(default)]
1723 pub body: String,
1724}
1725
1726/// Why `actor_id` may not edit (or, with `deleting`, delete) a comment by
1727/// `author_id`, or none when they may: its author may, and so may someone
1728/// who `maintains` the repository (the Maintain role or higher). A note of
1729/// something that happened is never changed, and a review's verdict stays,
1730/// so a comment that gave one can be edited but not deleted.
1731pub fn may_change_comment(
1732 kind: CommentKind,
1733 has_verdict: bool,
1734 author_id: &str,
1735 actor_id: &str,
1736 maintains: bool,
1737 deleting: bool,
1738) -> Result<(), &'static str> {
1739 if kind == CommentKind::Event {
1740 return Err("That is a record of something that happened, not a comment; it cannot be changed.");
1741 }
1742 if author_id != actor_id && !maintains {
1743 return Err(if deleting {
1744 "Only its author, or someone with the Maintain role or higher, can delete this comment."
1745 } else {
1746 "Only its author, or someone with the Maintain role or higher, can edit this comment."
1747 });
1748 }
1749 if deleting && has_verdict {
1750 return Err("A review cannot be deleted. Edit its text instead.");
1751 }
1752 Ok(())
1753}
1754
Pull requests from branches1755/// `open_pull`. Without `branch`, forks the repo and returns a draft pull
1756/// request to push to. With it, opens a pull request, ready for review,
1757/// for a branch already pushed to the repo. Returns `Outcome<Pull>`.
Work service in Rust, with RFC 3339 timestamps1758#[derive(Debug, Serialize, Deserialize)]
Issues and pull requests replace intents and attempts1759pub struct OpenPullArgs {
1760 pub actor: User,
1761 pub repo: RepoPath,
1762 /// The issue this is for.
Work service in Rust, with RFC 3339 timestamps1763 #[serde(default)]
Issues and pull requests replace intents and attempts1764 pub issue: Option<u32>,
1765 /// Defaults to the issue's title; required without an issue.
1766 #[serde(default)]
1767 pub title: String,
Pull requests from branches1768 /// What changed and why. Usually set later, when a draft is marked ready.
1769 #[serde(default)]
1770 pub body: String,
1771 /// A branch of the repository that already holds the change.
1772 #[serde(default)]
1773 pub branch: Option<String>,
Issues and pull requests replace intents and attempts1774 #[serde(default)]
1775 pub agent: String,
1776 pub runtime: Runtime,
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar1777 /// The branch to merge into: the default branch when absent.
1778 #[serde(default)]
1779 pub base: Option<String>,
Open a pull request as a draft: it can't merge, and agents' review routines wait, until it is marked ready1780 /// Opened from a branch as a draft, still being worked on: it can't
1781 /// merge, and agents' review routines wait, until it is marked ready.
1782 #[serde(default)]
1783 pub draft: bool,
Work service in Rust, with RFC 3339 timestamps1784}
1785
Merge Actions: cross-repo workflows and actions, release and deployment triggers, step timeouts1786/// `ready_pull`, `close_pull`, `reopen_pull`, `convert_pull_to_draft` and
1787/// `merge_pull`. Each returns `Outcome<Pull>`. `reopen_pull` opens a closed
1788/// pull request again (never a merged one), as the draft it was if it was
1789/// closed as one; `convert_pull_to_draft` turns one that is open back into
1790/// a draft, which leaves the merge queue.
Catching up with main takes seconds when the two sides touched different files1791///
1792/// Also `catch_up_pull`: brings the pull request up to date with the
1793/// default branch without a sandbox where that is safe, as the repos
1794/// 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 rights1795/// update it: its owner ([`Pull::owner`]) for a fork, any member for a
1796/// branch.
Catching up with main takes seconds when the two sides touched different files1797/// Returns `Outcome<repos::PullBranchUpdate>`; on `needs_agent` nothing was
1798/// pushed and the runner's `update` is the way on.
Work service in Rust, with RFC 3339 timestamps1799#[derive(Debug, Serialize, Deserialize)]
1800#[serde(rename_all = "camelCase")]
Issues and pull requests replace intents and attempts1801pub struct PullActionArgs {
Work service in Rust, with RFC 3339 timestamps1802 pub actor: User,
Issues and pull requests replace intents and attempts1803 pub repo: RepoPath,
1804 pub number: u32,
1805 /// For `ready_pull`: what changed and why.
Work service in Rust, with RFC 3339 timestamps1806 #[serde(default)]
1807 pub summary: String,
Issues and pull requests replace intents and attempts1808 /// For `merge_pull`: leave the issue open and the other pull requests
1809 /// for it untouched, because this one is only part of the work.
1810 #[serde(default)]
1811 pub keep_issue_open: bool,
Fast pages, required checks on the branch, self-hosted runners, honest incidents1812 /// For `merge_pull`: merge although required checks have not passed,
1813 /// where the repository lets members bypass them.
Acceptance checks in sandboxes, line comments and review verdicts1814 #[serde(default)]
1815 pub ignore_checks: bool,
Merge rulesets: branch and tag rules, agent-first, enforced on push and merge1816 /// For `merge_pull`: merge although rules are not met, where a ruleset
1817 /// lists the actor as one who may bypass it. Recorded as a bypass.
1818 #[serde(default)]
1819 pub bypass_rules: bool,
Acceptance checks in sandboxes, line comments and review verdicts1820}
1821
Agents as a team: lifecycle, merge queue, billing and a new shell1822/// Where a plan stands.
1823#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
1824#[serde(rename_all = "lowercase")]
1825pub enum PlanStatus {
1826 /// An agent is reading the repository and writing it.
1827 Planning,
1828 /// Written, and waiting for a person to read and apply it.
1829 Ready,
1830 /// It could not be written.
1831 Failed,
1832 /// Its issues have been opened.
1833 Applied,
1834}
1835
1836/// One issue a plan proposes.
1837#[derive(Clone, Debug, Default, Serialize, Deserialize)]
1838#[serde(rename_all = "camelCase", default)]
1839pub struct PlannedIssue {
1840 pub title: String,
1841 /// Markdown: what to change, where, and why.
1842 pub body: String,
1843 pub labels: Vec<String>,
Fast pages, required checks on the branch, self-hosted runners, honest incidents1844 /// What is true once it is done, in plain words. Added to the issue's
1845 /// body under "Definition of done". Plans written before this was
1846 /// called `done` named it `checks`.
1847 #[serde(alias = "checks")]
1848 pub done: Vec<String>,
Agents as a team: lifecycle, merge queue, billing and a new shell1849 /// The files it will most likely change.
1850 pub files: Vec<String>,
1851 /// The positions, counting from 1, of earlier issues in the plan that
1852 /// have to be merged first. An agent writes this as `depends_on`.
1853 #[serde(alias = "depends_on")]
1854 pub depends_on: Vec<u32>,
1855 /// Its number, once the plan has been applied and it was kept.
1856 pub number: Option<u32>,
1857}
1858
1859/// An outcome someone wrote, and the issues an agent proposes to get there.
1860#[derive(Clone, Debug, Serialize, Deserialize)]
1861#[serde(rename_all = "camelCase")]
1862pub struct Plan {
1863 pub id: String,
1864 pub repo_id: String,
1865 /// The outcome wanted, as written.
1866 pub brief: String,
1867 pub status: PlanStatus,
1868 /// The agent's account of how it split the work.
1869 pub summary: String,
1870 pub issues: Vec<PlannedIssue>,
1871 /// Why it could not be written, when `status` is `failed`.
1872 pub error: Option<String>,
1873 pub author: User,
1874 /// RFC 3339.
1875 pub created_at: String,
1876 /// RFC 3339.
1877 pub finished_at: Option<String>,
Dark gray base with lavender as an accent, and a live outcome view for plans1878 /// Once applied: where each issue it opened stands now, in plan order.
1879 /// Filled in by `get_plan` only.
1880 #[serde(default)]
1881 pub progress: Vec<IssueProgress>,
Agents ask each other, hand each other work, and answer1882 /// Questions and handoffs between the agents on its pull requests,
1883 /// newest first. Filled in by `get_plan` only.
1884 #[serde(default)]
1885 pub exchanges: Vec<AgentMessage>,
Dark gray base with lavender as an accent, and a live outcome view for plans1886}
1887
1888/// Where one issue of an applied plan stands.
1889#[derive(Clone, Debug, Serialize, Deserialize)]
1890#[serde(rename_all = "camelCase")]
1891pub struct IssueProgress {
1892 pub number: u32,
1893 pub title: String,
1894 /// `blocked` (waiting on issues it depends on), `waiting` (for an
1895 /// agent), `open` (nobody on it), one of the lifecycle stages
1896 /// (`working`, `checking`, `reviewing`, `revising`, `catching_up`,
Agents asked while not at work are woken to answer1897 /// `answering`, `queued`, `ready`, `needs_you`), `landed` or `closed`.
Dark gray base with lavender as an accent, and a live outcome view for plans1898 pub state: String,
1899 /// One sentence about where it stands.
1900 pub detail: String,
1901 /// The issues it is waiting on that are still open.
1902 pub blocked_by: Vec<u32>,
1903 /// The pull request carrying it, the newest if several.
1904 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-agent1905 /// Who or what is working on it, e.g. `g1t`.
Dark gray base with lavender as an accent, and a live outcome view for plans1906 pub agent: Option<String>,
Agents as a team: lifecycle, merge queue, billing and a new shell1907}
1908
1909/// `start_plan`: records an outcome to plan for. Members of the
1910/// repository's workspace only. Called by the runner service, which starts
1911/// the sandbox. Returns `Outcome<PlanJob>`.
1912#[derive(Debug, Serialize, Deserialize)]
1913pub struct StartPlanArgs {
1914 pub actor: User,
1915 pub repo: RepoPath,
1916 pub brief: String,
1917}
1918
1919/// What a sandbox needs to write a plan.
1920#[derive(Debug, Serialize, Deserialize)]
1921#[serde(rename_all = "camelCase")]
1922pub struct PlanJob {
1923 pub plan_id: String,
1924 /// Lets the sandbox, and nothing else, report this plan.
1925 pub token: String,
1926 pub brief: String,
1927 pub repo: RepoPath,
1928}
1929
1930/// `report_plan`: the plan a sandbox's agent wrote, or why it could not
1931/// write one. Returns `Outcome<bool>`.
1932#[derive(Debug, Serialize, Deserialize)]
1933#[serde(rename_all = "camelCase")]
1934pub struct ReportPlanArgs {
1935 pub plan_id: String,
1936 pub token: String,
1937 #[serde(default)]
1938 pub summary: String,
1939 #[serde(default)]
1940 pub issues: Vec<PlannedIssue>,
1941 #[serde(default)]
1942 pub error: Option<String>,
1943}
1944
1945/// `get_plan`. Members only. Returns `Outcome<Plan>`. `list_plans` takes
1946/// `ViewArgs` and returns `Outcome<Vec<Plan>>`, newest first.
1947#[derive(Debug, Serialize, Deserialize)]
1948pub struct PlanArgs {
1949 pub repo: RepoPath,
1950 pub viewer: Viewer,
1951 pub id: String,
1952}
1953
1954/// `apply_plan`: opens a plan's issues, each blocked by the ones it depends
1955/// on. Members only, and once. Returns `Outcome<Plan>`, its issues now
1956/// carrying their numbers.
1957#[derive(Debug, Serialize, Deserialize)]
1958pub struct ApplyPlanArgs {
1959 pub actor: User,
1960 pub repo: RepoPath,
1961 pub id: String,
1962 /// Queue every issue for a g1t agent.
1963 #[serde(default)]
1964 pub assign: bool,
1965 /// The positions, counting from 1, of the issues to open. All of them
1966 /// when absent.
1967 #[serde(default)]
1968 pub keep: Option<Vec<u32>>,
1969}
1970
1971/// `queue_issue`: asks for a g1t agent to take an issue as soon as it can,
1972/// or withdraws that. The author or a member may. Returns `Outcome<bool>`.
1973#[derive(Debug, Serialize, Deserialize)]
1974pub struct QueueIssueArgs {
1975 pub actor: User,
1976 pub repo: RepoPath,
1977 pub number: u32,
1978 pub queued: bool,
1979}
1980
1981/// `ready_issues`: issues waiting for a g1t agent that can be given one
1982/// now, in one repository or in all. Called by the runner service. Returns
1983/// `Vec<ReadyIssue>`.
1984#[derive(Debug, Default, Serialize, Deserialize)]
1985#[serde(rename_all = "camelCase")]
1986pub struct ReadyIssuesArgs {
1987 #[serde(default)]
1988 pub repo_id: Option<String>,
1989}
1990
1991#[derive(Debug, Serialize, Deserialize)]
1992pub struct ReadyIssue {
1993 pub repo: RepoPath,
1994 pub number: u32,
1995 /// Who queued it, on whose say-so the agent works.
1996 pub actor: User,
1997}
1998
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar1999/// `update_pull`: changes who a pull request is assigned to, whose review
2000/// is asked for, its labels, its milestone and the branch it merges into.
2001/// Each list given replaces the whole set. Whoever opened it, or someone
2002/// with the Triage role, may; changing the base needs the Write role.
2003/// Returns `Outcome<Pull>`.
Agents as a team: lifecycle, merge queue, billing and a new shell2004#[derive(Debug, Serialize, Deserialize)]
2005pub struct UpdatePullArgs {
2006 pub actor: User,
2007 pub repo: RepoPath,
2008 pub number: u32,
2009 #[serde(default)]
2010 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-agent2011 /// 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 bar2012 /// start one; the runner's `review` does. A team is named
2013 /// `workspace/team` (or `@workspace/team`): the whole list, people and
2014 /// teams, replaces who is asked.
Agents as a team: lifecycle, merge queue, billing and a new shell2015 #[serde(default)]
2016 pub reviewers: Option<Vec<String>>,
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar2017 /// Its labels; replaces the whole set, as `set_labels` does.
2018 #[serde(default)]
2019 pub labels: Option<Vec<String>>,
2020 /// The number of the milestone to put it in; 0 takes it out. Needs the
2021 /// Triage role.
2022 #[serde(default)]
2023 pub milestone: Option<u32>,
2024 /// The branch it merges into: an existing branch other than its own.
2025 /// Needs the Write role.
2026 #[serde(default)]
2027 pub base: Option<String>,
Agents as a team: lifecycle, merge queue, billing and a new shell2028}
2029
Fast pages, required checks on the branch, self-hosted runners, honest incidents2030/// `start_checks`: always refused now; a pull request's checks are the
2031/// workflows run on it. Kept so that a runner from before is answered.
2032/// Returns `Outcome<CheckJob>`.
Acceptance checks in sandboxes, line comments and review verdicts2033#[derive(Debug, Serialize, Deserialize)]
2034#[serde(rename_all = "camelCase")]
2035pub struct StartChecksArgs {
2036 pub pull_id: String,
2037}
2038
2039/// What a sandbox needs to carry out a check run.
2040#[derive(Debug, Serialize, Deserialize)]
2041#[serde(rename_all = "camelCase")]
2042pub struct CheckJob {
2043 pub run_id: String,
2044 /// Lets the sandbox, and nothing else, report this run's results.
2045 pub token: String,
2046 pub commands: Vec<String>,
2047 /// The repository holding the commit: the fork, or the repository itself.
2048 pub source: RepoPath,
2049 pub commit: String,
g1t is the stored author of what it opens; the person who asked is requested_by and keeps the author's rights2050 /// Who the pull request is for ([`Pull::owner`]: whoever asked g1t for
2051 /// it, or its author), and so can read its source.
Acceptance checks in sandboxes, line comments and review verdicts2052 pub author: User,
2053 /// Username of whoever wrote the checks: the issue's author.
2054 pub requested_by: String,
2055 pub repo: RepoPath,
2056 pub number: u32,
2057}
2058
2059/// `report_checks`: what a sandbox says about its run. With no results and
2060/// no error it has started. `skip` forgets the run, for one that will not
2061/// be carried out. Returns `Outcome<CheckRun>`.
2062#[derive(Debug, Serialize, Deserialize)]
2063#[serde(rename_all = "camelCase")]
2064pub struct ReportChecksArgs {
2065 pub run_id: String,
2066 pub token: String,
2067 #[serde(default)]
2068 pub results: Vec<CheckResult>,
2069 #[serde(default)]
2070 pub error: Option<String>,
2071 #[serde(default)]
2072 pub skip: bool,
Work service in Rust, with RFC 3339 timestamps2073}
2074
Issues and pull requests replace intents and attempts2075/// A pull request in progress, with where it lives.
2076#[derive(Debug, Serialize, Deserialize)]
2077pub struct ActivePull {
2078 pub pull: Pull,
2079 pub issue: Option<Issue>,
Agents as a team: lifecycle, merge queue, billing and a new shell2080 /// Where it stands, for one g1t is seeing through.
2081 #[serde(default)]
2082 pub lifecycle: Option<Lifecycle>,
Issues and pull requests replace intents and attempts2083}
2084
2085/// `list_active_pulls`: drafts and open pull requests the viewer started,
Agents as a team: lifecycle, merge queue, billing and a new shell2086/// most recently active first. Returns `Vec<ActivePull>`. Also
2087/// `list_assigned_issues`: open issues assigned to the viewer, most
2088/// recently changed first. Returns `Vec<Issue>`.
Work service in Rust, with RFC 3339 timestamps2089#[derive(Debug, Serialize, Deserialize)]
2090pub struct ViewerArgs {
2091 pub viewer: Viewer,
2092}
2093
2094/// `append_session`. Returns `Outcome<Appended>`.
2095#[derive(Debug, Serialize, Deserialize)]
2096pub struct AppendSessionArgs {
2097 pub actor: User,
Issues and pull requests replace intents and attempts2098 pub repo: RepoPath,
2099 pub number: u32,
Work service in Rust, with RFC 3339 timestamps2100 pub entries: Vec<NewSessionEntry>,
2101}
2102
2103#[derive(Debug, Serialize, Deserialize)]
2104pub struct Appended {
2105 pub count: u32,
2106}
Issues and pull requests replace intents and attempts2107
Agents as a team: lifecycle, merge queue, billing and a new shell2108/// `start_review`: begins a review of a pull request by a g1t agent. Called
2109/// by the runner service, which starts the sandbox. Returns
2110/// `Outcome<ReviewJob>`.
2111#[derive(Debug, Serialize, Deserialize)]
2112#[serde(rename_all = "camelCase")]
2113pub struct StartReviewArgs {
2114 pub pull_id: String,
2115}
2116
2117/// What a sandbox needs to review a pull request.
2118#[derive(Debug, Serialize, Deserialize)]
2119#[serde(rename_all = "camelCase")]
2120pub struct ReviewJob {
2121 pub run_id: String,
2122 /// Lets the sandbox, and nothing else, report this review.
2123 pub token: String,
2124 /// The repository holding the commit: the fork, or the repository itself.
2125 pub source: RepoPath,
2126 pub commit: String,
2127 pub repo: RepoPath,
2128 pub default_branch: String,
2129 pub number: u32,
2130 pub title: String,
2131 pub description: String,
2132 /// The issue the pull request is for, which says what it should achieve.
2133 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 rights2134 /// Who the pull request is for ([`Pull::owner`]: whoever asked g1t for
2135 /// it, or its author), and so can read its source.
Agents as a team: lifecycle, merge queue, billing and a new shell2136 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 tier2137 /// The files it changes, as of its latest push: how large the change
2138 /// is, which decides the model that reviews it.
2139 #[serde(default)]
2140 pub files: Vec<ChangedFile>,
2141 /// What among them runs, configures or guards things (CI workflows,
2142 /// secrets, infrastructure), once each. Any sends the review to the
2143 /// larger model.
2144 #[serde(default)]
2145 pub sensitive: Vec<String>,
Agents as a team: lifecycle, merge queue, billing and a new shell2146}
2147
2148/// A comment on one line, as a reviewing agent reports it.
2149#[derive(Debug, Serialize, Deserialize)]
2150pub struct ReviewComment {
2151 pub path: String,
2152 #[serde(default)]
2153 pub line: u32,
2154 pub body: String,
2155}
2156
2157/// `report_review`: the review a sandbox's agent wrote, or why it could not
2158/// write one. Returns `Outcome<bool>`.
2159#[derive(Debug, Serialize, Deserialize)]
2160#[serde(rename_all = "camelCase")]
2161pub struct ReportReviewArgs {
2162 pub run_id: String,
2163 pub token: String,
2164 #[serde(default)]
2165 pub verdict: Option<Verdict>,
2166 #[serde(default)]
2167 pub body: String,
2168 #[serde(default)]
2169 pub comments: Vec<ReviewComment>,
2170 /// The model that wrote it, by its public name.
2171 #[serde(default)]
2172 pub model: Option<String>,
2173 #[serde(default)]
2174 pub error: Option<String>,
2175}
2176
Issues and pull requests replace intents and attempts2177/// Lowercases, trims and de-duplicates labels, dropping empty ones.
2178/// Returns `None` if there are too many or one is too long.
2179pub fn normalize_labels(labels: &[String]) -> Option<Vec<String>> {
2180 let mut normalized: Vec<String> = Vec::new();
2181 for label in labels {
2182 let label = label
2183 .split_whitespace()
2184 .collect::<Vec<_>>()
2185 .join(" ")
2186 .to_lowercase();
2187 if label.is_empty() || normalized.contains(&label) {
2188 continue;
2189 }
2190 if label.chars().count() > MAX_LABEL_CHARS {
2191 return None;
2192 }
2193 normalized.push(label);
2194 }
2195 (normalized.len() <= MAX_LABELS).then_some(normalized)
2196}
2197
2198#[cfg(test)]
2199mod tests {
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar2200 use super::*;
Issues and pull requests replace intents and attempts2201
2202 fn labels(names: &[&str]) -> Vec<String> {
2203 names.iter().map(|name| (*name).to_owned()).collect()
2204 }
2205
2206 #[test]
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar2207 fn colors_and_due_dates_are_tidied() {
2208 assert_eq!(tidy_color("#A1B2C3").as_deref(), Some("a1b2c3"));
2209 assert_eq!(tidy_color("fc0").as_deref(), Some("ffcc00"));
2210 assert_eq!(tidy_color("red"), None);
2211 assert_eq!(tidy_color("12345"), None);
2212 assert_eq!(label_color_for("bug"), "d73a4a");
2213 assert_eq!(label_color_for("area: web"), label_color_for("area: web"));
2214 assert_eq!(tidy_due_on("2026-10-14").as_deref(), Some("2026-10-14"));
2215 assert_eq!(tidy_due_on("2026-10-14T00:00:00Z").as_deref(), Some("2026-10-14"));
2216 assert_eq!(tidy_due_on("2026-13-01"), None);
2217 assert_eq!(tidy_due_on("soon"), None);
2218 }
2219
2220 #[test]
2221 fn a_milestone_is_as_far_along_as_its_closed_items() {
2222 let mut milestone: Milestone = serde_json::from_value(serde_json::json!({
2223 "number": 1, "title": "Launch", "state": "open", "createdAt": "", "updatedAt": ""
2224 }))
2225 .unwrap();
2226 assert_eq!(milestone.percent(), 0);
2227 milestone.open_items = 3;
2228 milestone.closed_items = 1;
2229 assert_eq!(milestone.percent(), 25);
2230 }
2231
2232 #[test]
Issues and pull requests replace intents and attempts2233 fn labels_are_lowercased_trimmed_and_unique() {
2234 assert_eq!(
2235 normalize_labels(&labels(&[" Bug ", "bug", "", "Good First Issue"])),
2236 Some(labels(&["bug", "good first issue"]))
2237 );
2238 }
2239
2240 #[test]
2241 fn too_long_or_too_many_labels_are_refused() {
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar2242 assert_eq!(normalize_labels(&["x".repeat(51)]), None);
2243 assert!(normalize_labels(&["x".repeat(50)]).is_some());
2244 let many: Vec<String> = (0..21).map(|i| format!("label-{i}")).collect();
Issues and pull requests replace intents and attempts2245 assert_eq!(normalize_labels(&many), None);
2246 }
2247}
Agents as a team: lifecycle, merge queue, billing and a new shell2248
2249
2250// --- Merge queue ----------------------------------------------------------
2251
2252/// Where a pull request in a merge queue stands.
2253#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
2254#[serde(rename_all = "snake_case")]
2255pub enum QueueState {
2256 /// Waiting for its turn to be tested.
2257 Waiting,
2258 /// Its combined state is being built and checked.
2259 Testing,
2260 /// Its combined state passed; it lands once everything ahead has.
2261 Passed,
2262 /// Its combined state failed, or would not merge. It left the queue.
2263 Failed,
2264 /// On the default branch.
2265 Landed,
2266 /// Taken out of the queue by a person, or closed.
2267 Removed,
2268}
2269
2270impl QueueState {
2271 pub fn as_str(self) -> &'static str {
2272 match self {
2273 QueueState::Waiting => "waiting",
2274 QueueState::Testing => "testing",
2275 QueueState::Passed => "passed",
2276 QueueState::Failed => "failed",
2277 QueueState::Landed => "landed",
2278 QueueState::Removed => "removed",
2279 }
2280 }
2281
2282 /// Still in the queue.
2283 pub fn is_active(self) -> bool {
2284 matches!(
2285 self,
2286 QueueState::Waiting | QueueState::Testing | QueueState::Passed
2287 )
2288 }
2289}
2290
2291/// One pull request's place in a merge queue.
2292#[derive(Clone, Debug, Serialize, Deserialize)]
2293#[serde(rename_all = "camelCase")]
2294pub struct QueueEntry {
2295 pub id: String,
2296 pub number: u32,
2297 pub title: String,
g1t is one name: its agent's work, commits and comments show as @g1t, and nobody can claim g1t or g1t-agent2298 /// Who or what made the pull request, e.g. `g1t`.
Agents as a team: lifecycle, merge queue, billing and a new shell2299 pub agent: String,
2300 pub state: QueueState,
2301 /// The pull requests merged ahead of it in the state being tested, in
2302 /// queue order. Empty when it was tested on the default branch alone.
2303 pub ahead: Vec<u32>,
2304 /// The default branch's commit the tested state was built on.
2305 pub base_commit: Option<String>,
2306 /// The tested state: the default branch with everything ahead and this.
2307 pub combined_commit: Option<String>,
2308 /// Why it failed: a merge conflict or what could not be run.
2309 pub error: Option<String>,
Fast pages, required checks on the branch, self-hosted runners, honest incidents2310 /// Commands run against the tested state by queues from before its
2311 /// checks were workflows. Empty since.
Agents as a team: lifecycle, merge queue, billing and a new shell2312 pub results: Vec<CheckResult>,
2313 /// Username of whoever merged it into the queue: a person, or `g1t`.
2314 pub enqueued_by: String,
2315 /// RFC 3339.
2316 pub created_at: String,
2317 /// RFC 3339. When it landed or left.
2318 pub finished_at: Option<String>,
2319}
2320
2321/// A repository's merge queue: what is in it, in order, and what recently
2322/// left it.
2323#[derive(Clone, Debug, Serialize, Deserialize)]
2324#[serde(rename_all = "camelCase")]
2325pub struct QueueView {
2326 /// Whether the repository merges through the queue.
2327 pub enabled: bool,
2328 pub active: Vec<QueueEntry>,
2329 /// Newest first.
2330 pub recent: Vec<QueueEntry>,
2331}
2332
2333/// `queue`: a repository's merge queue. Returns `Outcome<QueueView>`.
2334#[derive(Debug, Serialize, Deserialize)]
2335pub struct QueueArgs {
2336 pub repo: RepoPath,
2337 pub viewer: Viewer,
2338}
2339
2340/// `queue_build`: the next batch to test for a repository, if nothing is
2341/// being tested now. Returns `Vec<QueueJob>`, one per entry, each testing
2342/// the default branch with that entry and everything ahead of it.
2343#[derive(Debug, Serialize, Deserialize)]
2344#[serde(rename_all = "camelCase")]
2345pub struct QueueBuildArgs {
2346 pub repo_id: String,
2347}
2348
2349/// One pull request in a state being tested: where its change is.
2350#[derive(Clone, Debug, Serialize, Deserialize)]
2351#[serde(rename_all = "camelCase")]
2352pub struct QueueStackItem {
2353 pub number: u32,
2354 pub title: String,
2355 /// The repository holding the change: its fork, or the repository.
2356 pub source: RepoPath,
2357 /// The branch of `source` holding it.
2358 pub branch: String,
2359 pub commit: String,
2360}
2361
2362/// What a sandbox needs to build and check one combined state.
2363#[derive(Clone, Debug, Serialize, Deserialize)]
2364#[serde(rename_all = "camelCase")]
2365pub struct QueueJob {
2366 pub entry_id: String,
2367 /// Lets the sandbox, and nothing else, report this state's result.
2368 pub token: String,
2369 pub repo: RepoPath,
2370 pub default_branch: String,
2371 /// The default branch's commit to build on.
2372 pub base_commit: String,
2373 /// Where to push the tested state, in the repository itself.
2374 pub branch: String,
2375 /// The pull requests to merge in, in order; the last is the entry.
2376 pub stack: Vec<QueueStackItem>,
Fast pages, required checks on the branch, self-hosted runners, honest incidents2377 /// Commands to run on the built state. Always empty: the state is
2378 /// checked by the `merge_group` workflows run on it, and the default
2379 /// branch's required checks must pass there.
Agents as a team: lifecycle, merge queue, billing and a new shell2380 pub checks: Vec<String>,
Fast pages, required checks on the branch, self-hosted runners, honest incidents2381 /// Always empty, as `checks`.
Agents as a team: lifecycle, merge queue, billing and a new shell2382 #[serde(default)]
2383 pub contract_checks: Vec<String>,
2384 /// Who the sandbox acts as: a member who can push the tested state.
2385 pub actor: User,
2386}
2387
2388/// `report_queue`: a sandbox's result for one combined state. Returns
2389/// `Outcome<QueueState>`.
2390#[derive(Debug, Serialize, Deserialize)]
2391#[serde(rename_all = "camelCase")]
2392pub struct ReportQueueArgs {
2393 pub entry_id: String,
2394 pub token: String,
2395 #[serde(default)]
2396 pub combined_commit: Option<String>,
2397 #[serde(default)]
2398 pub results: Vec<CheckResult>,
2399 /// Set when the state could not be built or checked.
2400 #[serde(default)]
2401 pub error: Option<String>,
2402 /// For a merge conflict: the pull request whose change it collided with.
2403 #[serde(default)]
2404 pub conflict_with: Option<u32>,
Agents and memory, checks and conflicts, profiles, slug renames, custom domains2405 /// For a merge conflict: the files that conflicted.
2406 #[serde(default)]
2407 pub conflicts: Vec<String>,
Agents as a team: lifecycle, merge queue, billing and a new shell2408}
Record your own agent's sessions automatically2409
2410
2411/// `locate_pull`: where a pull request lives, by its id, for a tool that
2412/// knows only the fork it is working in (`g1t.sh/pulls/<id>`). Returns
2413/// `Outcome<LocatedPull>`; not found for anyone who cannot see it.
2414#[derive(Debug, Serialize, Deserialize)]
2415pub struct LocatePullArgs {
2416 pub id: String,
2417 pub viewer: Viewer,
2418}
2419
2420#[derive(Clone, Debug, Serialize, Deserialize)]
2421#[serde(rename_all = "camelCase")]
2422pub struct LocatedPull {
2423 pub repo: RepoPath,
2424 pub number: u32,
2425 pub title: String,
2426 pub status: PullStatus,
2427}
Agents and memory, checks and conflicts, profiles, slug renames, custom domains2428
2429// --- A person's work -------------------------------------------------------
2430
2431/// Issues or pull requests, on a person's profile.
2432#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
2433#[serde(rename_all = "lowercase")]
2434pub enum AuthoredKind {
2435 Issue,
2436 Pull,
2437}
2438
2439/// The state filter on a person's work. `Closed` takes in merged pull
2440/// requests too; `Merged` is only those.
2441#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
2442#[serde(rename_all = "lowercase")]
2443pub enum AuthoredState {
2444 Open,
2445 Closed,
2446 Merged,
2447}
2448
2449/// How a person's work is ordered.
2450#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
2451#[serde(rename_all = "lowercase")]
2452pub enum AuthoredSort {
2453 /// Newest first.
2454 #[default]
2455 Created,
2456 /// Most recently changed first.
2457 Updated,
2458 /// Oldest first.
2459 Oldest,
2460}
2461
2462/// The most items one `by_author` page holds.
2463pub const AUTHORED_PAGE: u32 = 25;
2464
2465/// `by_author`: the issues and pull requests a person opened, only on
2466/// repositories `viewer` may read, so a private title never reaches anyone
2467/// who could not open it. Returns `Outcome<Authored>`; not found for an
2468/// account that does not exist.
2469#[derive(Debug, Serialize, Deserialize)]
2470pub struct ByAuthorArgs {
2471 pub username: String,
2472 pub viewer: Viewer,
2473 #[serde(default)]
2474 pub kind: Option<AuthoredKind>,
2475 #[serde(default)]
2476 pub state: Option<AuthoredState>,
2477 /// Only work on this repository: `namespace/name`.
2478 #[serde(default)]
2479 pub repo: Option<String>,
2480 #[serde(default)]
2481 pub sort: AuthoredSort,
2482 /// The `next` of the page before, to read on from there.
2483 #[serde(default)]
2484 pub before: Option<String>,
2485 /// At most [`AUTHORED_PAGE`]; that when absent.
2486 #[serde(default)]
2487 pub limit: Option<u32>,
2488}
2489
2490/// One issue or pull request a person opened.
2491#[derive(Clone, Debug, Serialize, Deserialize)]
2492#[serde(rename_all = "camelCase")]
2493pub struct AuthoredItem {
2494 pub kind: AuthoredKind,
2495 pub repo: RepoPath,
2496 pub number: u32,
2497 pub title: String,
2498 /// Open or closed; a merged pull request is closed.
2499 pub state: State,
2500 /// A pull request's own status.
2501 pub status: Option<PullStatus>,
2502 /// Why an issue was closed.
2503 pub reason: Option<IssueReason>,
2504 pub draft: bool,
2505 pub merged: bool,
2506 /// RFC 3339.
2507 pub created_at: String,
2508 /// RFC 3339.
2509 pub updated_at: String,
2510 /// When a pull request was merged. RFC 3339.
2511 pub merged_at: Option<String>,
2512}
2513
2514/// What a person has done, as far as the viewer may see.
2515#[derive(Clone, Debug, Default, Serialize, Deserialize)]
2516#[serde(rename_all = "camelCase")]
2517pub struct AuthoredCounts {
2518 pub pulls_merged: u32,
2519 pub pulls_open: u32,
2520 pub pulls: u32,
2521 pub issues: u32,
2522 pub issues_open: u32,
2523}
2524
2525/// A repository a person has opened work on, with how much.
2526#[derive(Clone, Debug, Serialize, Deserialize)]
2527pub struct AuthoredRepo {
2528 pub repo: RepoPath,
2529 pub count: u32,
2530}
2531
2532/// A page of a person's work.
2533#[derive(Clone, Debug, Default, Serialize, Deserialize)]
2534#[serde(rename_all = "camelCase")]
2535pub struct Authored {
2536 pub items: Vec<AuthoredItem>,
2537 /// Pass as `before` for the next page; null on the last.
2538 pub next: Option<String>,
2539 /// Over every repository the viewer may read, whatever the filters.
2540 pub counts: AuthoredCounts,
2541 /// Those repositories, most work first.
2542 pub repos: Vec<AuthoredRepo>,
2543}
Fast pages, required checks on the branch, self-hosted runners, honest incidents2544
Chat controls, public profiles, shadcn selects, and no Docs tab in a project2545/// How many days `contributions` covers: today and the 364 before it.
2546pub const CONTRIBUTION_DAYS: u64 = 365;
2547
2548/// `contributions`: what a person did each day of the last year (issues
2549/// and pull requests opened, reviews given), only on repositories `viewer`
2550/// may read, for the calendar on their profile. Returns
2551/// `Outcome<Contributions>`; not found for an account that does not exist.
2552#[derive(Debug, Serialize, Deserialize)]
2553pub struct ContributionsArgs {
2554 pub username: String,
2555 pub viewer: Viewer,
2556}
2557
2558/// One day with something on it. Days with nothing are left out.
2559#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
2560pub struct ContributionDay {
2561 /// `YYYY-MM-DD`, UTC.
2562 pub date: String,
2563 pub count: u32,
2564}
2565
2566/// A person's year, as far as the viewer may see.
2567#[derive(Clone, Debug, Default, Serialize, Deserialize)]
2568pub struct Contributions {
2569 /// Oldest first.
2570 pub days: Vec<ContributionDay>,
2571 pub total: u32,
2572 /// The first day counted, `YYYY-MM-DD`; the last is today.
2573 pub from: String,
2574}
2575
Fast pages, required checks on the branch, self-hosted runners, honest incidents2576#[cfg(test)]
2577mod required_tests {
2578 use super::*;
2579
2580 fn status(context: &str, state: &str) -> CommitStatus {
2581 CommitStatus {
2582 context: context.into(),
2583 state: state.into(),
2584 description: Some(format!("{context} {state}")),
2585 target_url: None,
2586 updated_at: String::new(),
Merge rulesets: branch and tag rules, agent-first, enforced on push and merge2587 source: None,
Merge checks: statuses and check runs on every commit2588 check_run_id: None,
Fast pages, required checks on the branch, self-hosted runners, honest incidents2589 }
2590 }
2591
2592 #[test]
2593 fn a_context_names_its_check_and_event() {
2594 assert_eq!(check_name("CI / pull_request"), ("CI", Some("pull_request")));
2595 assert_eq!(check_name("Build and test / merge_group"), ("Build and test", Some("merge_group")));
2596 assert_eq!(check_name("g1t / deploy"), ("g1t / deploy", None));
2597 assert_eq!(check_name("g1t / deploy (docs)"), ("g1t / deploy (docs)", None));
2598 assert_eq!(check_name("lint"), ("lint", None));
2599 }
2600
2601 #[test]
2602 fn required_checks_are_missing_pending_failed_or_passed() {
2603 let required = vec!["CI".to_owned(), "Lint".to_owned(), "g1t / deploy".to_owned(), "Docs".to_owned()];
2604 let statuses = [
2605 status("CI / pull_request", "success"),
2606 status("Lint / pull_request", "pending"),
2607 status("g1t / deploy", "failure"),
2608 ];
2609 let states: Vec<RequiredState> = required_checks(&required, &statuses).into_iter().map(|c| c.state).collect();
2610 assert_eq!(
2611 states,
2612 [RequiredState::Success, RequiredState::Pending, RequiredState::Failure, RequiredState::Expected]
2613 );
2614 }
2615
2616 #[test]
2617 fn any_event_reports_a_check_and_a_failure_wins() {
2618 let required = vec!["ci".to_owned()];
2619 let both = [status("CI / push", "failure"), status("CI / pull_request", "success")];
2620 let check = &required_checks(&required, &both)[0];
2621 assert_eq!(check.state, RequiredState::Failure);
2622 assert_eq!(check.description.as_deref(), Some("CI / push failure"));
2623 let queue = [status("CI / merge_group", "success")];
2624 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 bar2625 }
2626
2627 #[test]
2628 fn a_pull_request_merges_into_its_base_or_the_default_branch() {
2629 let mut pull: Pull = serde_json::from_value(serde_json::json!({
2630 "id": "pr_1", "repoId": "rep_1", "number": 14, "issue": null, "title": "Fix it", "body": null,
2631 "agent": "agent", "runtime": "external", "status": "open", "fork": null, "forkRepoId": null,
2632 "branch": "fix", "headCommit": null, "mergeBase": null, "mergedBy": null, "mergedAt": null,
2633 "supersededBy": null, "checkStatus": null, "author": { "id": "x", "username": "x" },
2634 "createdAt": "", "updatedAt": ""
2635 }))
2636 .unwrap();
2637 assert_eq!(pull.base_branch("main"), "main");
2638 assert!(pull.targets_default("main"));
2639 assert_eq!(pull.comparison(&None).base_branch, None);
2640 pull.base = Some("release".into());
2641 assert_eq!(pull.base_branch("main"), "release");
2642 assert!(!pull.targets_default("main"));
2643 assert_eq!(pull.comparison(&None).base_branch.as_deref(), Some("release"));
2644 }
2645
2646 #[test]
Fast pages, required checks on the branch, self-hosted runners, honest incidents2647 fn required_names_are_tidied() {
2648 let names = vec![" CI ".to_owned(), "ci".to_owned(), String::new(), "Lint".to_owned()];
2649 assert_eq!(tidy_required(&names), ["CI", "Lint"]);
2650 }
2651
2652 #[test]
2653 fn a_definition_of_done_is_added_once() {
2654 let items = commands_pass(&["cargo test".to_owned(), " ".to_owned()]);
2655 assert_eq!(items, ["`cargo test` passes."]);
2656 let body = with_definition_of_done("Fix the greeting.", &items);
2657 assert_eq!(body, "Fix the greeting.\n\n## Definition of done\n\n- `cargo test` passes.");
2658 assert_eq!(with_definition_of_done(&body, &items), body);
2659 assert_eq!(with_definition_of_done("", &items), "## Definition of done\n\n- `cargo test` passes.");
2660 assert_eq!(with_definition_of_done(" Text ", &[]), "Text");
2661 }
2662}
g1t is the stored author of what it opens; the person who asked is requested_by and keeps the author's rights2663
2664#[cfg(test)]
2665mod authorship_tests {
2666 use super::*;
2667 use crate::PrincipalKind;
2668 use crate::credentials::{Acting, Principal};
2669 use crate::identity::{AGENT_ID, AgentScope};
2670
2671 fn person() -> User {
2672 User {
2673 id: "usr_1".into(),
2674 username: "syntaqx".into(),
2675 verified: true,
2676 ..User::default()
2677 }
2678 }
2679
2680 fn agent_for(id: &str, username: &str) -> User {
2681 User {
2682 id: AGENT_ID.into(),
2683 username: "g1t".into(),
2684 kind: PrincipalKind::Agent,
2685 acting: Some(Box::new(Acting {
2686 credential_id: "tok_1".into(),
2687 agent: "g1t".into(),
2688 on_behalf_of: Principal { id: id.into(), username: username.into() },
2689 scope: AgentScope {
2690 repo: RepoPath { namespace: "acme".into(), name: "web".into() },
2691 operations: Vec::new(),
2692 run: None,
2693 },
2694 })),
2695 ..User::default()
2696 }
2697 }
2698
2699 fn pull(author: User, requested_by: Option<User>) -> Pull {
2700 let mut pull: Pull = serde_json::from_value(serde_json::json!({
2701 "id": "pr_1", "repoId": "rep_1", "number": 14, "issue": 12, "title": "Fix it", "body": null,
2702 "agent": "g1t", "runtime": "hosted", "status": "open", "fork": null, "forkRepoId": null,
2703 "branch": null, "headCommit": null, "mergeBase": null, "mergedBy": null, "mergedAt": null,
2704 "supersededBy": null, "checkStatus": null,
2705 "author": { "id": "x", "username": "x" },
2706 "createdAt": "", "updatedAt": ""
2707 }))
2708 .unwrap();
2709 pull.author = author;
2710 pull.requested_by = requested_by;
2711 pull
2712 }
2713
2714 #[test]
2715 fn a_change_a_person_has_g1t_make_is_g1t_s_requested_by_them() {
2716 let (author, asked) = authorship(&person(), true);
2717 assert_eq!((author.id.as_str(), author.username.as_str(), author.kind), (AGENT_ID, "g1t", PrincipalKind::Agent));
2718 let asked = asked.expect("the person asked for it");
2719 assert_eq!((asked.id.as_str(), asked.username.as_str(), asked.kind), ("usr_1", "syntaqx", PrincipalKind::User));
2720 }
2721
2722 #[test]
2723 fn what_g1t_s_agent_files_at_work_is_g1t_s_requested_by_whoever_it_works_for() {
2724 let (author, asked) = authorship(&agent_for("usr_1", "syntaqx"), false);
2725 assert_eq!(author.id, AGENT_ID);
2726 assert_eq!(asked.map(|user| user.username), Some("syntaqx".into()));
2727 }
2728
2729 #[test]
2730 fn nobody_asked_for_g1t_s_own_work() {
2731 // A run g1t started itself acts for g1t, not for a person.
2732 let (author, asked) = authorship(&agent_for(crate::system::ID, "g1t"), false);
2733 assert_eq!(author.id, AGENT_ID);
2734 assert!(asked.is_none());
2735 // A security update g1t opens is g1t's own, as before.
2736 let (author, asked) = authorship(&User::system("acme"), true);
2737 assert_eq!((author.id.as_str(), author.kind), (crate::system::ID, PrincipalKind::System));
2738 assert!(asked.is_none());
2739 }
2740
2741 #[test]
2742 fn anyone_else_opens_their_own() {
2743 let (author, asked) = authorship(&person(), false);
2744 assert_eq!((author.id.as_str(), author.kind), ("usr_1", PrincipalKind::User));
2745 assert!(asked.is_none());
2746 // Nothing but who they are is kept.
2747 assert!(author.workspaces.is_empty() && author.acting.is_none());
2748 }
2749
2750 #[test]
2751 fn the_requester_owns_g1t_s_pull_request_and_an_author_their_own() {
2752 let made = pull(g1t_author(), Some(person()));
2753 assert_eq!(made.owner().id, "usr_1");
2754 assert!(made.is_owned_by("usr_1"));
2755 assert!(!made.is_owned_by(AGENT_ID), "g1t's agent does not answer for its own change");
2756 let own = pull(person(), None);
2757 assert_eq!(own.owner().id, "usr_1");
2758 assert!(own.is_owned_by("usr_1"));
2759 }
2760
2761 #[test]
2762 fn requested_by_is_null_when_nobody_asked_and_read_as_absent_from_older_senders() {
2763 let own = pull(person(), None);
2764 let sent = serde_json::to_value(&own).unwrap();
2765 assert!(sent["requestedBy"].is_null());
2766 let mut older = sent.clone();
2767 older.as_object_mut().unwrap().remove("requestedBy");
2768 let read: Pull = serde_json::from_value(older).unwrap();
2769 assert!(read.requested_by.is_none());
2770 }
Merge Actions: cross-repo workflows and actions, release and deployment triggers, step timeouts2771
2772 #[test]
2773 fn a_comment_is_changed_by_its_author_or_a_maintainer() {
2774 use CommentKind::{Comment, Event};
2775 // Its author edits and deletes it; someone else needs Maintain.
2776 assert!(may_change_comment(Comment, false, "usr_1", "usr_1", false, false).is_ok());
2777 assert!(may_change_comment(Comment, false, "usr_1", "usr_1", false, true).is_ok());
2778 assert!(may_change_comment(Comment, false, "usr_1", "usr_2", false, false).is_err());
2779 assert!(may_change_comment(Comment, false, "usr_1", "usr_2", false, true).is_err());
2780 assert!(may_change_comment(Comment, false, "usr_1", "usr_2", true, false).is_ok());
2781 assert!(may_change_comment(Comment, false, "usr_1", "usr_2", true, true).is_ok());
2782 // A review's text can be edited, but its verdict stays.
2783 assert!(may_change_comment(Comment, true, "usr_1", "usr_1", false, false).is_ok());
2784 assert!(may_change_comment(Comment, true, "usr_1", "usr_1", true, true).is_err());
2785 // A note of what happened is nobody's to change.
2786 assert!(may_change_comment(Event, false, "usr_1", "usr_1", true, false).is_err());
2787 assert!(may_change_comment(Event, false, "usr_1", "usr_1", true, true).is_err());
2788 }
g1t is the stored author of what it opens; the person who asked is requested_by and keeps the author's rights2789}

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