Skip to content
1,392 linesCodeBlameRaw
1import type { AgentLook } from "./agent-look";
2import type { ChecksApi } from "./checks";
3import type { CodeownersReport, PullCodeOwners } from "./codeowners";
4import type { User, Viewer } from "./identity";
5import type { PullBranchUpdate, RepoPath } from "./repos";
6import type { Result } from "./result";
7import type { MergeRules, RulesApi } from "./rules";
8
9/**
10 * The filter on lists of issues and pull requests. An open pull request is
11 * a draft or one ready for review; a closed one was merged or closed
12 * without merging.
13 */
14export type State = "open" | "closed";
15
16/** Why an issue was closed. */
17export type IssueReason = "completed" | "not_planned";
18
19/**
20 * Something that should change in a repository: a bug, a feature, a
21 * question. Opened by a person, an agent or an integration. Pull requests
22 * are made against it; the one that is merged resolves it.
23 *
24 * Issues and pull requests share one sequence of numbers per repository.
25 */
26export type Issue = {
27 id: string;
28 repoId: string;
29 /** Shown as `#12`. */
30 number: number;
31 title: string;
32 /** Markdown. Also what an agent is given to work from. */
33 body: string;
34 labels: string[];
35 state: State;
36 /** Set when closed. */
37 reason: IssueReason | null;
38 /** The number of the pull request whose merge closed this issue. */
39 resolvedBy: number | null;
40 /** Who opened it: a person, an integration, or g1t (`kind` `agent`) for one its agent filed while at work. */
41 author: User;
42 /**
43 * For an issue g1t's agent filed: the person it was working for. They may
44 * manage it as its author could. See `workOwner`.
45 */
46 requestedBy: User | null;
47 /** RFC 3339. */
48 createdAt: string;
49 /** RFC 3339. */
50 updatedAt: string;
51 /** RFC 3339. */
52 closedAt: string | null;
53 /** Pull requests made against this issue, in any state. */
54 pullCount: number;
55 commentCount: number;
56 /** Usernames of the people it is assigned to. */
57 assignees: string[];
58 /** The numbers of the issues that have to be merged before this one is worked on. */
59 blockedBy: number[];
60 /**
61 * Whether a g1t agent takes it as soon as it can: at once, or when what it
62 * is blocked by has merged.
63 */
64 queued: boolean;
65 /**
66 * The agent working on it now: the one behind its newest pull request
67 * that is still in progress in a fork, such as `g1t`.
68 */
69 agent: string | null;
70 /** The milestone it is in, if any. */
71 milestone?: MilestoneRef | null;
72};
73
74/**
75 * A label of a repository: a name, a color and what it means. Issues and
76 * pull requests carry labels by name; names are lowercase.
77 */
78export type Label = {
79 name: string;
80 /** Six hex digits, without `#`. */
81 color: string;
82 description: string;
83 /** How many issues carry it, open or closed. */
84 issues: number;
85 /** How many pull requests carry it, in any state. */
86 pulls: number;
87};
88
89/** A milestone, as an issue or pull request names it. */
90export type MilestoneRef = { number: number; title: string };
91
92/**
93 * A goal, with an optional due date, that issues and pull requests are
94 * gathered under. Its progress is how many of them are closed.
95 */
96export type Milestone = {
97 /** Numbered from 1 in each repository, apart from issues. */
98 number: number;
99 title: string;
100 /** Markdown. */
101 description: string;
102 /** `YYYY-MM-DD`. */
103 dueOn: string | null;
104 state: State;
105 /** Open issues and pull requests in it. */
106 openItems: number;
107 /** Closed issues, and merged or closed pull requests, in it. */
108 closedItems: number;
109 createdAt: string;
110 updatedAt: string;
111 closedAt: string | null;
112};
113
114/** One milestone and what is in it, newest first. */
115export type MilestoneDetail = { milestone: Milestone; issues: Issue[]; pulls: Pull[] };
116
117/** How `setLabels` changes an item's labels. */
118export type LabelChange = "set" | "add" | "remove";
119
120/**
121 * Whose an issue or a pull request is to answer for: whoever asked g1t for
122 * it, or its author. They may change, close and steer it, are never asked to
123 * review it and cannot approve it, and see it as theirs. Mirrors
124 * `Pull::owner` in the Rust contracts.
125 */
126export function workOwner(item: Pick<Pull, "author" | "requestedBy">): User {
127 return item.requestedBy ?? item.author;
128}
129
130/** `draft` is still being worked on; `open` is ready for review. */
131export type PullStatus = "draft" | "open" | "merged" | "closed";
132
133/** Where the agent runs: on g1t's sandboxes, or in someone's own session. */
134export type Runtime = "hosted" | "external";
135
136/**
137 * A proposed change. It is made either in a fork created for it, which is
138 * how agents work, or on a branch pushed to the repository itself.
139 */
140export type Pull = {
141 id: string;
142 repoId: string;
143 /** Shown as `#12`. */
144 number: number;
145 /** The number of the issue this is for, if any. */
146 issue: number | null;
147 title: string;
148 /** Markdown: what changed and why. Set when marked ready. */
149 body: string | null;
150 /** A label for the agent doing the work, e.g. `claude-code`. */
151 agent: string;
152 runtime: Runtime;
153 status: PullStatus;
154 /** The fork holding the change, unless it is on a branch. */
155 fork: RepoPath | null;
156 /** The fork's repository id. */
157 forkRepoId: string | null;
158 /** The branch of the repository holding the change, unless it is in a fork. */
159 branch: string | null;
160 headCommit: string | null;
161 /**
162 * For a merged pull request, what the branch pointed to before the merge.
163 * Comparing against it shows what the pull request changed.
164 */
165 mergeBase: string | null;
166 /** Username of whoever merged it. */
167 mergedBy: string | null;
168 /** RFC 3339. */
169 mergedAt: string | null;
170 /**
171 * Set on a pull request closed because another one for the same issue was
172 * merged: that one's number.
173 */
174 supersededBy: number | null;
175 /**
176 * Where the latest run of the issue's acceptance checks stands, if there
177 * has been one against the current head.
178 */
179 checkStatus: CheckStatus | null;
180 /** The files it changes, as of its latest push. */
181 files: ChangedFile[];
182 /** Usernames of the people it is assigned to. */
183 assignees: string[];
184 /**
185 * Those whose review was asked for: usernames, and `g1t` when a g1t
186 * agent was asked.
187 */
188 reviewers: string[];
189 /**
190 * Teams whose review was asked for, as `workspace/slug`. A team stays here
191 * after review assignment picks people from it, who are in `reviewers`.
192 */
193 teamReviewers?: string[];
194 /** The labels it carries, by name. */
195 labels?: string[];
196 /** The milestone it is in, if any. */
197 milestone?: MilestoneRef | null;
198 /**
199 * The branch it merges into. Lists and `getPull` name it; null only in
200 * what services pass between themselves, for the default branch.
201 */
202 base?: string | null;
203 /** Who opened it: a person, or g1t (`kind` `agent`, username `g1t`) for a change g1t made. */
204 author: User;
205 /**
206 * For a change g1t made: the person who asked for it, by assigning an issue
207 * or handing g1t the work. They answer for it as its author would. See
208 * `workOwner`.
209 */
210 requestedBy: User | null;
211 /** RFC 3339. */
212 createdAt: string;
213 /** RFC 3339. */
214 updatedAt: string;
215 /**
216 * How sure g1t is of a g1t agent's change, from what it can observe, once
217 * the agent has finished it. Absent before then, and on changes g1t is
218 * not seeing through.
219 */
220 confidence?: Confidence | null;
221 /** Who last moved its head (a user id), and when; absent until a push after rulesets arrived. */
222 headPushedBy?: string;
223 headPushedAt?: string;
224};
225
226/** How sure g1t is that an agent's change is right. */
227export type ConfidenceLevel = "low" | "medium" | "high";
228
229/**
230 * How sure g1t is of a change an agent made, worked out from what can be
231 * observed: its checks, how often it was sent back, the reviewer agent's
232 * verdict, whether it touched tests, its size, where it reached, how close
233 * it came to its guardrails, and what it asked without an answer. The
234 * agent's own word can only lower it.
235 */
236export type Confidence = {
237 level: ConfidenceLevel;
238 /** A few words each, most telling first: what lowered it, or for `high`, what it rests on. */
239 reasons: string[];
240 /** What the agent said of its own change, if it said. */
241 selfReported: ConfidenceLevel | null;
242 /** What the agent said it was unsure about. */
243 uncertainAbout: string[];
244 /** The agent run it was worked out after. */
245 runId: string | null;
246 /** RFC 3339. */
247 assessedAt: string;
248};
249
250/** What became of the agent when an issue was opened and handed to it in one step. */
251export type AgentStartStatus = "started" | "queued" | "not_started";
252
253/** Whether the agent started, and if not, why and what fixes it. */
254export type AgentStart = {
255 status: AgentStartStatus;
256 /**
257 * Why it did not start: `not_paid`, `trial_used`, `limit`, `paused`,
258 * `issue_cap`, `billing_unavailable` or `no_model`; `waiting` when queued.
259 */
260 code: string | null;
261 /** What happened, in a sentence or two, with what to do. */
262 message: string | null;
263 /** Where the fix is: the workspace's billing or model settings. */
264 fixUrl: string | null;
265};
266
267/** An issue opened and handed to g1t in one step. The issue exists whatever became of the agent. */
268export type Delegated = {
269 issue: Issue;
270 /** The pull request the agent opened, when it started. */
271 pull: Pull | null;
272 agent: AgentStart;
273};
274
275/**
276 * What to put an agent on: an issue's title, and what to do in plain words,
277 * with what done means if you like (a "Definition of done" section). What
278 * has to pass before it merges is the branch's required checks.
279 */
280export type DelegateInput = {
281 title: string;
282 body: string;
283 labels?: string[];
284 /** Deprecated: commands, added to the body under "Definition of done". */
285 checks?: string[];
286};
287
288/** One file a pull request changes, and by how much. */
289export type ChangedFile = { path: string; additions: number; deletions: number };
290
291/**
292 * Another pull request in progress that changes some of the same files. Two
293 * for the same issue are alternatives; two for different issues are heading
294 * for a conflict.
295 */
296export type Overlap = {
297 number: number;
298 title: string;
299 /** The number of the issue the other pull request is for. */
300 issue: number | null;
301 /** The files both change. */
302 paths: string[];
303};
304
305/**
306 * On a pull request, `failed` means the merge queue took it out, until its
307 * head moves. The other states are from runs of commands written on issues,
308 * which g1t no longer runs.
309 */
310export type CheckStatus = "queued" | "running" | "passed" | "failed" | "errored";
311
312/** How one command went, in a run recorded before checks were workflows. */
313export type CheckResult = {
314 command: string;
315 passed: boolean;
316 /** Null when the command was stopped for taking too long. */
317 exitCode: number | null;
318 /** What the command printed; the end of it, when there was a lot. */
319 output: string;
320 durationMs: number;
321};
322
323/**
324 * A record against a pull request's head: the merge queue taking it out,
325 * with why, or an earlier run of commands written on its issue.
326 */
327export type CheckRun = {
328 id: string;
329 /** The commit that was checked. */
330 headCommit: string;
331 status: CheckStatus;
332 results: CheckResult[];
333 /** Why the checks could not be run, when `status` is `errored`. */
334 error: string | null;
335 /** RFC 3339. */
336 createdAt: string;
337 /** RFC 3339. */
338 finishedAt: string | null;
339};
340
341/** A reviewer's decision on a pull request. */
342export type Verdict = "approve" | "request_changes";
343
344/**
345 * A comment on an issue or a pull request. On a pull request it can sit on
346 * one line of the change, and it can carry a reviewer's verdict.
347 */
348export type Comment = {
349 id: string;
350 /**
351 * Something a person or an agent wrote, or something that happened: an
352 * assignment, a review asked for, a close.
353 */
354 kind: "comment" | "event";
355 author: User;
356 /**
357 * Markdown. For an event, what its author did, as the rest of a sentence
358 * that starts with their name: "assigned ana".
359 */
360 body: string;
361 /** The file commented on, for a comment on a line. */
362 path: string | null;
363 /** The line of that file, as numbered after the change. */
364 line: number | null;
365 verdict: Verdict | null;
366 /** RFC 3339. */
367 createdAt: string;
368 /** When its text was last edited, RFC 3339; absent if it never was. */
369 editedAt?: string;
370 /**
371 * Set when one of the workspace's agents wrote it, as itself. `author`
372 * is then the agent (its id, its handle as `username`, kind `agent`):
373 * show it by `agent.displayName` with an Agent badge, never as a person.
374 */
375 agent?: AgentRef;
376 /** Who the agent acted for, whose access capped it. Set with `agent`. */
377 actingFor?: User;
378 /**
379 * An agent's review. Its `verdict` (none for a review that only
380 * comments) is shown but advisory: it never counts toward required
381 * approvals or code owners, and never blocks a merge.
382 */
383 advisory?: boolean;
384};
385
386/**
387 * One of a workspace's own agents as a comment or review it wrote shows
388 * it: Margo (@margo), with the face drawn from `avatarSeed` or chosen as `look`. Its page
389 * is `/<workspace>/-/agents/<handle>`.
390 */
391export type AgentRef = {
392 id: string;
393 handle: string;
394 displayName: string;
395 avatarSeed: string;
396 /** Its chosen face (./agent-look.ts), when it has one. */
397 look?: AgentLook | null;
398};
399
400/** An agent's review: all of them advisory. */
401export type AgentVerdict = "comment" | "approve" | "request_changes";
402
403/** How many comments and reviews one agent may write on one issue or pull request an hour. */
404export const AGENT_COMMENTS_PER_HOUR = 5;
405
406/** An agent as its comments name it, from the agents service's record of it. */
407export function agentRef(agent: { id: string; handle: string; display_name: string; avatar_seed: string; look?: AgentLook | null }): AgentRef {
408 return { id: agent.id, handle: agent.handle, displayName: agent.display_name, avatarSeed: agent.avatar_seed, look: agent.look ?? null };
409}
410
411export type NewComment = {
412 /** May be empty when approving. */
413 body: string;
414 path?: string;
415 line?: number;
416 verdict?: Verdict;
417};
418
419/** What a sandbox needs to carry out a check run. */
420export type CheckJob = {
421 runId: string;
422 /** Lets the sandbox, and nothing else, report this run's results. */
423 token: string;
424 commands: string[];
425 /** The repository holding the commit: the fork, or the repository itself. */
426 source: RepoPath;
427 commit: string;
428 /** Who the pull request is for (`workOwner`: whoever asked g1t for it, or its author), and so can read its source. */
429 author: User;
430 /** Username of whoever wrote the checks: the issue's author. */
431 requestedBy: string;
432 repo: RepoPath;
433 number: number;
434};
435
436export type CheckReport = { results?: CheckResult[]; error?: string; skip?: boolean };
437
438export type SessionEntryKind = "prompt" | "message" | "tool_call" | "tool_result" | "note";
439
440/** One step of an agent's session: the "why" behind a pull request's commits. */
441export type SessionEntry = {
442 seq: number;
443 kind: SessionEntryKind;
444 text: string;
445 /** For tool calls and results. */
446 tool: string | null;
447 /** The fork's head commit when this entry was recorded, if known. */
448 commit: string | null;
449 /** RFC 3339. */
450 at: string;
451};
452
453export type NewSessionEntry = Pick<SessionEntry, "kind" | "text"> &
454 Partial<Pick<SessionEntry, "tool" | "commit">>;
455
456export type IssueDetail = {
457 issue: Issue;
458 /** Every pull request made against it, oldest first. */
459 pulls: Pull[];
460 comments: Comment[];
461};
462
463export type PullDetail = {
464 pull: Pull;
465 /** The issue it is for, if any. */
466 issue: Issue | null;
467 comments: Comment[];
468 /** The latest record against its head: the merge queue taking it out. */
469 checks: CheckRun | null;
470 /** Other pull requests in progress that change the same files. */
471 overlaps: Overlap[];
472 /**
473 * Whether the branch it would merge into has moved on without it, so that
474 * it has to catch up before it can merge.
475 */
476 behind: boolean;
477 /** Whether a g1t agent is reviewing it right now. */
478 reviewPending: boolean;
479 /**
480 * Where it stands on its way to being merged, for a pull request g1t is
481 * seeing through. Null on anyone else's.
482 */
483 lifecycle: Lifecycle | null;
484 /**
485 * A merge was asked for while it was behind: g1t is bringing it up to
486 * date and will then land it.
487 */
488 landing: boolean;
489 /** Why g1t stopped working on it, if it did. */
490 stalled: string | null;
491 /** Messages people sent the agent while it worked, oldest first. */
492 messages: AgentMessage[];
493 /** What workflow runs said about its head commit, one per workflow. */
494 statuses?: CommitStatus[];
495 /**
496 * Whether it merges cleanly into the branch it targets, worked out ahead
497 * of time whenever either side moves.
498 */
499 mergeable?: Mergeable;
500 /** When `mergeable` is `conflicting`: the files that conflict. */
501 conflicts?: string[];
502 /** Earlier records like `checks`, newest first, without their output. */
503 earlierChecks?: CheckRun[];
504 /**
505 * The checks the default branch's protection requires, each as it stands
506 * on the head commit. Empty when none are required.
507 */
508 requiredChecks?: RequiredCheck[];
509 /**
510 * Who owns the files it changes (the CODEOWNERS file of the branch it
511 * merges into) and whose approval is still needed. Absent without one.
512 */
513 codeOwners?: PullCodeOwners | null;
514 /**
515 * The rules of the branch it merges into it does not meet yet, for whoever
516 * is looking: what refuses the merge, what they may bypass, and what rulesets
517 * in evaluate would refuse. Absent once it is closed or merged.
518 */
519 rules?: MergeRules | null;
520};
521
522/** Where a required check stands on a commit; `expected` when nothing has reported it yet. */
523export type RequiredState = "success" | "failure" | "pending" | "expected";
524
525/** One check a branch's protection requires, as it stands on a commit. */
526export type RequiredCheck = {
527 /** A workflow's name, such as `CI`, or another status's context. */
528 name: string;
529 state: RequiredState;
530 description: string | null;
531 /** Where to see more: the workflow run, for one a workflow reported. */
532 targetUrl: string | null;
533};
534
535/** A check name reported on a repository's commits lately, for choosing required checks. */
536export type SeenCheck = {
537 name: string;
538 /** The events it was reported for, such as `pull_request`; empty for a status that names none. */
539 events: string[];
540 /** RFC 3339. */
541 lastSeen: string;
542};
543
544/**
545 * A status context's check name: `CI / pull_request` is the `CI` check,
546 * reported for a `pull_request` event. A context that does not end in an
547 * event, such as `g1t / deploy`, is its own name. As the work service reads it.
548 */
549export function checkName(context: string): string {
550 const at = context.lastIndexOf(" / ");
551 if (at <= 0) return context;
552 return STATUS_EVENTS.has(context.slice(at + 3)) ? context.slice(0, at) : context;
553}
554
555const STATUS_EVENTS = new Set([
556 "push",
557 "pull_request",
558 "pull_request_target",
559 "pull_request_review",
560 "merge_group",
561 "workflow_dispatch",
562 "workflow_run",
563 "workflow_call",
564 "schedule",
565 "release",
566 "issues",
567 "issue_comment",
568 "repository_dispatch",
569]);
570
571/**
572 * Whether a pull request merges cleanly into the branch it targets:
573 * `unknown` when it was never worked out or could not be, `checking` while
574 * a probe merges the two.
575 */
576export type Mergeable = "clean" | "conflicting" | "unknown" | "checking";
577
578/** What a sandbox needs to find out whether a pull request merges cleanly. */
579export type MergecheckJob = {
580 pullId: string;
581 /** Lets the sandbox, and nothing else, report this probe. */
582 token: string;
583 repo: RepoPath;
584 number: number;
585 defaultBranch: string;
586 /** The default branch's commit to merge into. */
587 base: string;
588 /** The repository holding the change: its fork, or the repository. */
589 source: RepoPath;
590 /** The branch of `source` holding it. */
591 branch: string;
592 /** The change's commit. */
593 head: string;
594 /** Who the pull request is for (`workOwner`: whoever asked g1t for it, or its author), and so can read its source. */
595 author: User;
596};
597
598/** What a workflow run (or another tool) says about a commit. */
599export type CommitStatus = {
600 /** What reported it, such as `CI / push`. */
601 context: string;
602 state: "pending" | "success" | "failure" | "error";
603 description: string | null;
604 targetUrl: string | null;
605 updatedAt: string;
606 /** The integration that reported it: `actions`, `deployments`, `security`, `g1t` or `api`. */
607 source?: string;
608 /** Set on the status a check run stands as: the check run's id. */
609 checkRunId?: string;
610};
611
612/**
613 * A step on the way from an assigned issue to a pull request that is ready
614 * to merge. g1t takes each one without being asked: `working` (the agent is
615 * making the change), `checking`, `reviewing`, `revising` (the agent is
616 * addressing failed checks or a review), `catching_up` (merging in the
617 * branch it would land on), `answering` (woken to answer another agent),
618 * then `ready` for a person to merge. `needs_you`
619 * means g1t has stopped and a person decides what happens next.
620 */
621export type Stage =
622 | "working"
623 | "checking"
624 | "reviewing"
625 | "revising"
626 | "catching_up"
627 | "answering"
628 | "queued"
629 | "ready"
630 | "needs_you";
631
632export type Lifecycle = {
633 stage: Stage;
634 /** One sentence saying what is happening, or why it stopped. */
635 detail: string;
636 /** How many times the agent has been sent back to revise it. */
637 revisions: number;
638};
639
640/** What the runner needs to carry out a step of a pull request's lifecycle. */
641export type LifecycleJob = {
642 pullId: string;
643 repo: RepoPath;
644 number: number;
645 /** Who the pull request belongs to (`workOwner`: whoever asked g1t for it, or its author). Sandboxes act as them. */
646 author: User;
647 /** The repository holding the change: its fork, or the repository itself. */
648 source: RepoPath;
649 /** The branch of the source holding the change; its default branch when null. */
650 branch: string | null;
651 defaultBranch: string;
652 title: string;
653 description: string;
654 issue: Issue | null;
655 /** For a revision: the failed checks or the review to address. */
656 feedback: string;
657 /** For a revision: which one this is, from 1. */
658 round: number;
659};
660
661/** An agent woken to answer what other agents sent it while it was not at work. */
662export type Wake = { job: LifecycleJob; messages: AgentMessage[] };
663
664/**
665 * `planning` while an agent reads the repository and writes it; `ready` for
666 * a person to read and apply; `failed` if it could not be written;
667 * `applied` once its issues are open.
668 */
669export type PlanStatus = "planning" | "ready" | "failed" | "applied";
670
671/** One issue a plan proposes. */
672export type PlannedIssue = {
673 title: string;
674 /** Markdown: what to change, where, and why. */
675 body: string;
676 labels: string[];
677 /** What is true once it is done, in plain words; added to the issue's body under "Definition of done". */
678 done: string[];
679 /** The files it will most likely change. */
680 files: string[];
681 /**
682 * The positions, counting from 1, of earlier issues in the plan that have
683 * to be merged first.
684 */
685 dependsOn: number[];
686 /** Its number, once the plan has been applied and it was kept. */
687 number: number | null;
688};
689
690/** An outcome someone wrote, and the issues an agent proposes to get there. */
691export type Plan = {
692 id: string;
693 repoId: string;
694 /** The outcome wanted, as written. */
695 brief: string;
696 status: PlanStatus;
697 /** The agent's account of how it split the work. */
698 summary: string;
699 issues: PlannedIssue[];
700 /** Why it could not be written, when `status` is `failed`. */
701 error: string | null;
702 author: User;
703 /** RFC 3339. */
704 createdAt: string;
705 /** RFC 3339. */
706 finishedAt: string | null;
707 /** Once applied: where each issue it opened stands now, in plan order. */
708 progress: IssueProgress[];
709 /** Questions and handoffs between its pull requests' agents, newest first. */
710 exchanges: AgentMessage[];
711};
712
713/** What a sandbox needs to write a plan. */
714export type PlanJob = {
715 planId: string;
716 /** Lets the sandbox, and nothing else, report this plan. */
717 token: string;
718 brief: string;
719 repo: RepoPath;
720};
721
722/** An issue waiting for a g1t agent that can be given one now. */
723export type ReadyIssue = {
724 repo: RepoPath;
725 number: number;
726 /** Who queued it, on whose say-so the agent works. */
727 actor: User;
728};
729
730/**
731 * How a repository wants its pull requests handled. A repository that has
732 * changed nothing has the defaults.
733 */
734export type RepoSettings = {
735 /**
736 * Land a g1t agent's pull request without a person once it is ready:
737 * checks passed and approved as the settings below require.
738 */
739 autoMerge: boolean;
740 /**
741 * The checks that must pass on a pull request's head before it may merge
742 * into the default branch, by name: a workflow's name (`CI`) or another
743 * status's context (`g1t / deploy`). The same for people and agents, and
744 * for the merge queue.
745 */
746 requiredChecks: string[];
747 /**
748 * Refuse to merge a pull request that does not contain the default
749 * branch's latest commits, so that what merges is what was checked. When
750 * off, merging one that is behind brings it up to date first.
751 */
752 requireUpToDate: boolean;
753 /**
754 * How many approving reviews a pull request needs before it may merge. A
755 * reviewer who has since asked for changes blocks it.
756 */
757 requiredApprovals: number;
758 /** Whether a g1t agent's approval counts towards `requiredApprovals`. */
759 countAgentApprovals: boolean;
760 /** Whether someone who may merge can bypass required checks that have not passed. */
761 allowIgnoringChecks: boolean;
762 /** Whether a g1t agent's pull request is reviewed by a second agent unasked. */
763 agentReview: boolean;
764 /**
765 * How many times a g1t agent is sent back to its pull request before a
766 * person is asked instead.
767 */
768 maxRevisions: number;
769 /**
770 * Merge through a queue: pull requests are tested together with those
771 * ahead of them, and only a combination that passed reaches the default
772 * branch.
773 */
774 mergeQueue: boolean;
775 /**
776 * Ask a person before merging a g1t agent's change whose confidence is
777 * low: auto-merge and the merge queue leave it until a person approves it.
778 */
779 holdLowConfidence: boolean;
780 /**
781 * Refuse to merge until the code owners of every file it changes have
782 * approved, as many as each section asks. People only; `g1t` only where
783 * the file names `@g1t`.
784 */
785 requireCodeOwnerReview?: boolean;
786 /** Username of the member who last changed the settings, if anyone has. */
787 updatedBy: string | null;
788 /** RFC 3339. */
789 updatedAt: string | null;
790};
791
792/** The settings a member can change. */
793export type RepoSettingsInput = Omit<RepoSettings, "updatedBy" | "updatedAt">;
794
795/** The next step for a pull request g1t is seeing through, already claimed. */
796export type Advance =
797 | { action: "none" }
798 | { action: "review" | "revise" | "catch_up"; job: LifecycleJob };
799
800/** What a sandbox needs to review a pull request. */
801export type ReviewJob = {
802 runId: string;
803 /** Lets the sandbox, and nothing else, report this review. */
804 token: string;
805 /** The repository holding the commit: the fork, or the repository itself. */
806 source: RepoPath;
807 commit: string;
808 repo: RepoPath;
809 defaultBranch: string;
810 number: number;
811 title: string;
812 description: string;
813 /** The issue the pull request is for, which says what it should achieve. */
814 issue: Issue | null;
815 /** Who the pull request is for (`workOwner`: whoever asked g1t for it, or its author), and so can read its source. */
816 author: User;
817 /**
818 * The files it changes, as of its latest push: how large the change is,
819 * which decides the model that reviews it.
820 */
821 files: ChangedFile[];
822 /**
823 * What among them runs, configures or guards things (CI workflows,
824 * secrets, infrastructure), once each. Any sends the review to the
825 * larger model.
826 */
827 sensitive: string[];
828};
829
830export type OpenIssueInput = {
831 title: string;
832 body: string;
833 labels?: string[];
834 /** Deprecated: commands, added to the body under "Definition of done". */
835 checks?: string[];
836 /** The number of the milestone to put it in. Needs the Triage role. */
837 milestone?: number;
838};
839
840export type UpdateIssueInput = {
841 title?: string;
842 body?: string;
843 labels?: string[];
844 /** Usernames of the people it is assigned to; replaces the whole set. */
845 assignees?: string[];
846 /** The number of the milestone to put it in; 0 takes it out. */
847 milestone?: number;
848};
849
850export type OpenPullInput = {
851 /** The number of the issue this is for. */
852 issue?: number;
853 /** Defaults to the issue's title; required without an issue. */
854 title?: string;
855 /** What changed and why. Usually set later, when a draft is marked ready. */
856 body?: string;
857 /**
858 * A branch of the repository that already holds the change. The pull
859 * request is then ready for review at once and has no fork.
860 */
861 branch?: string;
862 agent: string;
863 runtime: Runtime;
864 /** The branch to merge into: the default branch when left out. */
865 base?: string;
866 /**
867 * Opened from a branch as a draft, still being worked on: it can't merge,
868 * and agents' review routines wait, until it is marked ready.
869 */
870 draft?: boolean;
871};
872
873/** One repository's pull requests from `pullsForRepos`, newest first. */
874export type RepoPulls = {
875 repoId: string;
876 /** Draft and open. */
877 open: Pull[];
878 /** Merged and closed. */
879 closed: Pull[];
880};
881
882/** Issues, pull requests, comments and sessions. */
883export interface WorkApi extends RulesApi, ChecksApi {
884 openIssue(actor: User, repo: RepoPath, input: OpenIssueInput): Promise<Result<Issue>>;
885 /**
886 * Opens an issue to put g1t on at once: refused, with nothing
887 * opened, unless `actor` may put agents to work in `repo`. The runner's
888 * `delegate` calls it, then starts the agent.
889 */
890 delegateIssue(actor: User, repo: RepoPath, input: DelegateInput): Promise<Result<Issue>>;
891 /** Newest first. */
892 listIssues(
893 repo: RepoPath,
894 viewer: Viewer,
895 filter?: { state?: State; label?: string; milestone?: number },
896 ): Promise<Result<Issue[]>>;
897 getIssue(repo: RepoPath, number: number, viewer: Viewer): Promise<Result<IssueDetail>>;
898 /** The author or a member of the workspace may. */
899 updateIssue(actor: User, repo: RepoPath, number: number, input: UpdateIssueInput): Promise<Result<Issue>>;
900 closeIssue(actor: User, repo: RepoPath, number: number, reason?: IssueReason): Promise<Result<Issue>>;
901 reopenIssue(actor: User, repo: RepoPath, number: number): Promise<Result<Issue>>;
902 /** A repository's labels, by name, each with how many issues and pull requests carry it. */
903 listLabels(repo: RepoPath, viewer: Viewer): Promise<Result<Label[]>>;
904 /**
905 * Creates a label, or with `name` changes one; renaming it renames it on
906 * everything that carries it. Needs the Triage role.
907 */
908 saveLabel(
909 actor: User,
910 repo: RepoPath,
911 label: { name?: string; newName?: string; color?: string; description?: string },
912 ): Promise<Result<Label>>;
913 /** Removes a label from the repository and everything carrying it. */
914 deleteLabel(actor: User, repo: RepoPath, name: string): Promise<Result<boolean>>;
915 /** Adds the default labels the repository does not have yet; returns them all. */
916 addDefaultLabels(actor: User, repo: RepoPath): Promise<Result<Label[]>>;
917 /**
918 * The labels of an issue or a pull request, replaced, added to or taken
919 * from. Labels the repository lacks are created for someone with the
920 * Triage role. Returns its labels now.
921 */
922 setLabels(
923 actor: User,
924 repo: RepoPath,
925 number: number,
926 labels: string[],
927 change?: LabelChange,
928 ): Promise<Result<string[]>>;
929 /** Open ones by due date, then closed ones; both unless `state` says. */
930 listMilestones(repo: RepoPath, viewer: Viewer, state?: State): Promise<Result<Milestone[]>>;
931 getMilestone(repo: RepoPath, number: number, viewer: Viewer): Promise<Result<MilestoneDetail>>;
932 /** Creates a milestone, or with `number` changes the fields given. Needs the Triage role. */
933 saveMilestone(
934 actor: User,
935 repo: RepoPath,
936 milestone: { number?: number; title?: string; description?: string; dueOn?: string; state?: State },
937 ): Promise<Result<Milestone>>;
938 deleteMilestone(actor: User, repo: RepoPath, number: number): Promise<Result<boolean>>;
939 /** How many issues and pull requests are open. */
940 counts(repo: RepoPath, viewer: Viewer): Promise<Result<{ issues: number; pulls: number }>>;
941
942 /**
943 * On an issue or a pull request. On a pull request it may name a line of
944 * the change and carry a verdict; nobody can give a verdict on their own.
945 */
946 addComment(actor: User, repo: RepoPath, number: number, comment: NewComment): Promise<Result<Comment>>;
947 /**
948 * Changes a comment's text. Its author may, and so may anyone with the
949 * Maintain role or higher; notes of what happened cannot be changed.
950 */
951 editComment(actor: User, repo: RepoPath, commentId: string, body: string): Promise<Result<Comment>>;
952 /**
953 * Deletes a comment: its author, or anyone with the Maintain role or
954 * higher. A review that gave a verdict cannot be deleted, only edited.
955 */
956 deleteComment(actor: User, repo: RepoPath, commentId: string): Promise<Result<boolean>>;
957 /**
958 * One of the workspace's agents comments on an issue or a pull request
959 * as itself, on behalf of `actingFor`: the person whose access caps it.
960 * The checks a person's comment has apply to them (verified, can read
961 * the repository, not archived). At most `AGENT_COMMENTS_PER_HOUR`
962 * comments and reviews per agent per issue or pull request
963 * (`conflict` past it). Publishes `comment.created` with `agent`. For
964 * the agents service only: never reachable with a person's token.
965 */
966 workspaceAgentComment(
967 repo: RepoPath,
968 number: number,
969 agent: AgentRef,
970 actingFor: User,
971 body: string,
972 ): Promise<Result<Comment>>;
973 /**
974 * One of the workspace's agents reviews a pull request as itself, on
975 * behalf of `actingFor`. Advisory: the verdict is shown but never counts
976 * toward required approvals or code owners, and never blocks a merge.
977 * Refused on a draft and on a closed or merged pull request (`conflict`);
978 * `body` may be empty only when approving. Same checks and limit as
979 * `workspaceAgentComment`. Publishes `comment.created` with `agent`,
980 * `advisory` and the verdict.
981 */
982 workspaceAgentReview(
983 repo: RepoPath,
984 number: number,
985 agent: AgentRef,
986 actingFor: User,
987 verdict: AgentVerdict,
988 body: string,
989 ): Promise<Result<Comment>>;
990
991 /**
992 * Always refused now: a pull request's checks are the workflows run on
993 * it. Kept for a runner from before.
994 */
995 startChecks(pullId: string): Promise<Result<CheckJob>>;
996 /**
997 * What a sandbox says about a run from before checks were workflows: so
998 * one still finishing is recorded.
999 */
1000 reportChecks(runId: string, token: string, report: CheckReport): Promise<Result<CheckRun>>;
1001 /** Begins a review by a g1t agent. For the runner service. */
1002 startReview(pullId: string): Promise<Result<ReviewJob>>;
1003 /** Records that a review could not be written. For the runner service. */
1004 failReview(runId: string, token: string, error: string): Promise<Result<boolean>>;
1005 /**
1006 * Claims the probe of whether a pull request merges cleanly that a
1007 * `pull.mergecheck` event asked for. Refused when it is no longer wanted
1008 * or the repository has as many running as it may. For the runner service.
1009 */
1010 startMergecheck(pullId: string): Promise<Result<MergecheckJob>>;
1011 /** Records that a probe could not be carried out. For the runner service. */
1012 failMergecheck(pullId: string, token: string, error: string): Promise<Result<Mergeable>>;
1013
1014 /**
1015 * Works out the next step for a pull request g1t is seeing through and,
1016 * if there is one to take now, claims it, so that it is taken once
1017 * however often this is called. For the runner service.
1018 */
1019 advance(pullId: string): Promise<Advance>;
1020 /** Records that a step could not be carried out, so a person is asked. */
1021 stall(pullId: string, reason: string): Promise<boolean>;
1022 /** Ids of the open pull requests g1t is seeing through. */
1023 managedPulls(repoId?: string): Promise<string[]>;
1024
1025 /** A repository's merge queue: what is in it, in order, and what recently left. */
1026 queue(repo: RepoPath, viewer: Viewer): Promise<Result<QueueView>>;
1027 /**
1028 * The next batch of combined states to test for a repository, one per
1029 * entry; empty while a batch is being tested or nothing waits.
1030 */
1031 queueBuild(repoId: string): Promise<QueueJob[]>;
1032 /** Reports that a combined state could not be built or checked. */
1033 failQueue(entryId: string, token: string, error: string): Promise<Result<QueueState>>;
1034 /** Sends the agent working on a pull request a message, for its next step. */
1035 messageAgent(actor: User, repo: RepoPath, number: number, body: string): Promise<Result<AgentMessage>>;
1036 /** Takes a pull request out of the merge queue. Members only. */
1037 removeFromQueue(actor: User, repo: RepoPath, number: number): Promise<Result<Pull>>;
1038
1039 getSettings(repo: RepoPath, viewer: Viewer): Promise<Result<RepoSettings>>;
1040 /** The check names reported on the repository's commits in the last 30 days, most recent first. */
1041 seenChecks(repo: RepoPath, viewer: Viewer): Promise<Result<SeenCheck[]>>;
1042 /** Members of the repository's workspace only. */
1043 updateSettings(actor: User, repo: RepoPath, settings: RepoSettingsInput): Promise<Result<RepoSettings>>;
1044 /** The CODEOWNERS file at a branch (the default when left out), checked. Needs Read. */
1045 codeownersErrors(repo: RepoPath, viewer: Viewer, ref?: string | null): Promise<Result<CodeownersReport>>;
1046 /**
1047 * What the runner needs to bring a pull request up to date because a
1048 * merge of it was asked for. Null if none was.
1049 */
1050 catchUpJob(pullId: string): Promise<LifecycleJob | null>;
1051 /**
1052 * Claims a short step for the agent on a pull request to answer the
1053 * questions and handoffs it was sent while not at work, and hands them
1054 * over, marked read. Null when there is nothing waiting or it cannot
1055 * take a step now.
1056 */
1057 wakeForMessages(pullId: string): Promise<Wake | null>;
1058
1059 /**
1060 * Opens a pull request: a draft with a fork to push to, or, given a
1061 * branch, one ready for review.
1062 */
1063 openPull(actor: User, repo: RepoPath, input: OpenPullInput): Promise<Result<Pull>>;
1064 /** Newest first. */
1065 listPulls(
1066 repo: RepoPath,
1067 viewer: Viewer,
1068 state?: State,
1069 filter?: { label?: string; milestone?: number; base?: string },
1070 ): Promise<Result<Pull[]>>;
1071 /**
1072 * The newest `limit` open and closed pull requests of each repository,
1073 * in one call. Repositories the viewer cannot read, and forks, are left
1074 * out: ask those with `listPulls`.
1075 */
1076 pullsForRepos(repoIds: string[], viewer: Viewer, limit: number): Promise<RepoPulls[]>;
1077 getPull(repo: RepoPath, number: number, viewer: Viewer): Promise<Result<PullDetail>>;
1078 /**
1079 * Changes who a pull request is assigned to and whose review is asked
1080 * for; each list given replaces the whole set. Asking for `g1t`'s
1081 * review does not by itself start one: the runner's `review` does.
1082 */
1083 updatePull(
1084 actor: User,
1085 repo: RepoPath,
1086 number: number,
1087 changes: {
1088 assignees?: string[];
1089 reviewers?: string[];
1090 labels?: string[];
1091 /** 0 takes it out of its milestone. */
1092 milestone?: number;
1093 /** The branch it merges into. Needs the Write role. */
1094 base?: string;
1095 },
1096 ): Promise<Result<Pull>>;
1097 /**
1098 * Brings a pull request up to date with the default branch in seconds,
1099 * without a sandbox, when the two changed different files: the merge
1100 * commit is pushed to its branch as `actor`, who must be whoever opened
1101 * it (for a fork) or a member (for a branch). Otherwise `needs_agent`, and
1102 * nothing is pushed: the runner's `update` is the way on.
1103 */
1104 catchUpPull(actor: User, repo: RepoPath, number: number): Promise<Result<PullBranchUpdate>>;
1105 /** Marks a draft ready for review and sets its description. */
1106 readyPull(actor: User, repo: RepoPath, number: number, summary: string): Promise<Result<Pull>>;
1107 closePull(actor: User, repo: RepoPath, number: number): Promise<Result<Pull>>;
1108 /** Opens a closed pull request again, as the draft it was if it was closed as one. Never a merged one. */
1109 reopenPull(actor: User, repo: RepoPath, number: number): Promise<Result<Pull>>;
1110 /** Turns an open pull request back into a draft; it leaves the merge queue. */
1111 convertPullToDraft(actor: User, repo: RepoPath, number: number): Promise<Result<Pull>>;
1112 /**
1113 * Lands the pull request on the repository's default branch. Unless
1114 * `keepIssueOpen`, that resolves the issue it was for: the issue closes
1115 * naming this pull request, and the others still in progress for it close
1116 * as superseded. Only members of the repository's workspace may merge.
1117 *
1118 * If the default branch has moved, the pull request is brought up to date
1119 * first and lands when that is done; it comes back still open, and
1120 * `PullDetail.landing` is true meanwhile. A repository that requires pull
1121 * requests to be up to date refuses instead.
1122 */
1123 mergePull(
1124 actor: User,
1125 repo: RepoPath,
1126 number: number,
1127 options?: {
1128 keepIssueOpen?: boolean;
1129 /** Merge although required checks have not passed, where the rule requiring them allows it. */
1130 ignoreChecks?: boolean;
1131 /** Merge past rules a ruleset lets the actor bypass. Recorded as a bypass. */
1132 bypassRules?: boolean;
1133 },
1134 ): Promise<Result<Pull>>;
1135 /**
1136 * Drafts and open pull requests the viewer started, most recently active
1137 * first, each with where it stands if g1t is seeing it through.
1138 */
1139 listActivePulls(
1140 viewer: Viewer,
1141 ): Promise<{ pull: Pull; issue: Issue | null; lifecycle: Lifecycle | null }[]>;
1142 /**
1143 * The issues and pull requests a person opened, a page at a time, only
1144 * on repositories the viewer may read. Not found for no such account.
1145 */
1146 byAuthor(username: string, viewer: Viewer, filter?: AuthoredFilter): Promise<Result<Authored>>;
1147 /**
1148 * What a person did each day of the last year (issues and pull requests
1149 * opened, reviews given), only on repositories the viewer may read: the
1150 * calendar on their profile. Not found for no such account.
1151 */
1152 contributions(username: string, viewer: Viewer): Promise<Result<Contributions>>;
1153
1154 /**
1155 * Records an outcome to plan for. Members only. For the runner service,
1156 * which starts the sandbox in which an agent writes the plan.
1157 */
1158 startPlan(actor: User, repo: RepoPath, brief: string): Promise<Result<PlanJob>>;
1159 /** Records that a plan could not be written. For the runner service. */
1160 failPlan(planId: string, token: string, error: string): Promise<Result<boolean>>;
1161 /** Members only. */
1162 getPlan(repo: RepoPath, viewer: Viewer, id: string): Promise<Result<Plan>>;
1163 /** Newest first. Members only. */
1164 listPlans(repo: RepoPath, viewer: Viewer): Promise<Result<Plan[]>>;
1165 /**
1166 * Opens a plan's issues, each blocked by the ones it depends on. With
1167 * `assign`, each is queued for a g1t agent. `keep` holds the positions,
1168 * from 1, of the issues to open; all of them when absent. Once.
1169 */
1170 applyPlan(
1171 actor: User,
1172 repo: RepoPath,
1173 id: string,
1174 options?: { assign?: boolean; keep?: number[] },
1175 ): Promise<Result<Plan>>;
1176 /** Asks for a g1t agent to take an issue as soon as it can, or withdraws that. */
1177 queueIssue(actor: User, repo: RepoPath, number: number, queued: boolean): Promise<Result<boolean>>;
1178 /** Issues waiting for a g1t agent that can be given one now. For the runner. */
1179 readyIssues(repoId?: string): Promise<ReadyIssue[]>;
1180
1181 /** Open issues assigned to the viewer, most recently changed first. */
1182 listAssignedIssues(viewer: Viewer): Promise<Issue[]>;
1183
1184 appendSession(actor: User, repo: RepoPath, number: number, entries: NewSessionEntry[]): Promise<Result<{ count: number }>>;
1185 readSession(repo: RepoPath, number: number, viewer: Viewer, afterSeq?: number): Promise<Result<SessionEntry[]>>;
1186}
1187
1188/**
1189 * What to pass `ReposApi.compare` to see what a pull request changes.
1190 *
1191 * A fork is compared as a whole. A branch is compared by name while the
1192 * pull request is open, and by the commit it was merged or closed at
1193 * afterwards, so later pushes to the branch do not change the record.
1194 */
1195export function pullComparison(pull: Pull): {
1196 repoId: string;
1197 base: string | null;
1198 head: string | null;
1199 /** The branch it merges into, which it is compared from. */
1200 baseBranch: string | null;
1201} {
1202 const baseBranch = pull.base ?? null;
1203 if (pull.forkRepoId) return { repoId: pull.forkRepoId, base: pull.mergeBase, head: null, baseBranch };
1204 const settled = pull.status === "merged" || pull.status === "closed";
1205 return {
1206 repoId: pull.repoId,
1207 base: pull.mergeBase,
1208 head: (settled && pull.headCommit) || pull.branch,
1209 baseBranch,
1210 };
1211}
1212
1213
1214/** Where a pull request in a merge queue stands. */
1215/** Where one issue of an applied plan stands. */
1216export type IssueProgress = {
1217 number: number;
1218 title: string;
1219 /**
1220 * `blocked`, `waiting` (for an agent), `open`, a lifecycle stage,
1221 * `landed` or `closed`.
1222 */
1223 state: string;
1224 detail: string;
1225 /** The issues it is waiting on that are still open. */
1226 blockedBy: number[];
1227 pull: number | null;
1228 agent: string | null;
1229};
1230
1231/** A message a person sent an agent at work on a pull request. */
1232export type AgentMessage = {
1233 id: string;
1234 author: string;
1235 body: string;
1236 createdAt: string;
1237 /** When the agent received it; null until then. */
1238 deliveredAt: string | null;
1239 /** `message` from a person; from an agent a `question`, `handoff` or `answer`. */
1240 kind: "message" | "question" | "handoff" | "answer";
1241 /** The pull request whose agent sent it, when an agent did. */
1242 fromNumber: number | null;
1243 /** The pull request it was sent to. */
1244 toNumber: number;
1245 /** The reply to a question or handoff, once there is one. */
1246 answer: string | null;
1247 declined: boolean;
1248 /** For the sending agent: what to expect when the one it asked is not at work. */
1249 hint?: string;
1250};
1251
1252export type QueueState = "waiting" | "testing" | "passed" | "failed" | "landed" | "removed";
1253
1254/** One pull request's place in a merge queue. */
1255export type QueueEntry = {
1256 id: string;
1257 number: number;
1258 title: string;
1259 agent: string;
1260 state: QueueState;
1261 /** The pull requests merged ahead of it in the state being tested, in order. */
1262 ahead: number[];
1263 baseCommit: string | null;
1264 combinedCommit: string | null;
1265 error: string | null;
1266 results: CheckResult[];
1267 /** Username of whoever merged it into the queue: a person, or `g1t`. */
1268 enqueuedBy: string;
1269 /** RFC 3339. */
1270 createdAt: string;
1271 /** RFC 3339. */
1272 finishedAt: string | null;
1273};
1274
1275/** A repository's merge queue: what is in it, in order, and what recently left. */
1276export type QueueView = {
1277 enabled: boolean;
1278 active: QueueEntry[];
1279 /** Newest first. */
1280 recent: QueueEntry[];
1281};
1282
1283export type QueueStackItem = {
1284 number: number;
1285 title: string;
1286 /** The repository holding the change, and its branch. */
1287 source: RepoPath;
1288 branch: string;
1289 commit: string;
1290};
1291
1292/** What a sandbox needs to build and check one combined state. */
1293export type QueueJob = {
1294 entryId: string;
1295 token: string;
1296 repo: RepoPath;
1297 defaultBranch: string;
1298 baseCommit: string;
1299 branch: string;
1300 stack: QueueStackItem[];
1301 /** Always empty: the state is checked by the merge_group workflows run on it. */
1302 checks: string[];
1303 /** Always empty, as `checks`. */
1304 contractChecks: string[];
1305 actor: User;
1306};
1307
1308// --- A person's work ---------------------------------------------------------
1309// Mirrors the `Authored*` types in `crates/contracts/src/work.rs`.
1310
1311export type AuthoredKind = "issue" | "pull";
1312/** `closed` takes in merged pull requests too; `merged` is only those. */
1313export type AuthoredState = "open" | "closed" | "merged";
1314/** `created` is newest first, `updated` most recently changed, `oldest` oldest first. */
1315export type AuthoredSort = "created" | "updated" | "oldest";
1316
1317/** The most items one `byAuthor` page holds. */
1318export const AUTHORED_PAGE = 25;
1319
1320export type AuthoredFilter = {
1321 kind?: AuthoredKind;
1322 state?: AuthoredState;
1323 /** `namespace/name`. */
1324 repo?: string;
1325 sort?: AuthoredSort;
1326 /** The `next` of the page before. */
1327 before?: string;
1328 limit?: number;
1329};
1330
1331export type AuthoredItem = {
1332 kind: AuthoredKind;
1333 repo: RepoPath;
1334 number: number;
1335 title: string;
1336 /** A merged pull request is closed. */
1337 state: State;
1338 /** A pull request's own status; null on an issue. */
1339 status: PullStatus | null;
1340 /** Why an issue was closed. */
1341 reason: IssueReason | null;
1342 draft: boolean;
1343 merged: boolean;
1344 /** RFC 3339. */
1345 createdAt: string;
1346 /** RFC 3339. */
1347 updatedAt: string;
1348 /** RFC 3339. */
1349 mergedAt: string | null;
1350};
1351
1352/** Over every repository the viewer may read, whatever the filters. */
1353export type AuthoredCounts = {
1354 pullsMerged: number;
1355 pullsOpen: number;
1356 pulls: number;
1357 issues: number;
1358 issuesOpen: number;
1359};
1360
1361export type Authored = {
1362 items: AuthoredItem[];
1363 /** Pass as `before` for the next page; null on the last. */
1364 next: string | null;
1365 counts: AuthoredCounts;
1366 /** The repositories they worked in that the viewer may read, most work first. */
1367 repos: { repo: RepoPath; count: number }[];
1368};
1369
1370// Mirrors `Contributions` in `crates/contracts/src/work.rs`.
1371
1372/** How many days `contributions` covers: today and the 364 before it. */
1373export const CONTRIBUTION_DAYS = 365;
1374
1375/** One day with something on it; days with nothing are left out. */
1376export type ContributionDay = {
1377 /** `YYYY-MM-DD`, UTC. */
1378 date: string;
1379 /** Everything that day, commits included. */
1380 count: number;
1381 /** How many of `count` are commits they pushed to a default branch. */
1382 commits?: number;
1383};
1384
1385/** A person's year, as far as the viewer may see. */
1386export type Contributions = {
1387 /** Oldest first. */
1388 days: ContributionDay[];
1389 total: number;
1390 /** The first day counted, `YYYY-MM-DD`; the last is today (UTC). */
1391 from: string;
1392};