Skip to content

g1t/crates/contracts/src/work.rs

2,683 lines95,542 bytesCodeBlame

Pick any line to see why it is the way it is: the commit, the pull request and issue it came from, and what the agent was thinking.

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

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