flagon-io/g1t

public

Where people and agents ship software together. The open-source git platform for the whole job: issues, agents, checks and deploys to the edge.

g1t/crates/contracts/src/work.rs

1,424 lines46,985 bytesCodeBlame
1//! The work service: issues, pull requests, comments and sessions.
2//!
3//! Each `*Args` struct is the argument of the method of the same name,
4//! served at `POST /rpc/<method>`.
5//!
6//! Issues and pull requests share one sequence of numbers per repository,
7//! so `#12` names exactly one of them.
8
9use serde::{Deserialize, Serialize};
10
11use crate::repos::{CompareArgs, RepoPath};
12use crate::{User, Viewer};
13
14/// Labels every repository starts with. Any other label comes into being
15/// the first time it is put on an issue.
16pub const DEFAULT_LABELS: [&str; 5] = ["bug", "feature", "docs", "chore", "question"];
17
18/// `open` or `closed`: the filter on lists of issues and pull requests. An
19/// open pull request is a draft or one ready for review; a closed one was
20/// merged or closed without merging.
21#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
22#[serde(rename_all = "lowercase")]
23pub enum State {
24 Open,
25 Closed,
26}
27
28/// Why an issue was closed.
29#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
30#[serde(rename_all = "snake_case")]
31pub enum IssueReason {
32 /// The work was done. If a pull request did it, `resolved_by` names it.
33 Completed,
34 NotPlanned,
35}
36
37impl IssueReason {
38 pub fn as_str(self) -> &'static str {
39 match self {
40 IssueReason::Completed => "completed",
41 IssueReason::NotPlanned => "not_planned",
42 }
43 }
44}
45
46/// Something that should change in a repository: a bug, a feature, a
47/// question. Opened by a person, an agent or an integration. Pull requests
48/// are made against it; the one that is merged resolves it.
49#[derive(Clone, Debug, Serialize, Deserialize)]
50#[serde(rename_all = "camelCase")]
51pub struct Issue {
52 pub id: String,
53 pub repo_id: String,
54 /// Shown as `#12`.
55 pub number: u32,
56 pub title: String,
57 /// Markdown. Also what an agent is given to work from.
58 pub body: String,
59 pub labels: Vec<String>,
60 /// Commands that must pass for a pull request to be accepted.
61 pub checks: Vec<String>,
62 pub state: State,
63 /// Set when closed.
64 pub reason: Option<IssueReason>,
65 /// The number of the pull request whose merge closed this issue.
66 pub resolved_by: Option<u32>,
67 pub author: User,
68 /// RFC 3339.
69 pub created_at: String,
70 /// RFC 3339.
71 pub updated_at: String,
72 /// RFC 3339.
73 pub closed_at: Option<String>,
74 /// Pull requests made against this issue, in any state.
75 pub pull_count: u32,
76 pub comment_count: u32,
77 /// Usernames of the people it is assigned to.
78 #[serde(default)]
79 pub assignees: Vec<String>,
80 /// The numbers of the issues that have to be merged before this one is
81 /// worked on.
82 #[serde(default)]
83 pub blocked_by: Vec<u32>,
84 /// Whether a g1t agent takes it as soon as it can: at once, or when
85 /// what it is blocked by has merged.
86 #[serde(default)]
87 pub queued: bool,
88 /// The agent working on it now: the one behind its newest pull request
89 /// that is still in progress in a fork, such as `g1t-agent`.
90 #[serde(default)]
91 pub agent: Option<String>,
92}
93
94#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
95#[serde(rename_all = "lowercase")]
96pub enum PullStatus {
97 /// Still being worked on.
98 Draft,
99 /// Ready for review.
100 Open,
101 Merged,
102 /// Closed without merging.
103 Closed,
104}
105
106impl PullStatus {
107 pub fn as_str(self) -> &'static str {
108 match self {
109 PullStatus::Draft => "draft",
110 PullStatus::Open => "open",
111 PullStatus::Merged => "merged",
112 PullStatus::Closed => "closed",
113 }
114 }
115
116 /// Whether the pull request can still be changed or merged.
117 pub fn is_active(self) -> bool {
118 matches!(self, PullStatus::Draft | PullStatus::Open)
119 }
120}
121
122/// Where the agent runs: on g1t's sandboxes, or in someone's own session.
123#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
124#[serde(rename_all = "lowercase")]
125pub enum Runtime {
126 Hosted,
127 External,
128}
129
130/// A proposed change. It is made either in a fork created for it, which is
131/// how agents work, or on a branch pushed to the repository itself.
132#[derive(Clone, Debug, Serialize, Deserialize)]
133#[serde(rename_all = "camelCase")]
134pub struct Pull {
135 pub id: String,
136 pub repo_id: String,
137 /// Shown as `#12`.
138 pub number: u32,
139 /// The number of the issue this is for, if any.
140 pub issue: Option<u32>,
141 pub title: String,
142 /// Markdown: what changed and why. Set when marked ready.
143 pub body: Option<String>,
144 /// A label for the agent doing the work, e.g. `claude-code`.
145 pub agent: String,
146 pub runtime: Runtime,
147 pub status: PullStatus,
148 /// The fork holding the change, unless it is on a branch.
149 pub fork: Option<RepoPath>,
150 /// The fork's repository id.
151 pub fork_repo_id: Option<String>,
152 /// The branch of the repository holding the change, unless it is in a
153 /// fork.
154 pub branch: Option<String>,
155 pub head_commit: Option<String>,
156 /// For a merged pull request, what the branch pointed to before the
157 /// merge. Comparing against it shows what the pull request changed.
158 pub merge_base: Option<String>,
159 /// Username of whoever merged it.
160 pub merged_by: Option<String>,
161 /// RFC 3339.
162 pub merged_at: Option<String>,
163 /// Set on a pull request closed because another one for the same issue
164 /// was merged: that one's number.
165 pub superseded_by: Option<u32>,
166 /// Where the latest run of the issue's acceptance checks stands, if
167 /// there has been one against the current head.
168 pub check_status: Option<CheckStatus>,
169 /// The files it changes, as of its latest push.
170 #[serde(default)]
171 pub files: Vec<ChangedFile>,
172 /// Usernames of the people it is assigned to.
173 #[serde(default)]
174 pub assignees: Vec<String>,
175 /// Those whose review was asked for: usernames, and `g1t-agent` when a
176 /// g1t agent was asked.
177 #[serde(default)]
178 pub reviewers: Vec<String>,
179 pub author: User,
180 /// RFC 3339.
181 pub created_at: String,
182 /// RFC 3339.
183 pub updated_at: String,
184}
185
186/// One file a pull request changes, and by how much.
187#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
188pub struct ChangedFile {
189 pub path: String,
190 pub additions: u32,
191 pub deletions: u32,
192}
193
194/// Another pull request in progress that changes some of the same files.
195/// Two for the same issue are alternatives; two for different issues are
196/// heading for a conflict.
197#[derive(Clone, Debug, Serialize, Deserialize)]
198pub struct Overlap {
199 pub number: u32,
200 pub title: String,
201 /// The number of the issue the other pull request is for.
202 pub issue: Option<u32>,
203 /// The files both change.
204 pub paths: Vec<String>,
205}
206
207impl Pull {
208 /// What to ask the repos service to see what this pull request changes.
209 ///
210 /// A fork is compared as a whole. A branch is compared by name while
211 /// the pull request is open, and by the commit it was merged or closed
212 /// at afterwards, so later pushes to the branch do not change the record.
213 pub fn comparison(&self, viewer: &Viewer) -> CompareArgs {
214 let settled = matches!(self.status, PullStatus::Merged | PullStatus::Closed);
215 let (repo_id, head) = match &self.fork_repo_id {
216 Some(fork) => (fork.clone(), None),
217 None => (
218 self.repo_id.clone(),
219 self.head_commit
220 .clone()
221 .filter(|_| settled)
222 .or_else(|| self.branch.clone()),
223 ),
224 };
225 CompareArgs {
226 repo_id,
227 viewer: viewer.clone(),
228 base: self.merge_base.clone(),
229 head,
230 }
231 }
232}
233
234#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
235#[serde(rename_all = "lowercase")]
236pub enum CheckStatus {
237 /// Waiting for a sandbox.
238 Queued,
239 Running,
240 Passed,
241 Failed,
242 /// The checks could not be run at all.
243 Errored,
244}
245
246impl CheckStatus {
247 pub fn as_str(self) -> &'static str {
248 match self {
249 CheckStatus::Queued => "queued",
250 CheckStatus::Running => "running",
251 CheckStatus::Passed => "passed",
252 CheckStatus::Failed => "failed",
253 CheckStatus::Errored => "errored",
254 }
255 }
256}
257
258/// How one acceptance check went.
259#[derive(Clone, Debug, Serialize, Deserialize)]
260#[serde(rename_all = "camelCase")]
261pub struct CheckResult {
262 pub command: String,
263 pub passed: bool,
264 /// Absent when the command was stopped for taking too long.
265 #[serde(default)]
266 pub exit_code: Option<i32>,
267 /// What the command printed, standard output and error together. The
268 /// end of it, when there was a lot.
269 #[serde(default)]
270 pub output: String,
271 #[serde(default)]
272 pub duration_ms: u64,
273}
274
275/// One run of an issue's acceptance checks against a pull request's head,
276/// in a sandbox that holds nothing but that commit.
277#[derive(Clone, Debug, Serialize, Deserialize)]
278#[serde(rename_all = "camelCase")]
279pub struct CheckRun {
280 pub id: String,
281 /// The commit that was checked.
282 pub head_commit: String,
283 pub status: CheckStatus,
284 pub results: Vec<CheckResult>,
285 /// Why the checks could not be run, when `status` is `errored`.
286 pub error: Option<String>,
287 /// RFC 3339.
288 pub created_at: String,
289 /// RFC 3339.
290 pub finished_at: Option<String>,
291}
292
293/// A reviewer's decision on a pull request.
294#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
295#[serde(rename_all = "snake_case")]
296pub enum Verdict {
297 Approve,
298 RequestChanges,
299}
300
301impl Verdict {
302 pub fn as_str(self) -> &'static str {
303 match self {
304 Verdict::Approve => "approve",
305 Verdict::RequestChanges => "request_changes",
306 }
307 }
308}
309
310/// What an entry in a conversation is.
311#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
312#[serde(rename_all = "lowercase")]
313pub enum CommentKind {
314 /// Something a person or an agent wrote.
315 #[default]
316 Comment,
317 /// Something that happened: an assignment, a review asked for, a close.
318 Event,
319}
320
321/// A comment on an issue or a pull request. On a pull request it can sit
322/// on one line of the change, and it can carry a reviewer's verdict.
323#[derive(Clone, Debug, Serialize, Deserialize)]
324#[serde(rename_all = "camelCase")]
325pub struct Comment {
326 pub id: String,
327 /// Something a person wrote, or something that happened.
328 #[serde(default)]
329 pub kind: CommentKind,
330 pub author: User,
331 /// Markdown. For an event, what its author did, as the rest of a
332 /// sentence that starts with their name: "assigned ana".
333 pub body: String,
334 /// The file commented on, for a comment on a line.
335 pub path: Option<String>,
336 /// The line of that file, as numbered after the change.
337 pub line: Option<u32>,
338 pub verdict: Option<Verdict>,
339 /// RFC 3339.
340 pub created_at: String,
341}
342
343#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
344#[serde(rename_all = "snake_case")]
345pub enum SessionEntryKind {
346 Prompt,
347 Message,
348 ToolCall,
349 ToolResult,
350 Note,
351}
352
353/// One step of an agent's session: the "why" behind a pull request's commits.
354#[derive(Clone, Debug, Serialize, Deserialize)]
355pub struct SessionEntry {
356 pub seq: u32,
357 pub kind: SessionEntryKind,
358 pub text: String,
359 /// For tool calls and results.
360 pub tool: Option<String>,
361 /// The fork's head commit when this entry was recorded, if known.
362 pub commit: Option<String>,
363 /// RFC 3339.
364 pub at: String,
365}
366
367#[derive(Clone, Debug, Serialize, Deserialize)]
368pub struct NewSessionEntry {
369 pub kind: SessionEntryKind,
370 pub text: String,
371 #[serde(default)]
372 pub tool: Option<String>,
373 #[serde(default)]
374 pub commit: Option<String>,
375}
376
377#[derive(Clone, Debug, Serialize, Deserialize)]
378pub struct IssueDetail {
379 pub issue: Issue,
380 /// Every pull request made against it, oldest first.
381 pub pulls: Vec<Pull>,
382 pub comments: Vec<Comment>,
383}
384
385#[derive(Clone, Debug, Serialize, Deserialize)]
386pub struct PullDetail {
387 pub pull: Pull,
388 /// The issue it is for, if any.
389 pub issue: Option<Issue>,
390 pub comments: Vec<Comment>,
391 /// The latest run of the issue's acceptance checks.
392 pub checks: Option<CheckRun>,
393 /// Other pull requests in progress that change the same files.
394 #[serde(default)]
395 pub overlaps: Vec<Overlap>,
396 /// Whether the branch it would merge into has moved on without it, so
397 /// that it has to catch up before it can merge.
398 #[serde(default)]
399 pub behind: bool,
400 /// Whether a g1t agent is reviewing it right now.
401 #[serde(default)]
402 pub review_pending: bool,
403 /// Where it stands on its way to being merged, for a pull request g1t
404 /// is seeing through. Absent on anyone else's.
405 #[serde(default)]
406 pub lifecycle: Option<Lifecycle>,
407 /// A merge was asked for while it was behind: g1t is bringing it up to
408 /// date and will then land it.
409 #[serde(default)]
410 pub landing: bool,
411 /// Why g1t stopped working on it, if it did: a catch-up that could not
412 /// be completed, for example.
413 #[serde(default)]
414 pub stalled: Option<String>,
415 /// Messages people sent the agent while it worked, oldest first.
416 #[serde(default)]
417 pub messages: Vec<AgentMessage>,
418 /// What workflow runs said about its head commit, one per workflow.
419 #[serde(default)]
420 pub statuses: Vec<CommitStatus>,
421}
422
423/// What a workflow run (or another tool) says about a commit.
424#[derive(Clone, Debug, Serialize, Deserialize)]
425#[serde(rename_all = "camelCase")]
426pub struct CommitStatus {
427 /// What reported it, such as `CI / push`.
428 pub context: String,
429 /// `pending`, `success`, `failure` or `error`.
430 pub state: String,
431 pub description: Option<String>,
432 /// Where to see more, such as the run's page.
433 pub target_url: Option<String>,
434 pub updated_at: String,
435}
436
437/// `set_commit_status`: for services only. Returns `Outcome<bool>`.
438#[derive(Debug, Serialize, Deserialize)]
439#[serde(rename_all = "camelCase")]
440pub struct SetCommitStatusArgs {
441 pub repo_id: String,
442 pub sha: String,
443 pub context: String,
444 pub state: String,
445 #[serde(default)]
446 pub description: Option<String>,
447 #[serde(default)]
448 pub target_url: Option<String>,
449}
450
451/// A message a person sent an agent at work on a pull request. The agent
452/// receives it at its next step.
453#[derive(Clone, Debug, Serialize, Deserialize)]
454#[serde(rename_all = "camelCase")]
455pub struct AgentMessage {
456 pub id: String,
457 pub author: String,
458 pub body: String,
459 /// RFC 3339.
460 pub created_at: String,
461 /// RFC 3339. When the agent received it; null until then.
462 pub delivered_at: Option<String>,
463 /// `message` from a person, or from another pull request's agent a
464 /// `question`, a `handoff` of work, or the `answer` to one.
465 #[serde(default = "message_kind")]
466 pub kind: String,
467 /// The pull request whose agent sent it, when an agent did.
468 #[serde(default)]
469 pub from_number: Option<u32>,
470 /// The pull request it was sent to.
471 #[serde(default)]
472 pub to_number: u32,
473 /// For a question or handoff: the reply, once there is one.
474 #[serde(default)]
475 pub answer: Option<String>,
476 /// For a handoff: whether it was declined.
477 #[serde(default)]
478 pub declined: bool,
479 /// For the agent that sent it: what to expect, when the agent it asked
480 /// is not at work and will not answer soon.
481 #[serde(default, skip_serializing_if = "Option::is_none")]
482 pub hint: Option<String>,
483}
484
485fn message_kind() -> String {
486 "message".to_owned()
487}
488
489/// `message_agent`: sends the agent working on a pull request a message.
490/// The pull request's author and members of the workspace may. Returns
491/// `Outcome<AgentMessage>`.
492#[derive(Debug, Serialize, Deserialize)]
493pub struct MessageAgentArgs {
494 pub actor: User,
495 pub repo: RepoPath,
496 pub number: u32,
497 pub body: String,
498 /// For an agent: `question` or `handoff`; a person's is a `message`.
499 #[serde(default)]
500 pub kind: Option<String>,
501 /// For an agent: the pull request it is working on, which the reply
502 /// goes back to.
503 #[serde(default)]
504 pub from_number: Option<u32>,
505}
506
507/// `answer_message`: replies to a question or a handoff an agent received,
508/// accepting or declining a handoff. The reply reaches the asking agent at
509/// its next step. Returns `Outcome<AgentMessage>`, the message answered.
510#[derive(Debug, Serialize, Deserialize)]
511pub struct AnswerMessageArgs {
512 pub actor: User,
513 pub repo: RepoPath,
514 pub id: String,
515 pub body: String,
516 #[serde(default)]
517 pub decline: bool,
518}
519
520/// `take_messages`: the messages not yet delivered to the agent working on
521/// a pull request, marked delivered. Only g1t's agents may. Returns
522/// `Outcome<Vec<AgentMessage>>`.
523#[derive(Debug, Serialize, Deserialize)]
524pub struct TakeMessagesArgs {
525 pub actor: User,
526 pub repo: RepoPath,
527 pub number: u32,
528}
529
530/// A step on the way from an assigned issue to a pull request that is ready
531/// to merge. g1t takes each one without being asked.
532#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
533#[serde(rename_all = "snake_case")]
534pub enum Stage {
535 /// The agent is making the change.
536 Working,
537 /// The issue's acceptance checks are running against it.
538 Checking,
539 /// A g1t agent is reviewing it.
540 Reviewing,
541 /// The agent is addressing failed checks or a review.
542 Revising,
543 /// The agent is merging in the branch it would land on, which moved.
544 CatchingUp,
545 /// Woken to answer a question another agent asked it, or a handoff.
546 Answering,
547 /// In the repository's merge queue, being tested with what is ahead of
548 /// it before it lands.
549 Queued,
550 /// Checks passed, reviewed and approved, up to date. A person merges.
551 Ready,
552 /// g1t has stopped and a person has to decide what happens next.
553 NeedsYou,
554}
555
556/// Where a pull request made by a g1t agent stands. See [`Stage`].
557#[derive(Clone, Debug, Serialize, Deserialize)]
558pub struct Lifecycle {
559 pub stage: Stage,
560 /// One sentence saying what is happening, or why it stopped.
561 pub detail: String,
562 /// How many times the agent has been sent back to revise it.
563 pub revisions: u32,
564}
565
566/// `advance`: works out the next step for a pull request g1t is seeing
567/// through and, if there is one to take now, claims it, so that it is
568/// taken once however many times this is called. Called by the runner
569/// service, which carries the step out. Returns `Advance`.
570#[derive(Debug, Serialize, Deserialize)]
571#[serde(rename_all = "camelCase")]
572pub struct AdvanceArgs {
573 pub pull_id: String,
574}
575
576#[derive(Debug, Serialize, Deserialize)]
577#[serde(tag = "action", rename_all = "snake_case")]
578pub enum Advance {
579 /// Nothing to do now: a step is under way, or it is a person's turn.
580 None,
581 /// Have a g1t agent review it.
582 Review { job: LifecycleJob },
583 /// Send the agent back to address `job.feedback`.
584 Revise { job: LifecycleJob },
585 /// Merge in the branch it would land on.
586 CatchUp { job: LifecycleJob },
587}
588
589/// What the runner needs to carry out a step of a pull request's lifecycle.
590#[derive(Debug, Serialize, Deserialize)]
591#[serde(rename_all = "camelCase")]
592pub struct LifecycleJob {
593 pub pull_id: String,
594 pub repo: RepoPath,
595 pub number: u32,
596 /// Who the pull request belongs to. Sandboxes act as them.
597 pub author: User,
598 /// The repository holding the change: its fork, or the repository
599 /// itself for one made on a branch.
600 pub source: RepoPath,
601 /// The branch of the source holding the change; its default branch
602 /// when absent.
603 #[serde(default)]
604 pub branch: Option<String>,
605 pub default_branch: String,
606 pub title: String,
607 pub description: String,
608 pub issue: Option<Issue>,
609 /// For a revision: the failed checks or the review to address.
610 pub feedback: String,
611 /// For a revision: which one this is, from 1.
612 pub round: u32,
613}
614
615/// How a repository wants its pull requests handled. A repository that has
616/// changed nothing has the defaults.
617#[derive(Clone, Debug, Serialize, Deserialize)]
618#[serde(rename_all = "camelCase", default)]
619pub struct RepoSettings {
620 /// Land a g1t agent's pull request without a person once it is ready:
621 /// checks passed and approved as the settings below require.
622 pub auto_merge: bool,
623 /// Refuse to merge a pull request that does not contain the default
624 /// branch's latest commits, so that what merges is what was checked.
625 /// When off, merging one that is behind brings it up to date first.
626 pub require_up_to_date: bool,
627 /// How many approving reviews a pull request needs before it may
628 /// merge. A reviewer who has since asked for changes blocks it.
629 pub required_approvals: u32,
630 /// Whether a g1t agent's approval counts towards `required_approvals`.
631 pub count_agent_approvals: bool,
632 /// Whether a member may merge although the acceptance checks did not
633 /// pass.
634 pub allow_ignoring_checks: bool,
635 /// Whether a g1t agent's pull request is reviewed by a second agent
636 /// without being asked.
637 pub agent_review: bool,
638 /// How many times a g1t agent is sent back to its pull request before
639 /// a person is asked instead.
640 pub max_revisions: u32,
641 /// Merge through a queue: pull requests are tested together with those
642 /// ahead of them, and only a combination that passed reaches the default
643 /// branch.
644 pub merge_queue: bool,
645 /// Username of the member who last changed the settings, if anyone has.
646 pub updated_by: Option<String>,
647 /// RFC 3339.
648 pub updated_at: Option<String>,
649}
650
651impl Default for RepoSettings {
652 fn default() -> Self {
653 RepoSettings {
654 auto_merge: false,
655 require_up_to_date: false,
656 required_approvals: 0,
657 count_agent_approvals: true,
658 allow_ignoring_checks: true,
659 agent_review: true,
660 max_revisions: 2,
661 merge_queue: false,
662 updated_by: None,
663 updated_at: None,
664 }
665 }
666}
667
668/// `update_settings`: replaces a repository's settings. Members of its
669/// workspace only. Returns `Outcome<RepoSettings>`. `get_settings` takes
670/// `ViewArgs` and returns the same.
671#[derive(Debug, Serialize, Deserialize)]
672pub struct UpdateSettingsArgs {
673 pub actor: User,
674 pub repo: RepoPath,
675 /// Who changed them and when are filled in by the service.
676 pub settings: RepoSettings,
677}
678
679/// `catch_up_job`: what the runner needs to bring a pull request up to date
680/// because a merge of it was asked for. Null if none was. Returns
681/// `Option<LifecycleJob>`.
682#[derive(Debug, Serialize, Deserialize)]
683#[serde(rename_all = "camelCase")]
684pub struct CatchUpJobArgs {
685 pub pull_id: String,
686}
687
688/// `wake_for_messages`: the agent on a pull request was asked a question
689/// or handed work while it was not at work. Claims a short step for it to
690/// answer, and hands over what it was sent, marked read. Null when there
691/// is nothing waiting, or the pull request cannot take a step now.
692/// Returns `Option<Wake>`.
693#[derive(Debug, Serialize, Deserialize)]
694#[serde(rename_all = "camelCase")]
695pub struct WakeForMessagesArgs {
696 pub pull_id: String,
697}
698
699/// What an agent woken to answer needs: its pull request, and what it was
700/// sent, oldest first.
701#[derive(Debug, Serialize, Deserialize)]
702#[serde(rename_all = "camelCase")]
703pub struct Wake {
704 pub job: LifecycleJob,
705 pub messages: Vec<AgentMessage>,
706}
707
708/// `stall`: records that a step could not be carried out, so that g1t
709/// stops and a person is asked. Returns `bool`.
710#[derive(Debug, Serialize, Deserialize)]
711#[serde(rename_all = "camelCase")]
712pub struct StallArgs {
713 pub pull_id: String,
714 pub reason: String,
715}
716
717/// `managed_pulls`: ids of the open pull requests g1t is seeing through,
718/// in one repository or in all of them. Returns `Vec<String>`.
719#[derive(Debug, Default, Serialize, Deserialize)]
720#[serde(rename_all = "camelCase")]
721pub struct ManagedPullsArgs {
722 #[serde(default)]
723 pub repo_id: Option<String>,
724}
725
726/// `open_issue`. Returns `Outcome<Issue>`.
727#[derive(Debug, Serialize, Deserialize)]
728pub struct OpenIssueArgs {
729 pub actor: User,
730 pub repo: RepoPath,
731 pub title: String,
732 #[serde(default)]
733 pub body: String,
734 #[serde(default)]
735 pub labels: Vec<String>,
736 #[serde(default)]
737 pub checks: Vec<String>,
738}
739
740/// `list_issues`, newest first. Returns `Outcome<Vec<Issue>>`.
741#[derive(Debug, Serialize, Deserialize)]
742pub struct ListIssuesArgs {
743 pub repo: RepoPath,
744 pub viewer: Viewer,
745 #[serde(default)]
746 pub state: Option<State>,
747 /// Only issues carrying this label.
748 #[serde(default)]
749 pub label: Option<String>,
750}
751
752/// `list_pulls`, newest first. Returns `Outcome<Vec<Pull>>`.
753#[derive(Debug, Serialize, Deserialize)]
754pub struct ListPullsArgs {
755 pub repo: RepoPath,
756 pub viewer: Viewer,
757 #[serde(default)]
758 pub state: Option<State>,
759}
760
761/// `get_issue` (`Outcome<IssueDetail>`), `get_pull` (`Outcome<PullDetail>`),
762/// `read_session` (`Outcome<Vec<SessionEntry>>`), `list_labels`
763/// (`Outcome<Vec<String>>`) and `counts` (`Outcome<Counts>`). The last two
764/// ignore `number`.
765#[derive(Debug, Serialize, Deserialize)]
766#[serde(rename_all = "camelCase")]
767pub struct ViewArgs {
768 pub repo: RepoPath,
769 #[serde(default)]
770 pub number: u32,
771 pub viewer: Viewer,
772 /// For `read_session`: only entries after this sequence number.
773 #[serde(default)]
774 pub after_seq: u32,
775}
776
777/// How many issues and pull requests are open on a repository.
778#[derive(Debug, Serialize, Deserialize)]
779pub struct Counts {
780 pub issues: u32,
781 pub pulls: u32,
782}
783
784/// `update_issue`: changes whichever fields are given. The author or a
785/// member of the workspace may. Returns `Outcome<Issue>`.
786#[derive(Debug, Serialize, Deserialize)]
787pub struct UpdateIssueArgs {
788 pub actor: User,
789 pub repo: RepoPath,
790 pub number: u32,
791 #[serde(default)]
792 pub title: Option<String>,
793 #[serde(default)]
794 pub body: Option<String>,
795 #[serde(default)]
796 pub labels: Option<Vec<String>>,
797 /// Usernames of the people it is assigned to; replaces the whole set.
798 /// Assigning it to the g1t agent is the runner's `run`, not this.
799 #[serde(default)]
800 pub assignees: Option<Vec<String>>,
801}
802
803/// `close_issue` and `reopen_issue`. Each returns `Outcome<Issue>`.
804#[derive(Debug, Serialize, Deserialize)]
805pub struct IssueActionArgs {
806 pub actor: User,
807 pub repo: RepoPath,
808 pub number: u32,
809 /// For `close_issue`; `completed` if left out.
810 #[serde(default)]
811 pub reason: Option<IssueReason>,
812}
813
814/// `add_comment`, on an issue or a pull request. On a pull request it may
815/// name a line of the change, and may carry a verdict; nobody can give a
816/// verdict on their own pull request. Returns `Outcome<Comment>`.
817#[derive(Debug, Serialize, Deserialize)]
818pub struct AddCommentArgs {
819 pub actor: User,
820 pub repo: RepoPath,
821 pub number: u32,
822 /// May be empty when approving.
823 #[serde(default)]
824 pub body: String,
825 #[serde(default)]
826 pub path: Option<String>,
827 #[serde(default)]
828 pub line: Option<u32>,
829 #[serde(default)]
830 pub verdict: Option<Verdict>,
831}
832
833/// `open_pull`. Without `branch`, forks the repo and returns a draft pull
834/// request to push to. With it, opens a pull request, ready for review,
835/// for a branch already pushed to the repo. Returns `Outcome<Pull>`.
836#[derive(Debug, Serialize, Deserialize)]
837pub struct OpenPullArgs {
838 pub actor: User,
839 pub repo: RepoPath,
840 /// The issue this is for.
841 #[serde(default)]
842 pub issue: Option<u32>,
843 /// Defaults to the issue's title; required without an issue.
844 #[serde(default)]
845 pub title: String,
846 /// What changed and why. Usually set later, when a draft is marked ready.
847 #[serde(default)]
848 pub body: String,
849 /// A branch of the repository that already holds the change.
850 #[serde(default)]
851 pub branch: Option<String>,
852 #[serde(default)]
853 pub agent: String,
854 pub runtime: Runtime,
855}
856
857/// `ready_pull`, `close_pull` and `merge_pull`. Each returns `Outcome<Pull>`.
858#[derive(Debug, Serialize, Deserialize)]
859#[serde(rename_all = "camelCase")]
860pub struct PullActionArgs {
861 pub actor: User,
862 pub repo: RepoPath,
863 pub number: u32,
864 /// For `ready_pull`: what changed and why.
865 #[serde(default)]
866 pub summary: String,
867 /// For `merge_pull`: leave the issue open and the other pull requests
868 /// for it untouched, because this one is only part of the work.
869 #[serde(default)]
870 pub keep_issue_open: bool,
871 /// For `merge_pull`: merge although the acceptance checks have not
872 /// passed.
873 #[serde(default)]
874 pub ignore_checks: bool,
875}
876
877/// Where a plan stands.
878#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
879#[serde(rename_all = "lowercase")]
880pub enum PlanStatus {
881 /// An agent is reading the repository and writing it.
882 Planning,
883 /// Written, and waiting for a person to read and apply it.
884 Ready,
885 /// It could not be written.
886 Failed,
887 /// Its issues have been opened.
888 Applied,
889}
890
891/// One issue a plan proposes.
892#[derive(Clone, Debug, Default, Serialize, Deserialize)]
893#[serde(rename_all = "camelCase", default)]
894pub struct PlannedIssue {
895 pub title: String,
896 /// Markdown: what to change, where, and why.
897 pub body: String,
898 pub labels: Vec<String>,
899 /// Commands that must pass once the change is made.
900 pub checks: Vec<String>,
901 /// The files it will most likely change.
902 pub files: Vec<String>,
903 /// The positions, counting from 1, of earlier issues in the plan that
904 /// have to be merged first. An agent writes this as `depends_on`.
905 #[serde(alias = "depends_on")]
906 pub depends_on: Vec<u32>,
907 /// Its number, once the plan has been applied and it was kept.
908 pub number: Option<u32>,
909}
910
911/// An outcome someone wrote, and the issues an agent proposes to get there.
912#[derive(Clone, Debug, Serialize, Deserialize)]
913#[serde(rename_all = "camelCase")]
914pub struct Plan {
915 pub id: String,
916 pub repo_id: String,
917 /// The outcome wanted, as written.
918 pub brief: String,
919 pub status: PlanStatus,
920 /// The agent's account of how it split the work.
921 pub summary: String,
922 pub issues: Vec<PlannedIssue>,
923 /// Why it could not be written, when `status` is `failed`.
924 pub error: Option<String>,
925 pub author: User,
926 /// RFC 3339.
927 pub created_at: String,
928 /// RFC 3339.
929 pub finished_at: Option<String>,
930 /// Once applied: where each issue it opened stands now, in plan order.
931 /// Filled in by `get_plan` only.
932 #[serde(default)]
933 pub progress: Vec<IssueProgress>,
934 /// Questions and handoffs between the agents on its pull requests,
935 /// newest first. Filled in by `get_plan` only.
936 #[serde(default)]
937 pub exchanges: Vec<AgentMessage>,
938}
939
940/// Where one issue of an applied plan stands.
941#[derive(Clone, Debug, Serialize, Deserialize)]
942#[serde(rename_all = "camelCase")]
943pub struct IssueProgress {
944 pub number: u32,
945 pub title: String,
946 /// `blocked` (waiting on issues it depends on), `waiting` (for an
947 /// agent), `open` (nobody on it), one of the lifecycle stages
948 /// (`working`, `checking`, `reviewing`, `revising`, `catching_up`,
949 /// `answering`, `queued`, `ready`, `needs_you`), `landed` or `closed`.
950 pub state: String,
951 /// One sentence about where it stands.
952 pub detail: String,
953 /// The issues it is waiting on that are still open.
954 pub blocked_by: Vec<u32>,
955 /// The pull request carrying it, the newest if several.
956 pub pull: Option<u32>,
957 /// Who or what is working on it, e.g. `g1t-agent`.
958 pub agent: Option<String>,
959}
960
961/// `start_plan`: records an outcome to plan for. Members of the
962/// repository's workspace only. Called by the runner service, which starts
963/// the sandbox. Returns `Outcome<PlanJob>`.
964#[derive(Debug, Serialize, Deserialize)]
965pub struct StartPlanArgs {
966 pub actor: User,
967 pub repo: RepoPath,
968 pub brief: String,
969}
970
971/// What a sandbox needs to write a plan.
972#[derive(Debug, Serialize, Deserialize)]
973#[serde(rename_all = "camelCase")]
974pub struct PlanJob {
975 pub plan_id: String,
976 /// Lets the sandbox, and nothing else, report this plan.
977 pub token: String,
978 pub brief: String,
979 pub repo: RepoPath,
980}
981
982/// `report_plan`: the plan a sandbox's agent wrote, or why it could not
983/// write one. Returns `Outcome<bool>`.
984#[derive(Debug, Serialize, Deserialize)]
985#[serde(rename_all = "camelCase")]
986pub struct ReportPlanArgs {
987 pub plan_id: String,
988 pub token: String,
989 #[serde(default)]
990 pub summary: String,
991 #[serde(default)]
992 pub issues: Vec<PlannedIssue>,
993 #[serde(default)]
994 pub error: Option<String>,
995}
996
997/// `get_plan`. Members only. Returns `Outcome<Plan>`. `list_plans` takes
998/// `ViewArgs` and returns `Outcome<Vec<Plan>>`, newest first.
999#[derive(Debug, Serialize, Deserialize)]
1000pub struct PlanArgs {
1001 pub repo: RepoPath,
1002 pub viewer: Viewer,
1003 pub id: String,
1004}
1005
1006/// `apply_plan`: opens a plan's issues, each blocked by the ones it depends
1007/// on. Members only, and once. Returns `Outcome<Plan>`, its issues now
1008/// carrying their numbers.
1009#[derive(Debug, Serialize, Deserialize)]
1010pub struct ApplyPlanArgs {
1011 pub actor: User,
1012 pub repo: RepoPath,
1013 pub id: String,
1014 /// Queue every issue for a g1t agent.
1015 #[serde(default)]
1016 pub assign: bool,
1017 /// The positions, counting from 1, of the issues to open. All of them
1018 /// when absent.
1019 #[serde(default)]
1020 pub keep: Option<Vec<u32>>,
1021}
1022
1023/// `queue_issue`: asks for a g1t agent to take an issue as soon as it can,
1024/// or withdraws that. The author or a member may. Returns `Outcome<bool>`.
1025#[derive(Debug, Serialize, Deserialize)]
1026pub struct QueueIssueArgs {
1027 pub actor: User,
1028 pub repo: RepoPath,
1029 pub number: u32,
1030 pub queued: bool,
1031}
1032
1033/// `ready_issues`: issues waiting for a g1t agent that can be given one
1034/// now, in one repository or in all. Called by the runner service. Returns
1035/// `Vec<ReadyIssue>`.
1036#[derive(Debug, Default, Serialize, Deserialize)]
1037#[serde(rename_all = "camelCase")]
1038pub struct ReadyIssuesArgs {
1039 #[serde(default)]
1040 pub repo_id: Option<String>,
1041}
1042
1043#[derive(Debug, Serialize, Deserialize)]
1044pub struct ReadyIssue {
1045 pub repo: RepoPath,
1046 pub number: u32,
1047 /// Who queued it, on whose say-so the agent works.
1048 pub actor: User,
1049}
1050
1051/// `update_pull`: changes who a pull request is assigned to and whose
1052/// review is asked for. Each list given replaces the whole set. Whoever
1053/// opened it, or a member of the workspace, may. Returns `Outcome<Pull>`.
1054#[derive(Debug, Serialize, Deserialize)]
1055pub struct UpdatePullArgs {
1056 pub actor: User,
1057 pub repo: RepoPath,
1058 pub number: u32,
1059 #[serde(default)]
1060 pub assignees: Option<Vec<String>>,
1061 /// May include `g1t-agent`. Asking for its review does not by itself
1062 /// start one; the runner's `review` does.
1063 #[serde(default)]
1064 pub reviewers: Option<Vec<String>>,
1065}
1066
1067/// `start_checks`: begins a run of the acceptance checks for a pull request
1068/// that is ready for review. Called by the runner service, which starts the
1069/// sandbox. Returns `Outcome<CheckJob>`.
1070#[derive(Debug, Serialize, Deserialize)]
1071#[serde(rename_all = "camelCase")]
1072pub struct StartChecksArgs {
1073 pub pull_id: String,
1074}
1075
1076/// What a sandbox needs to carry out a check run.
1077#[derive(Debug, Serialize, Deserialize)]
1078#[serde(rename_all = "camelCase")]
1079pub struct CheckJob {
1080 pub run_id: String,
1081 /// Lets the sandbox, and nothing else, report this run's results.
1082 pub token: String,
1083 pub commands: Vec<String>,
1084 /// The repository holding the commit: the fork, or the repository itself.
1085 pub source: RepoPath,
1086 pub commit: String,
1087 /// Who opened the pull request, and so can read its source.
1088 pub author: User,
1089 /// Username of whoever wrote the checks: the issue's author.
1090 pub requested_by: String,
1091 pub repo: RepoPath,
1092 pub number: u32,
1093}
1094
1095/// `report_checks`: what a sandbox says about its run. With no results and
1096/// no error it has started. `skip` forgets the run, for one that will not
1097/// be carried out. Returns `Outcome<CheckRun>`.
1098#[derive(Debug, Serialize, Deserialize)]
1099#[serde(rename_all = "camelCase")]
1100pub struct ReportChecksArgs {
1101 pub run_id: String,
1102 pub token: String,
1103 #[serde(default)]
1104 pub results: Vec<CheckResult>,
1105 #[serde(default)]
1106 pub error: Option<String>,
1107 #[serde(default)]
1108 pub skip: bool,
1109}
1110
1111/// A pull request in progress, with where it lives.
1112#[derive(Debug, Serialize, Deserialize)]
1113pub struct ActivePull {
1114 pub pull: Pull,
1115 pub issue: Option<Issue>,
1116 /// Where it stands, for one g1t is seeing through.
1117 #[serde(default)]
1118 pub lifecycle: Option<Lifecycle>,
1119}
1120
1121/// `list_active_pulls`: drafts and open pull requests the viewer started,
1122/// most recently active first. Returns `Vec<ActivePull>`. Also
1123/// `list_assigned_issues`: open issues assigned to the viewer, most
1124/// recently changed first. Returns `Vec<Issue>`.
1125#[derive(Debug, Serialize, Deserialize)]
1126pub struct ViewerArgs {
1127 pub viewer: Viewer,
1128}
1129
1130/// `append_session`. Returns `Outcome<Appended>`.
1131#[derive(Debug, Serialize, Deserialize)]
1132pub struct AppendSessionArgs {
1133 pub actor: User,
1134 pub repo: RepoPath,
1135 pub number: u32,
1136 pub entries: Vec<NewSessionEntry>,
1137}
1138
1139#[derive(Debug, Serialize, Deserialize)]
1140pub struct Appended {
1141 pub count: u32,
1142}
1143
1144/// `start_review`: begins a review of a pull request by a g1t agent. Called
1145/// by the runner service, which starts the sandbox. Returns
1146/// `Outcome<ReviewJob>`.
1147#[derive(Debug, Serialize, Deserialize)]
1148#[serde(rename_all = "camelCase")]
1149pub struct StartReviewArgs {
1150 pub pull_id: String,
1151}
1152
1153/// What a sandbox needs to review a pull request.
1154#[derive(Debug, Serialize, Deserialize)]
1155#[serde(rename_all = "camelCase")]
1156pub struct ReviewJob {
1157 pub run_id: String,
1158 /// Lets the sandbox, and nothing else, report this review.
1159 pub token: String,
1160 /// The repository holding the commit: the fork, or the repository itself.
1161 pub source: RepoPath,
1162 pub commit: String,
1163 pub repo: RepoPath,
1164 pub default_branch: String,
1165 pub number: u32,
1166 pub title: String,
1167 pub description: String,
1168 /// The issue the pull request is for, which says what it should achieve.
1169 pub issue: Option<Issue>,
1170 /// Who opened the pull request, and so can read its source.
1171 pub author: User,
1172}
1173
1174/// A comment on one line, as a reviewing agent reports it.
1175#[derive(Debug, Serialize, Deserialize)]
1176pub struct ReviewComment {
1177 pub path: String,
1178 #[serde(default)]
1179 pub line: u32,
1180 pub body: String,
1181}
1182
1183/// `report_review`: the review a sandbox's agent wrote, or why it could not
1184/// write one. Returns `Outcome<bool>`.
1185#[derive(Debug, Serialize, Deserialize)]
1186#[serde(rename_all = "camelCase")]
1187pub struct ReportReviewArgs {
1188 pub run_id: String,
1189 pub token: String,
1190 #[serde(default)]
1191 pub verdict: Option<Verdict>,
1192 #[serde(default)]
1193 pub body: String,
1194 #[serde(default)]
1195 pub comments: Vec<ReviewComment>,
1196 /// The model that wrote it, by its public name.
1197 #[serde(default)]
1198 pub model: Option<String>,
1199 #[serde(default)]
1200 pub error: Option<String>,
1201}
1202
1203/// Lowercases, trims and de-duplicates labels, dropping empty ones.
1204/// Returns `None` if there are too many or one is too long.
1205pub fn normalize_labels(labels: &[String]) -> Option<Vec<String>> {
1206 const MAX_LABELS: usize = 10;
1207 const MAX_LABEL_CHARS: usize = 40;
1208 let mut normalized: Vec<String> = Vec::new();
1209 for label in labels {
1210 let label = label
1211 .split_whitespace()
1212 .collect::<Vec<_>>()
1213 .join(" ")
1214 .to_lowercase();
1215 if label.is_empty() || normalized.contains(&label) {
1216 continue;
1217 }
1218 if label.chars().count() > MAX_LABEL_CHARS {
1219 return None;
1220 }
1221 normalized.push(label);
1222 }
1223 (normalized.len() <= MAX_LABELS).then_some(normalized)
1224}
1225
1226#[cfg(test)]
1227mod tests {
1228 use super::normalize_labels;
1229
1230 fn labels(names: &[&str]) -> Vec<String> {
1231 names.iter().map(|name| (*name).to_owned()).collect()
1232 }
1233
1234 #[test]
1235 fn labels_are_lowercased_trimmed_and_unique() {
1236 assert_eq!(
1237 normalize_labels(&labels(&[" Bug ", "bug", "", "Good First Issue"])),
1238 Some(labels(&["bug", "good first issue"]))
1239 );
1240 }
1241
1242 #[test]
1243 fn too_long_or_too_many_labels_are_refused() {
1244 assert_eq!(normalize_labels(&["x".repeat(41)]), None);
1245 let many: Vec<String> = (0..11).map(|i| format!("label-{i}")).collect();
1246 assert_eq!(normalize_labels(&many), None);
1247 }
1248}
1249
1250
1251// --- Merge queue ----------------------------------------------------------
1252
1253/// Where a pull request in a merge queue stands.
1254#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
1255#[serde(rename_all = "snake_case")]
1256pub enum QueueState {
1257 /// Waiting for its turn to be tested.
1258 Waiting,
1259 /// Its combined state is being built and checked.
1260 Testing,
1261 /// Its combined state passed; it lands once everything ahead has.
1262 Passed,
1263 /// Its combined state failed, or would not merge. It left the queue.
1264 Failed,
1265 /// On the default branch.
1266 Landed,
1267 /// Taken out of the queue by a person, or closed.
1268 Removed,
1269}
1270
1271impl QueueState {
1272 pub fn as_str(self) -> &'static str {
1273 match self {
1274 QueueState::Waiting => "waiting",
1275 QueueState::Testing => "testing",
1276 QueueState::Passed => "passed",
1277 QueueState::Failed => "failed",
1278 QueueState::Landed => "landed",
1279 QueueState::Removed => "removed",
1280 }
1281 }
1282
1283 /// Still in the queue.
1284 pub fn is_active(self) -> bool {
1285 matches!(
1286 self,
1287 QueueState::Waiting | QueueState::Testing | QueueState::Passed
1288 )
1289 }
1290}
1291
1292/// One pull request's place in a merge queue.
1293#[derive(Clone, Debug, Serialize, Deserialize)]
1294#[serde(rename_all = "camelCase")]
1295pub struct QueueEntry {
1296 pub id: String,
1297 pub number: u32,
1298 pub title: String,
1299 /// Who or what made the pull request, e.g. `g1t-agent`.
1300 pub agent: String,
1301 pub state: QueueState,
1302 /// The pull requests merged ahead of it in the state being tested, in
1303 /// queue order. Empty when it was tested on the default branch alone.
1304 pub ahead: Vec<u32>,
1305 /// The default branch's commit the tested state was built on.
1306 pub base_commit: Option<String>,
1307 /// The tested state: the default branch with everything ahead and this.
1308 pub combined_commit: Option<String>,
1309 /// Why it failed: a merge conflict or what could not be run.
1310 pub error: Option<String>,
1311 /// The checks run against the tested state.
1312 pub results: Vec<CheckResult>,
1313 /// Username of whoever merged it into the queue: a person, or `g1t`.
1314 pub enqueued_by: String,
1315 /// RFC 3339.
1316 pub created_at: String,
1317 /// RFC 3339. When it landed or left.
1318 pub finished_at: Option<String>,
1319}
1320
1321/// A repository's merge queue: what is in it, in order, and what recently
1322/// left it.
1323#[derive(Clone, Debug, Serialize, Deserialize)]
1324#[serde(rename_all = "camelCase")]
1325pub struct QueueView {
1326 /// Whether the repository merges through the queue.
1327 pub enabled: bool,
1328 pub active: Vec<QueueEntry>,
1329 /// Newest first.
1330 pub recent: Vec<QueueEntry>,
1331}
1332
1333/// `queue`: a repository's merge queue. Returns `Outcome<QueueView>`.
1334#[derive(Debug, Serialize, Deserialize)]
1335pub struct QueueArgs {
1336 pub repo: RepoPath,
1337 pub viewer: Viewer,
1338}
1339
1340/// `queue_build`: the next batch to test for a repository, if nothing is
1341/// being tested now. Returns `Vec<QueueJob>`, one per entry, each testing
1342/// the default branch with that entry and everything ahead of it.
1343#[derive(Debug, Serialize, Deserialize)]
1344#[serde(rename_all = "camelCase")]
1345pub struct QueueBuildArgs {
1346 pub repo_id: String,
1347}
1348
1349/// One pull request in a state being tested: where its change is.
1350#[derive(Clone, Debug, Serialize, Deserialize)]
1351#[serde(rename_all = "camelCase")]
1352pub struct QueueStackItem {
1353 pub number: u32,
1354 pub title: String,
1355 /// The repository holding the change: its fork, or the repository.
1356 pub source: RepoPath,
1357 /// The branch of `source` holding it.
1358 pub branch: String,
1359 pub commit: String,
1360}
1361
1362/// What a sandbox needs to build and check one combined state.
1363#[derive(Clone, Debug, Serialize, Deserialize)]
1364#[serde(rename_all = "camelCase")]
1365pub struct QueueJob {
1366 pub entry_id: String,
1367 /// Lets the sandbox, and nothing else, report this state's result.
1368 pub token: String,
1369 pub repo: RepoPath,
1370 pub default_branch: String,
1371 /// The default branch's commit to build on.
1372 pub base_commit: String,
1373 /// Where to push the tested state, in the repository itself.
1374 pub branch: String,
1375 /// The pull requests to merge in, in order; the last is the entry.
1376 pub stack: Vec<QueueStackItem>,
1377 /// Every acceptance check of every pull request in the stack.
1378 pub checks: Vec<String>,
1379 /// The checks of issues already completed: the default branch's
1380 /// contract. One that fails on the base alone is not held against the
1381 /// entry.
1382 #[serde(default)]
1383 pub contract_checks: Vec<String>,
1384 /// Who the sandbox acts as: a member who can push the tested state.
1385 pub actor: User,
1386}
1387
1388/// `report_queue`: a sandbox's result for one combined state. Returns
1389/// `Outcome<QueueState>`.
1390#[derive(Debug, Serialize, Deserialize)]
1391#[serde(rename_all = "camelCase")]
1392pub struct ReportQueueArgs {
1393 pub entry_id: String,
1394 pub token: String,
1395 #[serde(default)]
1396 pub combined_commit: Option<String>,
1397 #[serde(default)]
1398 pub results: Vec<CheckResult>,
1399 /// Set when the state could not be built or checked.
1400 #[serde(default)]
1401 pub error: Option<String>,
1402 /// For a merge conflict: the pull request whose change it collided with.
1403 #[serde(default)]
1404 pub conflict_with: Option<u32>,
1405}
1406
1407
1408/// `locate_pull`: where a pull request lives, by its id, for a tool that
1409/// knows only the fork it is working in (`g1t.sh/pulls/<id>`). Returns
1410/// `Outcome<LocatedPull>`; not found for anyone who cannot see it.
1411#[derive(Debug, Serialize, Deserialize)]
1412pub struct LocatePullArgs {
1413 pub id: String,
1414 pub viewer: Viewer,
1415}
1416
1417#[derive(Clone, Debug, Serialize, Deserialize)]
1418#[serde(rename_all = "camelCase")]
1419pub struct LocatedPull {
1420 pub repo: RepoPath,
1421 pub number: u32,
1422 pub title: String,
1423 pub status: PullStatus,
1424}