Skip to content

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

1,951 linesCodeBlameRaw
1import type { ChecksApi } from "./checks";
2import type { CodeownersReport, PullCodeOwners } from "./codeowners";
3import type { G1tEvent } from "./events";
4import type { User, Viewer } from "./identity";
5import type { PullBranchUpdate, RepoPath, UpdateMethod } from "./repos";
6import type { MergeMethod } from "./rules";
7import type { Result } from "./result";
8import type { MergeRules, RulesApi } from "./rules";
9
10/**
11 * The filter on lists of work items and pull requests. An open pull
12 * request is a draft or one ready for review; a closed one was merged or
13 * closed without merging.
14 */
15export type State = "open" | "closed";
16
17/** Why a work item was closed. */
18export type CloseReason = "completed" | "not_planned";
19
20/**
21 * How a work item is linked to a repository: where it was filed (`origin`,
22 * which keeps an old issue's number), where the work is meant to land
23 * (`target`), or where work for it landed or is under way (`touched`).
24 */
25export type LinkRole = "origin" | "target" | "touched";
26
27/** A repository a work item is linked to. */
28export type ItemRepo = {
29 repoId: string;
30 /** `owner/name`, as it is now, or as it was when the repository was deleted. */
31 path: string;
32 role: LinkRole;
33 /** For the origin of a work item moved from a repository's issues: its number there. */
34 legacyNumber: number | null;
35 /** A pull request or commit that closes it landed in this repository. */
36 landed: boolean;
37 /**
38 * The repository is deleted, and can still be restored until it is
39 * purged: `path` is where it was. It has nothing to open or read.
40 */
41 deleted?: boolean;
42};
43
44/** What resolved a work item in one repository: a pull request merged, or a commit pushed. */
45export type Resolution = {
46 /** `owner/name`. */
47 repo: string;
48 pull: number | null;
49 commit: string | null;
50 /** Who pushed that commit, by username. */
51 pushedBy: string | null;
52};
53
54/**
55 * Any tracked work of a workspace: a feature, a bug, a chore. Numbered in
56 * the workspace with its new pull requests, and linked to the repositories
57 * it concerns. It closes once what closes it has landed in every target,
58 * or, with no targets, once anything that closes it lands. Mirrors
59 * `crates/contracts/src/work/items.rs`.
60 */
61export type WorkItem = {
62 /** `wki_…`; one moved from an issue keeps its `iss_…`. */
63 id: string;
64 /** The workspace's slug. */
65 workspace: string;
66 /** Shown as `#12`; on the site at `/<workspace>/-/work/<number>`. */
67 number: number;
68 title: string;
69 /** Markdown. Also what an agent is given to work from. */
70 body: string;
71 labels: string[];
72 state: State;
73 /** Set when closed. */
74 reason: CloseReason | null;
75 /** What resolved it, in each repository it landed in, oldest first. */
76 resolutions: Resolution[];
77 /** Who opened it: a person, an integration, or g1t (`kind` `agent`) for one its agent filed while at work. */
78 author: User;
79 /**
80 * For one g1t's agent filed: the person it was working for. They may
81 * manage it as its author could. See `workOwner`.
82 */
83 requestedBy: User | null;
84 /** RFC 3339. */
85 createdAt: string;
86 /** RFC 3339. */
87 updatedAt: string;
88 /** RFC 3339. */
89 closedAt: string | null;
90 /** Pull requests made for it, in any state and repository. */
91 pullCount: number;
92 commentCount: number;
93 /** Usernames of the people it is assigned to. */
94 assignees: string[];
95 /** The numbers of the work items of the workspace that have to land before this one is worked on. */
96 blockedBy: number[];
97 /**
98 * The agent working on it now: the one behind its newest pull request
99 * that is still in progress in a fork, such as `g1t`.
100 */
101 agent: string | null;
102 /** The workspace milestone it is in, if any. */
103 milestone?: MilestoneRef | null;
104 /** The repositories it is linked to: its origin first, then targets, then those it touched. */
105 repos: ItemRepo[];
106};
107
108/** The targets of a work item that have not landed yet, by path. */
109export function missingTargets(repos: ItemRepo[]): string[] {
110 return repos.filter((repo) => repo.role === "target" && !repo.landed).map((repo) => repo.path);
111}
112
113/** A work item and everything on it. */
114export type WorkItemDetail = {
115 item: WorkItem;
116 /** Every pull request made for it, in the repositories the viewer may read, oldest first. */
117 pulls: Pull[];
118 comments: Comment[];
119 /** How people (and g1t) reacted to its description. */
120 reactions: ReactionGroup[];
121};
122
123/** What `#N` names (`resolveRef`): see `refs.ts` for the order it is read in. */
124export type RefTarget =
125 | { kind: "work_item"; workspace: string; number: number; itemId: string }
126 | { kind: "pull"; repo: string; number: number }
127 | { kind: "none" };
128
129/**
130 * What a comment, a reaction or a set of labels is on: a pull request of a
131 * repository, or a work item of a workspace.
132 */
133export type Thread = { repo: RepoPath; number: number } | { workspace: string; number: number };
134
135/**
136 * A label of a workspace: a name, a color and what it means. Work items and
137 * the pull requests of its repositories carry labels by name; names are
138 * lowercase.
139 */
140export type Label = {
141 name: string;
142 /** Six hex digits, without `#`. */
143 color: string;
144 description: string;
145 /** How many work items carry it, open or closed. */
146 items: number;
147 /** How many pull requests carry it, in any state. */
148 pulls: number;
149};
150
151/** A work item a commit resolved, as the commit page lists it. */
152export type ClosedItem = { workspace: string; number: number; title: string; closed: boolean };
153
154/** A milestone, as a work item or pull request names it. */
155export type MilestoneRef = { number: number; title: string };
156
157/**
158 * A goal of a workspace, with an optional due date, that work items and
159 * pull requests are gathered under. Its progress is how many of them are
160 * closed.
161 */
162export type Milestone = {
163 /** Numbered from 1 in each workspace, apart from work items. */
164 number: number;
165 title: string;
166 /** Markdown. */
167 description: string;
168 /** `YYYY-MM-DD`. */
169 dueOn: string | null;
170 state: State;
171 /** Open work items and pull requests in it. */
172 openItems: number;
173 /** Closed work items, and merged or closed pull requests, in it. */
174 closedItems: number;
175 createdAt: string;
176 updatedAt: string;
177 closedAt: string | null;
178 /** For one moved from a repository when work items moved to the workspace: its id. */
179 originRepoId?: string;
180};
181
182/** One milestone and what is in it that the viewer may see, newest first. */
183export type MilestoneDetail = { milestone: Milestone; items: WorkItem[]; pulls: Pull[] };
184
185/** How `setLabels` changes an item's labels. */
186export type LabelChange = "set" | "add" | "remove";
187
188/**
189 * Whose a work item or a pull request is to answer for: whoever asked g1t for
190 * it, or its author. They may change, close and steer it, are never asked to
191 * review it and cannot approve it, and see it as theirs. Mirrors
192 * `Pull::owner` in the Rust contracts.
193 */
194export function workOwner(item: Pick<Pull, "author" | "requestedBy">): User {
195 return item.requestedBy ?? item.author;
196}
197
198/** `draft` is still being worked on; `open` is ready for review. */
199export type PullStatus = "draft" | "open" | "merged" | "closed";
200
201/** Where the agent runs: on g1t's sandboxes, or in someone's own session. */
202export type Runtime = "hosted" | "external";
203
204/**
205 * A proposed change. It is made either in a fork created for it, which is
206 * how agents work, on a branch pushed to the repository itself, or on a
207 * branch of someone's fork of it (`headRepo`).
208 */
209export type Pull = {
210 id: string;
211 repoId: string;
212 /** Shown as `#12`. New ones are numbered in the workspace's sequence, which work items share. */
213 number: number;
214 /** The work item this is for, if any, by number in the workspace. */
215 item: number | null;
216 title: string;
217 /** Markdown: what changed and why. Set when marked ready. */
218 body: string | null;
219 /** A label for the agent doing the work, e.g. `claude-code`. */
220 agent: string;
221 runtime: Runtime;
222 status: PullStatus;
223 /** The fork holding the change, unless it is on a branch. */
224 fork: RepoPath | null;
225 /** The fork's repository id. */
226 forkRepoId: string | null;
227 /**
228 * The branch holding the change, unless it is in a working copy (`fork`):
229 * of `headRepo` when set, else of the repository.
230 */
231 branch: string | null;
232 /**
233 * A fork of the repository (its `parentId`, directly or through other
234 * forks) whose `branch` holds the change, when it was opened from one.
235 * Null for a branch of the repository itself and for a working copy.
236 */
237 headRepo?: RepoPath | null;
238 /** `headRepo`'s id. */
239 headRepoId?: string | null;
240 headCommit: string | null;
241 /**
242 * For a merged pull request, what the branch pointed to before the merge.
243 * Comparing against it shows what the pull request changed.
244 */
245 mergeBase: string | null;
246 /** Username of whoever merged it. */
247 mergedBy: string | null;
248 /** RFC 3339. */
249 mergedAt: string | null;
250 /** How it merged, once merged. */
251 mergeMethod?: MergeMethod | null;
252 /** Where its merge left the base: the merge or squash commit, the last rebased one, or its head. */
253 mergeCommit?: string | null;
254 /** Merge it on its own once everything it needs is met, as a person asked. */
255 autoMerge?: AutoMerge | null;
256 /**
257 * The work items it closes when it merges into the default branch, by
258 * number: its own and those its title and description name after a
259 * closing keyword (`Fixes #12`). Merging marks this repository's link to
260 * each as landed.
261 */
262 closes?: number[];
263 /** The work items it is part of (`Part of #12`, `Refs #12`): linked, never closed by it. */
264 parts?: number[];
265 /** Its branch was deleted after it merged or closed, and can be restored at `headCommit`. */
266 headBranchDeleted?: boolean;
267 /**
268 * Set on a pull request closed because another one for the same work item
269 * was merged: that one's number.
270 */
271 supersededBy: number | null;
272 /**
273 * Where the latest run of the work item's acceptance checks stands, if
274 * there has been one against the current head.
275 */
276 checkStatus: CheckStatus | null;
277 /** The files it changes, as of its latest push. */
278 files: ChangedFile[];
279 /** Usernames of the people it is assigned to. */
280 assignees: string[];
281 /**
282 * Those whose review was asked for: usernames, and `g1t` when a g1t
283 * agent was asked.
284 */
285 reviewers: string[];
286 /**
287 * Teams whose review was asked for, as `workspace/slug`. A team stays here
288 * after review assignment picks people from it, who are in `reviewers`.
289 */
290 teamReviewers?: string[];
291 /** The labels it carries, by name. */
292 labels?: string[];
293 /** The milestone it is in, if any. */
294 milestone?: MilestoneRef | null;
295 /**
296 * The branch it merges into. Lists and `getPull` name it; null only in
297 * what services pass between themselves, for the default branch.
298 */
299 base?: string | null;
300 /** Who opened it: a person, or g1t (`kind` `agent`, username `g1t`) for a change g1t made. */
301 author: User;
302 /**
303 * For a change g1t made: the person who asked for it, by assigning a work item
304 * or handing g1t the work. They answer for it as its author would. See
305 * `workOwner`.
306 */
307 requestedBy: User | null;
308 /** RFC 3339. */
309 createdAt: string;
310 /** RFC 3339. */
311 updatedAt: string;
312 /** Who last moved its head (a user id), and when; absent until a push after rulesets arrived. */
313 headPushedBy?: string;
314 headPushedAt?: string;
315};
316
317/** One file a pull request changes, and by how much. */
318export type ChangedFile = { path: string; additions: number; deletions: number };
319
320/**
321 * Another pull request in progress that changes some of the same files. Two
322 * for the same work item are alternatives; two for different ones are
323 * heading for a conflict.
324 */
325export type Overlap = {
326 number: number;
327 title: string;
328 /** The number of the work item the other pull request is for. */
329 item: number | null;
330 /** The files both change. */
331 paths: string[];
332};
333
334/**
335 * On a pull request, `failed` means the merge queue took it out, until its
336 * head moves. The other states are from runs of commands written on work items,
337 * which g1t no longer runs.
338 */
339export type CheckStatus = "queued" | "running" | "passed" | "failed" | "errored";
340
341/** How one command went, in a run recorded before checks were workflows. */
342export type CheckResult = {
343 command: string;
344 passed: boolean;
345 /** Null when the command was stopped for taking too long. */
346 exitCode: number | null;
347 /** What the command printed; the end of it, when there was a lot. */
348 output: string;
349 durationMs: number;
350};
351
352/**
353 * A record against a pull request's head: the merge queue taking it out,
354 * with why, or an earlier run of commands written on its work item.
355 */
356export type CheckRun = {
357 id: string;
358 /** The commit that was checked. */
359 headCommit: string;
360 status: CheckStatus;
361 results: CheckResult[];
362 /** Why the checks could not be run, when `status` is `errored`. */
363 error: string | null;
364 /** RFC 3339. */
365 createdAt: string;
366 /** RFC 3339. */
367 finishedAt: string | null;
368};
369
370/** A reviewer's decision on a pull request. */
371export type Verdict = "approve" | "request_changes";
372
373/**
374 * A comment on a work item or a pull request. On a pull request it can sit on
375 * one line of the change, and it can carry a reviewer's verdict.
376 */
377export type Comment = {
378 id: string;
379 /**
380 * Something a person or an agent wrote, or something that happened: an
381 * assignment, a review asked for, a close.
382 */
383 kind: "comment" | "event";
384 author: User;
385 /**
386 * Markdown. For an event, what its author did, as the rest of a sentence
387 * that starts with their name: "assigned ana".
388 */
389 body: string;
390 /** The file commented on, for a comment on a line. */
391 path: string | null;
392 /** The line of that file, as numbered after the change. */
393 line: number | null;
394 verdict: Verdict | null;
395 /** RFC 3339. */
396 createdAt: string;
397 /** When its text was last edited, RFC 3339; absent if it never was. */
398 editedAt?: string;
399 /**
400 * Set when one of the workspace's agents wrote it, as itself. `author`
401 * is then the agent (its id, its handle as `username`, kind `agent`):
402 * show it by `agent.displayName` with an Agent badge, never as a person.
403 */
404 agent?: AgentRef;
405 /**
406 * Who the agent acted for, whose access capped it. Set with `agent`, and
407 * when g1t (`author`) wrote it, or did what an event notes, in a session
408 * someone started: show "on behalf of @them".
409 */
410 actingFor?: User;
411 /**
412 * An agent's review. Its `verdict` (none for a review that only
413 * comments) is shown but advisory: it never counts toward required
414 * approvals or code owners, and never blocks a merge.
415 */
416 advisory?: boolean;
417 /** How people (and g1t) reacted to it. Absent when nobody has. */
418 reactions?: ReactionGroup[];
419};
420
421/** An emoji reaction: the same eight as everywhere else people react to code. */
422export type ReactionContent = "+1" | "-1" | "laugh" | "hooray" | "confused" | "heart" | "rocket" | "eyes";
423
424/** The reactions, in the order they are offered, with the emoji each shows. */
425export const REACTIONS: { content: ReactionContent; emoji: string; label: string }[] = [
426 { content: "+1", emoji: "👍", label: "Thumbs up" },
427 { content: "-1", emoji: "👎", label: "Thumbs down" },
428 { content: "laugh", emoji: "😄", label: "Laugh" },
429 { content: "hooray", emoji: "🎉", label: "Hooray" },
430 { content: "confused", emoji: "😕", label: "Confused" },
431 { content: "heart", emoji: "❤️", label: "Heart" },
432 { content: "rocket", emoji: "🚀", label: "Rocket" },
433 { content: "eyes", emoji: "👀", label: "Eyes" },
434];
435
436/** Everyone who reacted one way to the same thing, first to react first; `g1t` for g1t. */
437export type ReactionGroup = {
438 content: ReactionContent;
439 count: number;
440 users: string[];
441};
442
443/**
444 * One of a workspace's own agents, as a comment or review it wrote before
445 * g1t's agents were removed shows it: Margo (@margo), with the face drawn
446 * from `avatarSeed`. Kept so those comments still render.
447 */
448export type AgentRef = {
449 id: string;
450 handle: string;
451 displayName: string;
452 avatarSeed: string;
453};
454
455export type NewComment = {
456 /** May be empty when approving. */
457 body: string;
458 path?: string;
459 line?: number;
460 verdict?: Verdict;
461};
462
463export type CheckReport = { results?: CheckResult[]; error?: string; skip?: boolean };
464
465export type SessionEntryKind = "prompt" | "message" | "tool_call" | "tool_result" | "note" | "skill";
466
467/** One step of an agent's session: the "why" behind a pull request's commits. */
468export type SessionEntry = {
469 seq: number;
470 kind: SessionEntryKind;
471 text: string;
472 /** For tool calls and results. */
473 tool: string | null;
474 /** The fork's head commit when this entry was recorded, if known. */
475 commit: string | null;
476 /** RFC 3339. */
477 at: string;
478 /**
479 * Its text is not shown to this viewer (`text` is empty): a prompt shows
480 * only to whoever typed it, and a tool's output to whoever ran it.
481 */
482 withheld?: boolean;
483};
484
485export type NewSessionEntry = Pick<SessionEntry, "kind" | "text"> & Partial<Pick<SessionEntry, "tool" | "commit">>;
486
487export type PullDetail = {
488 pull: Pull;
489 /** The work item it is for, if any. */
490 item: WorkItem | null;
491 comments: Comment[];
492 /** How people (and g1t) reacted to its description. */
493 reactions: ReactionGroup[];
494 /** The latest record against its head: the merge queue taking it out. */
495 checks: CheckRun | null;
496 /** Other pull requests in progress that change the same files. */
497 overlaps: Overlap[];
498 /**
499 * Whether the branch it would merge into has moved on without it, so that
500 * it has to catch up before it can merge.
501 */
502 behind: boolean;
503 /**
504 * Where it stands on its way to being merged, for a pull request g1t is
505 * seeing through. Null on anyone else's.
506 */
507 lifecycle: Lifecycle | null;
508 /**
509 * A merge was asked for while it was behind: g1t is bringing it up to
510 * date and will then land it.
511 */
512 landing: boolean;
513 /** Why g1t stopped working on it, if it did. */
514 stalled: string | null;
515 /** What workflow runs said about its head commit, one per workflow. */
516 statuses?: CommitStatus[];
517 /**
518 * Whether it merges cleanly into the branch it targets, worked out ahead
519 * of time whenever either side moves.
520 */
521 mergeable?: Mergeable;
522 /** When `mergeable` is `conflicting`: the files that conflict. */
523 conflicts?: string[];
524 /** Earlier records like `checks`, newest first, without their output. */
525 earlierChecks?: CheckRun[];
526 /**
527 * The checks the default branch's protection requires, each as it stands
528 * on the head commit. Empty when none are required.
529 */
530 requiredChecks?: RequiredCheck[];
531 /**
532 * Who owns the files it changes (the CODEOWNERS file of the branch it
533 * merges into) and whose approval is still needed. Absent without one.
534 */
535 codeOwners?: PullCodeOwners | null;
536 /**
537 * The rules of the branch it merges into it does not meet yet, for whoever
538 * is looking: what refuses the merge, what they may bypass, and what rulesets
539 * in evaluate would refuse. Absent once it is closed or merged.
540 */
541 rules?: MergeRules | null;
542};
543
544/** Where a required check stands on a commit; `expected` when nothing has reported it yet. */
545export type RequiredState = "success" | "failure" | "pending" | "expected";
546
547/** One check a branch's protection requires, as it stands on a commit. */
548export type RequiredCheck = {
549 /** A workflow's name, such as `CI`, or another status's context. */
550 name: string;
551 state: RequiredState;
552 description: string | null;
553 /** Where to see more: the workflow run, for one a workflow reported. */
554 targetUrl: string | null;
555};
556
557/** A check name reported on a repository's commits lately, for choosing required checks. */
558export type SeenCheck = {
559 name: string;
560 /** The events it was reported for, such as `pull_request`; empty for a status that names none. */
561 events: string[];
562 /** RFC 3339. */
563 lastSeen: string;
564};
565
566/**
567 * A status context's check name: `CI / pull_request` is the `CI` check,
568 * reported for a `pull_request` event. A context that does not end in an
569 * event, such as `g1t / deploy`, is its own name. As the work service reads it.
570 */
571export function checkName(context: string): string {
572 const at = context.lastIndexOf(" / ");
573 if (at <= 0) return context;
574 return STATUS_EVENTS.has(context.slice(at + 3)) ? context.slice(0, at) : context;
575}
576
577const STATUS_EVENTS = new Set([
578 "push",
579 "pull_request",
580 "pull_request_target",
581 "pull_request_review",
582 "merge_group",
583 "workflow_dispatch",
584 "workflow_run",
585 "workflow_call",
586 "schedule",
587 "release",
588 "issues",
589 "issue_comment",
590 "work_item",
591 "work_item_comment",
592 "repository_dispatch",
593]);
594
595/**
596 * Whether a pull request merges cleanly into the branch it targets:
597 * `unknown` when it was never worked out or could not be, `checking` while
598 * a probe merges the two.
599 */
600export type Mergeable = "clean" | "conflicting" | "unknown" | "checking";
601
602/** What a sandbox needs to find out whether a pull request merges cleanly. */
603export type MergecheckJob = {
604 pullId: string;
605 /** Lets the sandbox, and nothing else, report this probe. */
606 token: string;
607 repo: RepoPath;
608 number: number;
609 defaultBranch: string;
610 /** The default branch's commit to merge into. */
611 base: string;
612 /** The repository holding the change: its fork, or the repository. */
613 source: RepoPath;
614 /** The branch of `source` holding it. */
615 branch: string;
616 /** The change's commit. */
617 head: string;
618 /** Who the pull request is for (`workOwner`: whoever asked g1t for it, or its author), and so can read its source. */
619 author: User;
620};
621
622/** What a workflow run (or another tool) says about a commit. */
623export type CommitStatus = {
624 /** What reported it, such as `CI / push`. */
625 context: string;
626 state: "pending" | "success" | "failure" | "error";
627 description: string | null;
628 targetUrl: string | null;
629 updatedAt: string;
630 /** The integration that reported it: `actions`, `deployments`, `security`, `g1t` or `api`. */
631 source?: string;
632 /** Set on the status a check run stands as: the check run's id. */
633 checkRunId?: string;
634};
635
636/**
637 * A step on the way from an assigned work item to a pull request that is ready
638 * to merge. g1t takes each one without being asked: `working` (the agent is
639 * making the change), `checking`, `reviewing`, `revising` (the agent is
640 * addressing failed checks or a review), `catching_up` (merging in the
641 * branch it would land on), `answering` (woken to answer another agent),
642 * then `ready` for a person to merge. `needs_you`
643 * means g1t has stopped and a person decides what happens next.
644 */
645export type Stage = "working" | "checking" | "reviewing" | "revising" | "catching_up" | "answering" | "queued" | "ready" | "needs_you";
646
647export type Lifecycle = {
648 stage: Stage;
649 /** One sentence saying what is happening, or why it stopped. */
650 detail: string;
651 /** How many times the agent has been sent back to revise it. */
652 revisions: number;
653};
654
655/**
656 * How a repository wants its pull requests handled. A repository that has
657 * changed nothing has the defaults.
658 */
659export type RepoSettings = {
660 /**
661 * Land a g1t agent's pull request without a person once it is ready:
662 * checks passed and approved as the settings below require.
663 */
664 autoMerge: boolean;
665 /**
666 * The checks that must pass on a pull request's head before it may merge
667 * into the default branch, by name: a workflow's name (`CI`) or another
668 * status's context (`g1t / deploy`). The same for people and agents, and
669 * for the merge queue.
670 */
671 requiredChecks: string[];
672 /**
673 * Refuse to merge a pull request that does not contain the default
674 * branch's latest commits, so that what merges is what was checked. When
675 * off, merging one that is behind brings it up to date first.
676 */
677 requireUpToDate: boolean;
678 /**
679 * How many approving reviews a pull request needs before it may merge. A
680 * reviewer who has since asked for changes blocks it.
681 */
682 requiredApprovals: number;
683 /** Whether a g1t agent's approval counts towards `requiredApprovals`. */
684 countAgentApprovals: boolean;
685 /** Whether someone who may merge can bypass required checks that have not passed. */
686 allowIgnoringChecks: boolean;
687 /**
688 * Merge through a queue: pull requests are tested together with those
689 * ahead of them, and only a combination that passed reaches the default
690 * branch.
691 */
692 mergeQueue: boolean;
693 /**
694 * Refuse to merge until the code owners of every file it changes have
695 * approved, as many as each section asks. People only; `g1t` only where
696 * the file names `@g1t`.
697 */
698 requireCodeOwnerReview?: boolean;
699 /**
700 * Whether work items can be filed from it (as their origin): null for the
701 * default, which is on for a repository and off for a fork of one. Those
702 * filed from it before stay readable, and it can always be a target.
703 */
704 hasWorkItems?: boolean | null;
705 /** Whether work items can be filed from it now: `hasWorkItems`, or its default. */
706 workItemsEnabled?: boolean;
707 /** Which merge methods pull requests may use (at least one); rules can narrow them per branch. */
708 allowMergeCommit: boolean;
709 allowSquashMerge: boolean;
710 allowRebaseMerge: boolean;
711 allowFastForward: boolean;
712 /** The method offered first, and used when none is named. */
713 defaultMergeMethod: MergeMethod;
714 /** What merge and squash commits say unless their merger writes it. */
715 mergeCommitMessage: CommitMessageDefault;
716 squashCommitMessage: CommitMessageDefault;
717 /**
718 * For `custom`: templates whose `{title}`, `{number}`, `{body}`, `{branch}`,
719 * `{head_repo}`, `{base}`, `{author}`, `{commits}`, `{items}` and
720 * `{co_authors}` are filled in.
721 */
722 mergeCommitTemplate: string | null;
723 squashCommitTemplate: string | null;
724 /** Offer to bring a pull request up to date whenever its base has new commits. */
725 suggestUpdatingBranches: boolean;
726 /** Let people turn on auto-merge for a pull request. */
727 allowAutoMerge: boolean;
728 /** Delete a pull request's branch once it merges (restorable from it). */
729 deleteBranchOnMerge: boolean;
730 /** Commits made on g1t.sh carry a `Signed-off-by:` trailer for whoever made them. */
731 requireWebCommitSignOff: boolean;
732 /** Let anyone who can read it comment on its commits. */
733 allowCommitComments: boolean;
734 /**
735 * Land the work items a pull request names in this repository when it
736 * merges into the default branch (and those a commit pushed there names),
737 * closing those with nothing else left to land.
738 */
739 autoCloseItems: boolean;
740 /** Username of the member who last changed the settings, if anyone has. */
741 updatedBy: string | null;
742 /** RFC 3339. */
743 updatedAt: string | null;
744};
745
746/** What a merge or squash commit's message says by default (`pull_title_and_commits` is squash only). */
747export type CommitMessageDefault = "default" | "pull_title" | "pull_title_and_description" | "pull_title_and_commits" | "custom";
748
749/** A person's request that a pull request merge on its own once it may. */
750export type AutoMerge = {
751 method: MergeMethod;
752 commitTitle: string | null;
753 commitMessage: string | null;
754 /** Username of who asked. */
755 enabledBy: string;
756 /** RFC 3339. */
757 enabledAt: string;
758};
759
760/** A comment on a commit, or on one line (`path`, `line`) of a file it changed. */
761export type CommitComment = {
762 id: string;
763 repoId: string;
764 /** The commit's full hash. */
765 commit: string;
766 path: string | null;
767 line: number | null;
768 /** Username. */
769 author: string;
770 /** Markdown. */
771 body: string;
772 /** RFC 3339. */
773 createdAt: string;
774 updatedAt: string | null;
775};
776
777/** How a pull request may merge now, for whoever is looking (`mergeChoices`). */
778export type MergeChoices = {
779 /** The methods it may merge by: the repository's, as the rules for its base allow. In the order offered. */
780 methods: MergeMethod[];
781 /** The one offered first: the repository's default when it may be used, else the first of `methods`. */
782 defaultMethod: MergeMethod | null;
783 /** Each of `methods`, with its default commit message and whether it can be used now. */
784 options: MergeChoice[];
785 /** Its base has commits it does not have. */
786 behind: boolean;
787 /** It contains its base's head, so it can fast-forward as it is. */
788 canFastForward: boolean;
789 /** Offer to bring it up to date: it is behind, and the repository suggests (or requires) it. */
790 suggestUpdate: boolean;
791 /** The repository requires it to be up to date before it merges. */
792 requireUpToDate: boolean;
793 /** It merges through the merge queue, which lands it as tested whatever method is chosen. */
794 mergeQueue: boolean;
795 /** The viewer may turn on auto-merge for it. */
796 autoMergeAllowed: boolean;
797 /** Whether "delete the branch" starts ticked. */
798 deleteBranchDefault: boolean;
799 /** Its branch can be deleted once it merges (never a working copy's). */
800 canDeleteBranch: boolean;
801 /** Commits made merging it carry a `Signed-off-by:` trailer for the merger. */
802 signOff: boolean;
803};
804
805/** One way a pull request may merge. */
806export type MergeChoice = {
807 method: MergeMethod;
808 /** For `merge` and `squash`: the commit's default title and the rest of its message. */
809 commitTitle: string | null;
810 commitMessage: string | null;
811 /** Why it cannot merge this way now, if it cannot. */
812 unavailable: string | null;
813};
814
815/** How a pull request merges, as the merger chose. */
816export type MergeOptions = {
817 /** Leave its work item open: this pull request is only part of the work. */
818 keepItemOpen?: boolean;
819 /** Merge although required checks have not passed, where the rule requiring them allows it. */
820 ignoreChecks?: boolean;
821 /** Merge past rules a ruleset lets the actor bypass. Recorded as a bypass. */
822 bypassRules?: boolean;
823 /** The repository's default when absent. */
824 mergeMethod?: MergeMethod;
825 /** For `merge` and `squash`: the commit's title and the rest of its message. */
826 commitTitle?: string;
827 commitMessage?: string;
828 /** Refuse if its head is no longer this commit. */
829 expectedHead?: string;
830 /** Delete its branch once merged; the repository's setting when absent. */
831 deleteBranch?: boolean;
832};
833
834/** The settings a member can change. */
835export type RepoSettingsInput = Omit<RepoSettings, "updatedBy" | "updatedAt" | "workItemsEnabled">;
836
837/**
838 * What the runner is to do next for a pull request: always nothing now,
839 * since no step of a pull request is carried out in a sandbox any more.
840 */
841export type Advance = { action: "none" };
842
843export type OpenWorkItemInput = {
844 /** The workspace; may be left out with `repo`, meaning the repository's. */
845 workspace?: string;
846 /** The repository it is filed from, its origin. Without one, only a member of the workspace may file it. */
847 repo?: RepoPath;
848 /** Repositories of the workspace where the work is meant to land. */
849 targets?: RepoPath[];
850 title: string;
851 body: string;
852 labels?: string[];
853 /** The number of the workspace milestone to put it in. Needs the Triage role. */
854 milestone?: number;
855};
856
857export type UpdateWorkItemInput = {
858 title?: string;
859 body?: string;
860 labels?: string[];
861 /** Usernames of the people it is assigned to; replaces the whole set. */
862 assignees?: string[];
863 /** The number of the workspace milestone to put it in; 0 takes it out. */
864 milestone?: number;
865 /** The work items of the workspace that have to land first, by number; replaces the whole set. */
866 blocked_by?: number[];
867};
868
869/** How many `listWorkItems` and `listPulls` give at most in one call. */
870export const LIST_PAGE = 100;
871
872/** The filters on `listWorkItems`. */
873export type WorkItemFilter = {
874 /** Only those linked to this repository (in any role, or in `role`). */
875 repo?: RepoPath;
876 role?: LinkRole;
877 state?: State;
878 label?: string;
879 milestone?: number;
880 /** Only those assigned to this username. */
881 assignee?: string;
882 /** Only those closed at or after this time (RFC 3339), the most recently closed first instead of the newest. */
883 closedSince?: string;
884 /** Only those numbered below this: the next page, after one whose lowest number this is. */
885 before?: number;
886};
887
888export type OpenPullInput = {
889 /** The work item this is for, by number in the repository's workspace. */
890 item?: number;
891 /** Defaults to the work item's title; required without one. */
892 title?: string;
893 /** What changed and why. Usually set later, when a draft is marked ready. */
894 body?: string;
895 /**
896 * A branch of the repository that already holds the change. The pull
897 * request is then ready for review at once and has no fork.
898 */
899 branch?: string;
900 /**
901 * A fork of the repository (directly or through other forks) whose
902 * `branch` holds the change. Needs `branch`.
903 */
904 headRepo?: RepoPath;
905 agent: string;
906 runtime: Runtime;
907 /** The branch to merge into: the default branch when left out. */
908 base?: string;
909 /**
910 * Opened from a branch as a draft, still being worked on: it can't merge
911 * until it is marked ready.
912 */
913 draft?: boolean;
914};
915
916/** One repository's pull requests from `pullsForRepos`, newest first. */
917export type RepoPulls = {
918 repoId: string;
919 /** Draft and open. */
920 open: Pull[];
921 /** Merged and closed. */
922 closed: Pull[];
923};
924
925/** Where a comment is kept, for changing it: a repository's pull requests, or a workspace's work items. */
926export type CommentScope = { repo: RepoPath } | { workspace: string };
927
928/**
929 * Where an image put in Markdown is kept (`upload_attachment`): a
930 * repository, for its pull requests, commits and the work items filed
931 * from it, or a work item, which keeps its own (one filed with no
932 * repository takes images too, once it is open).
933 */
934export type AttachmentScope = { repo: RepoPath } | { workspace: string; number: number };
935
936/** An image kept for Markdown: `path` is under the usercontent origin (`/attachments/<id>`). */
937export type Attachment = { id: string; name: string; content_type: string; size: number; path: string };
938
939/** Work items, pull requests, comments and sessions. */
940export interface WorkApi extends RulesApi, ChecksApi, CoordinationApi {
941 /**
942 * Files a work item: from `repo` (its origin) by anyone who may take part
943 * there, or in the workspace alone by a member. `targets` are where it is
944 * meant to land.
945 */
946 openWorkItem(actor: User, input: OpenWorkItemInput): Promise<Result<WorkItem>>;
947 /** A workspace's work items the viewer may see, newest first, at most [`LIST_PAGE`] (`before` pages on). */
948 listWorkItems(workspace: string, viewer: Viewer, filter?: WorkItemFilter): Promise<Result<WorkItem[]>>;
949 getWorkItem(workspace: string, number: number, viewer: Viewer): Promise<Result<WorkItemDetail>>;
950 /** Its owner, or someone with the Triage role on it, may. */
951 updateWorkItem(actor: User, workspace: string, number: number, input: UpdateWorkItemInput): Promise<Result<WorkItem>>;
952 /**
953 * Closes it; with `by`, as the commit there (on its repository's default
954 * branch, naming it after a closing keyword) would have: it lands there,
955 * and closes once nothing else is left to land.
956 */
957 closeWorkItem(actor: User, workspace: string, number: number, reason?: CloseReason, by?: { repo: RepoPath; commit: string }): Promise<Result<WorkItem>>;
958 reopenWorkItem(actor: User, workspace: string, number: number): Promise<Result<WorkItem>>;
959 /** Links a repository of the workspace as a target (or as touched), or changes how it is linked. Needs Triage. */
960 linkWorkItem(actor: User, workspace: string, number: number, repo: RepoPath, role?: LinkRole): Promise<Result<WorkItem>>;
961 /** Removes a repository's link (never its origin's). Needs Triage. */
962 unlinkWorkItem(actor: User, workspace: string, number: number, repo: RepoPath): Promise<Result<WorkItem>>;
963 /** What `#number` names for the viewer, read in a repository (and its workspace) or a workspace alone. */
964 resolveRef(viewer: Viewer, context: { repo: RepoPath } | { workspace: string }, number: number): Promise<RefTarget>;
965 /** The work items that a commit pushed to the repository's default branch resolved, of those the viewer may read. */
966 closedByCommit(repo: RepoPath, commit: string, viewer: Viewer): Promise<Result<ClosedItem[]>>;
967 /** A workspace's labels, by name, each with how many work items and pull requests carry it. */
968 listLabels(workspace: string, viewer: Viewer): Promise<Result<Label[]>>;
969 /**
970 * Creates a label, or with `name` changes one; renaming it renames it on
971 * everything that carries it. Needs the Write role in the workspace.
972 */
973 saveLabel(actor: User, workspace: string, label: { name?: string; newName?: string; color?: string; description?: string }): Promise<Result<Label>>;
974 /** Removes a label from the workspace and everything carrying it. */
975 deleteLabel(actor: User, workspace: string, name: string): Promise<Result<boolean>>;
976 /** Adds the default labels the workspace does not have yet; returns them all. */
977 addDefaultLabels(actor: User, workspace: string): Promise<Result<Label[]>>;
978 /**
979 * The labels of a work item or a pull request, replaced, added to or
980 * taken from. Labels the workspace lacks are created for someone who may
981 * manage labels. Returns its labels now.
982 */
983 setLabels(actor: User, thread: Thread, labels: string[], change?: LabelChange): Promise<Result<string[]>>;
984 /** A workspace's, open ones by due date, then closed ones; both unless `state` says. */
985 listMilestones(workspace: string, viewer: Viewer, state?: State): Promise<Result<Milestone[]>>;
986 getMilestone(workspace: string, number: number, viewer: Viewer): Promise<Result<MilestoneDetail>>;
987 /** Creates a milestone, or with `number` changes the fields given. Needs the Write role in the workspace. */
988 saveMilestone(
989 actor: User,
990 workspace: string,
991 milestone: { number?: number; title?: string; description?: string; dueOn?: string; state?: State },
992 ): Promise<Result<Milestone>>;
993 deleteMilestone(actor: User, workspace: string, number: number): Promise<Result<boolean>>;
994 /** How many work items linked to the repository, and how many of its pull requests, are open. */
995 counts(repo: RepoPath, viewer: Viewer): Promise<Result<{ items: number; pulls: number }>>;
996 /** Moves on the job that moves issues into work items; does nothing once done. */
997 migrateWorkItems(): Promise<{ done: boolean; repos: number; numbered: number; unplaced: number }>;
998
999 /**
1000 * On a work item or a pull request. On a pull request it may name a line
1001 * of the change and carry a verdict; nobody can give a verdict on their own.
1002 */
1003 addComment(actor: User, thread: Thread, comment: NewComment): Promise<Result<Comment>>;
1004 /**
1005 * Changes a comment's text. Its author may, and so may anyone with the
1006 * Maintain role or higher; notes of what happened cannot be changed.
1007 */
1008 editComment(actor: User, scope: CommentScope, commentId: string, body: string): Promise<Result<Comment>>;
1009 /**
1010 * Deletes a comment: its author, or anyone with the Maintain role or
1011 * higher. A review that gave a verdict cannot be deleted, only edited.
1012 */
1013 deleteComment(actor: User, scope: CommentScope, commentId: string): Promise<Result<boolean>>;
1014 /**
1015 * Keeps an image to show in Markdown there, for anyone who may comment
1016 * on the repository or take part in the work item. `data` is the file in
1017 * base64; its type is read from its bytes. PNG, JPEG, GIF or WebP of at
1018 * most 10 MB: larger is `too_large`, another type `unsupported_type`.
1019 */
1020 uploadAttachment(actor: User, scope: AttachmentScope, file: { name: string; contentType?: string; data: string }): Promise<Result<Attachment>>;
1021 /**
1022 * Reacts to comment `commentId` on a work item or pull request, or to its
1023 * description without one; with `remove`, takes it back. Anyone who can
1024 * read it may. What it has now.
1025 */
1026 react(actor: User, thread: Thread, reaction: { commentId?: string; content: ReactionContent; remove?: boolean }): Promise<Result<ReactionGroup[]>>;
1027
1028 /**
1029 * What a sandbox says about a run from before checks were workflows: so
1030 * one still finishing is recorded.
1031 */
1032 reportChecks(runId: string, token: string, report: CheckReport): Promise<Result<CheckRun>>;
1033 /**
1034 * Claims the probe of whether a pull request merges cleanly that a
1035 * `pull.mergecheck` event asked for. Refused when it is no longer wanted
1036 * or the repository has as many running as it may. For the runner service.
1037 */
1038 startMergecheck(pullId: string): Promise<Result<MergecheckJob>>;
1039 /** Records that a probe could not be carried out. For the runner service. */
1040 failMergecheck(pullId: string, token: string, error: string): Promise<Result<Mergeable>>;
1041
1042 /**
1043 * Works out the next step for a pull request g1t is seeing through and,
1044 * if there is one to take now, claims it, so that it is taken once
1045 * however often this is called. For the runner service.
1046 */
1047 advance(pullId: string): Promise<Advance>;
1048 /**
1049 * Ids of the open pull requests to move along: those g1t merges by itself
1050 * or someone set to auto-merge; with `repoId`, also every open one whose
1051 * lifecycle is remembered, to bring it up to date after a merge there.
1052 */
1053 managedPulls(repoId?: string): Promise<string[]>;
1054
1055 /** A repository's merge queue: what is in it, in order, and what recently left. */
1056 queue(repo: RepoPath, viewer: Viewer): Promise<Result<QueueView>>;
1057 /**
1058 * The next batch of combined states to test for a repository, one per
1059 * entry; empty while a batch is being tested or nothing waits.
1060 */
1061 queueBuild(repoId: string): Promise<QueueJob[]>;
1062 /** Reports that a combined state could not be built or checked. */
1063 failQueue(entryId: string, token: string, error: string): Promise<Result<QueueState>>;
1064 /** Takes a pull request out of the merge queue. Members only. */
1065 removeFromQueue(actor: User, repo: RepoPath, number: number): Promise<Result<Pull>>;
1066
1067 getSettings(repo: RepoPath, viewer: Viewer): Promise<Result<RepoSettings>>;
1068 /** The check names reported on the repository's commits in the last 30 days, most recent first. */
1069 seenChecks(repo: RepoPath, viewer: Viewer): Promise<Result<SeenCheck[]>>;
1070 /** Members of the repository's workspace only. */
1071 updateSettings(actor: User, repo: RepoPath, settings: RepoSettingsInput): Promise<Result<RepoSettings>>;
1072 /** The CODEOWNERS file at a branch (the default when left out), checked. Needs Read. */
1073 codeownersErrors(repo: RepoPath, viewer: Viewer, ref?: string | null): Promise<Result<CodeownersReport>>;
1074
1075 /**
1076 * Opens a pull request: a draft with a fork to push to, or, given a
1077 * branch, one ready for review.
1078 */
1079 openPull(actor: User, repo: RepoPath, input: OpenPullInput): Promise<Result<Pull>>;
1080 /** Newest first, at most [`LIST_PAGE`] (`before` pages on). */
1081 listPulls(
1082 repo: RepoPath,
1083 viewer: Viewer,
1084 state?: State,
1085 filter?: {
1086 label?: string;
1087 milestone?: number;
1088 base?: string;
1089 /** Only those numbered below this: the next page, after one whose lowest number this is. */
1090 before?: number;
1091 /** Only those merged at or after this time (RFC 3339). */
1092 mergedSince?: string;
1093 },
1094 ): Promise<Result<Pull[]>>;
1095 /**
1096 * Delivery for up to 100 repositories over UTC days `from` through
1097 * `until`, over their whole history. Repositories the viewer cannot read,
1098 * and forks, are left out of `projects`.
1099 */
1100 deliveryInsights(repoIds: string[], viewer: Viewer, from: string, until: string): Promise<DeliveryInsights>;
1101 /**
1102 * What up to 100 of a workspace's repositories' work says over the last
1103 * weeks, for Home's daily signal and the Workspace overview: each row is
1104 * one repository's, so batches add up. Repositories the viewer cannot
1105 * read are left out.
1106 */
1107 workspaceSignals(args: WorkspaceSignalsArgs, viewer: Viewer): Promise<WorkspaceSignals>;
1108 /**
1109 * The newest `limit` open and closed pull requests of each repository,
1110 * in one call. Repositories the viewer cannot read, and forks, are left
1111 * out: ask those with `listPulls`.
1112 */
1113 pullsForRepos(repoIds: string[], viewer: Viewer, limit: number): Promise<RepoPulls[]>;
1114 getPull(repo: RepoPath, number: number, viewer: Viewer): Promise<Result<PullDetail>>;
1115 /**
1116 * Changes who a pull request is assigned to and whose review is asked
1117 * for; each list given replaces the whole set. Asking for `g1t`'s
1118 * review does not by itself start one: the runner's `review` does.
1119 */
1120 updatePull(
1121 actor: User,
1122 repo: RepoPath,
1123 number: number,
1124 changes: {
1125 assignees?: string[];
1126 reviewers?: string[];
1127 labels?: string[];
1128 /** 0 takes it out of its milestone. */
1129 milestone?: number;
1130 /** The branch it merges into. Needs the Write role. */
1131 base?: string;
1132 },
1133 ): Promise<Result<Pull>>;
1134 /**
1135 * Brings a pull request up to date with the default branch in seconds,
1136 * without a sandbox, when the two changed different files: the merge
1137 * commit is pushed to its branch as `actor`, who must be whoever opened
1138 * it (for a fork) or a member (for a branch). Otherwise `needs_agent`, and
1139 * nothing is pushed: the runner's `update` is the way on.
1140 */
1141 catchUpPull(actor: User, repo: RepoPath, number: number, options?: { updateMethod?: UpdateMethod; expectedHead?: string }): Promise<Result<PullBranchUpdate>>;
1142 /**
1143 * Turns a pull request's auto-merge on (with how to merge) or off. On needs
1144 * Write, since it merges as whoever turned it on; its author may also turn
1145 * it off. Refused where the repository does not allow auto-merge or the
1146 * method. One that may merge already merges at once.
1147 */
1148 setAutoMerge(
1149 actor: User,
1150 repo: RepoPath,
1151 number: number,
1152 enabled: boolean,
1153 options?: { mergeMethod?: MergeMethod; commitTitle?: string; commitMessage?: string },
1154 ): Promise<Result<Pull>>;
1155 /** How a pull request may merge now: methods, default messages, auto-merge and branch deletion. */
1156 mergeChoices(viewer: Viewer, repo: RepoPath, number: number): Promise<Result<MergeChoices>>;
1157 /** Deletes a merged or closed pull request's branch (Push on where it is). */
1158 deleteHeadBranch(actor: User, repo: RepoPath, number: number): Promise<Result<Pull>>;
1159 /** Makes a deleted pull request branch again at its head. */
1160 restoreHeadBranch(actor: User, repo: RepoPath, number: number): Promise<Result<Pull>>;
1161 /** A commit's comments, oldest first; with no commit, the repository's, newest first, 30 a page. */
1162 commitComments(viewer: Viewer, repo: RepoPath, commit?: string | null, page?: number): Promise<Result<CommitComment[]>>;
1163 /** Comments on a commit, or on one line of a file it changed. Needs Read, where commit comments are on. */
1164 addCommitComment(actor: User, repo: RepoPath, commit: string, body: string, at?: { path: string; line: number }): Promise<Result<CommitComment>>;
1165 editCommitComment(actor: User, repo: RepoPath, id: string, body: string): Promise<Result<CommitComment>>;
1166 deleteCommitComment(actor: User, repo: RepoPath, id: string): Promise<Result<boolean>>;
1167 /** Marks a draft ready for review and sets its description. */
1168 readyPull(actor: User, repo: RepoPath, number: number, summary: string): Promise<Result<Pull>>;
1169 closePull(actor: User, repo: RepoPath, number: number): Promise<Result<Pull>>;
1170 /** Opens a closed pull request again, as the draft it was if it was closed as one. Never a merged one. */
1171 reopenPull(actor: User, repo: RepoPath, number: number): Promise<Result<Pull>>;
1172 /** Turns an open pull request back into a draft; it leaves the merge queue. */
1173 convertPullToDraft(actor: User, repo: RepoPath, number: number): Promise<Result<Pull>>;
1174 /**
1175 * Lands the pull request on the repository's default branch. Unless
1176 * `keepItemOpen`, that lands the work items it closes in this repository
1177 * (each closing once nothing else is left to land), and the others still
1178 * in progress here for its own close as superseded. Only members of the
1179 * repository's workspace may merge.
1180 *
1181 * If the default branch has moved, the pull request is brought up to date
1182 * first and lands when that is done; it comes back still open, and
1183 * `PullDetail.landing` is true meanwhile. A repository that requires pull
1184 * requests to be up to date refuses instead.
1185 */
1186 mergePull(actor: User, repo: RepoPath, number: number, options?: MergeOptions): Promise<Result<Pull>>;
1187 /**
1188 * Drafts and open pull requests the viewer started, most recently active
1189 * first, each with where it stands if g1t is seeing it through.
1190 */
1191 listActivePulls(viewer: Viewer): Promise<{ pull: Pull; item: WorkItem | null; lifecycle: Lifecycle | null }[]>;
1192 /**
1193 * The work items and pull requests a person opened, a page at a time,
1194 * only those the viewer may read. Not found for no such account.
1195 */
1196 byAuthor(username: string, viewer: Viewer, filter?: AuthoredFilter): Promise<Result<Authored>>;
1197 /**
1198 * What a person did each day of the last year (work items and pull requests
1199 * opened, reviews given), only on repositories the viewer may read: the
1200 * calendar on their profile. Not found for no such account.
1201 */
1202 contributions(username: string, viewer: Viewer): Promise<Result<Contributions>>;
1203
1204 appendSession(actor: User, repo: RepoPath, number: number, entries: NewSessionEntry[]): Promise<Result<{ count: number }>>;
1205 readSession(repo: RepoPath, number: number, viewer: Viewer, afterSeq?: number): Promise<Result<SessionEntry[]>>;
1206}
1207
1208/**
1209 * What to pass `ReposApi.compare` to see what a pull request changes.
1210 *
1211 * A working copy is compared as a whole. A branch, of the repository or of
1212 * a fork of it, is compared by name while the pull request is open, and by
1213 * the commit it was merged or closed at afterwards, so later pushes to the
1214 * branch do not change the record. A fork's branch is compared from the
1215 * repository it merges into (`targetId`).
1216 */
1217export function pullComparison(pull: Pull): {
1218 repoId: string;
1219 base: string | null;
1220 head: string | null;
1221 /** The branch it merges into, which it is compared from. */
1222 baseBranch: string | null;
1223 /** For a fork's branch: the repository it merges into. */
1224 targetId: string | null;
1225} {
1226 const baseBranch = pull.base ?? null;
1227 if (pull.forkRepoId) return { repoId: pull.forkRepoId, base: pull.mergeBase, head: null, baseBranch, targetId: null };
1228 const settled = pull.status === "merged" || pull.status === "closed";
1229 return {
1230 repoId: pull.headRepoId ?? pull.repoId,
1231 base: pull.mergeBase,
1232 // A fork's branch by the commit it was last seen at, which the repository
1233 // it merges into holds too, so those who read that repository can read it.
1234 head: ((settled || pull.headRepoId) && pull.headCommit) || pull.branch,
1235 baseBranch,
1236 targetId: pull.headRepoId ? pull.repoId : null,
1237 };
1238}
1239
1240/** The id of the repository holding a pull request's change: its working copy, its fork, or the repository. */
1241export function pullSourceId(pull: Pull): string {
1242 return pull.forkRepoId ?? pull.headRepoId ?? pull.repoId;
1243}
1244
1245export type QueueState = "waiting" | "testing" | "passed" | "failed" | "landed" | "removed";
1246
1247/** One pull request's place in a merge queue. */
1248export type QueueEntry = {
1249 id: string;
1250 number: number;
1251 title: string;
1252 agent: string;
1253 state: QueueState;
1254 /** The pull requests merged ahead of it in the state being tested, in order. */
1255 ahead: number[];
1256 baseCommit: string | null;
1257 combinedCommit: string | null;
1258 error: string | null;
1259 results: CheckResult[];
1260 /** Username of whoever merged it into the queue: a person, or `g1t`. */
1261 enqueuedBy: string;
1262 /** RFC 3339. */
1263 createdAt: string;
1264 /** RFC 3339. */
1265 finishedAt: string | null;
1266};
1267
1268/** A repository's merge queue: what is in it, in order, and what recently left. */
1269export type QueueView = {
1270 enabled: boolean;
1271 active: QueueEntry[];
1272 /** Newest first. */
1273 recent: QueueEntry[];
1274};
1275
1276export type QueueStackItem = {
1277 number: number;
1278 title: string;
1279 /** The repository holding the change, and its branch. */
1280 source: RepoPath;
1281 branch: string;
1282 commit: string;
1283 /** How it lands: the state is built with its commits made this way. */
1284 method?: MergeMethod;
1285 /** For merge and squash: the commit's message. */
1286 message?: string;
1287 /** For squash: who the commit is by. */
1288 author?: User;
1289};
1290
1291/** What a sandbox needs to build and check one combined state. */
1292export type QueueJob = {
1293 entryId: string;
1294 token: string;
1295 repo: RepoPath;
1296 defaultBranch: string;
1297 baseCommit: string;
1298 branch: string;
1299 stack: QueueStackItem[];
1300 /** Always empty: the state is checked by the merge_group workflows run on it. */
1301 checks: string[];
1302 /** Always empty, as `checks`. */
1303 contractChecks: string[];
1304 actor: User;
1305};
1306
1307// --- A person's work ---------------------------------------------------------
1308// Mirrors the `Authored*` types in `crates/contracts/src/work.rs`.
1309
1310export type AuthoredKind = "item" | "pull";
1311/** `closed` takes in merged pull requests too; `merged` is only those. */
1312export type AuthoredState = "open" | "closed" | "merged";
1313/** `created` is newest first, `updated` most recently changed, `oldest` oldest first. */
1314export type AuthoredSort = "created" | "updated" | "oldest";
1315
1316/** The most items one `byAuthor` page holds. */
1317export const AUTHORED_PAGE = 25;
1318
1319export type AuthoredFilter = {
1320 kind?: AuthoredKind;
1321 state?: AuthoredState;
1322 /** `namespace/name`. */
1323 repo?: string;
1324 sort?: AuthoredSort;
1325 /** The `next` of the page before. */
1326 before?: string;
1327 limit?: number;
1328};
1329
1330export type AuthoredItem = {
1331 kind: AuthoredKind;
1332 /** The workspace it is numbered in. */
1333 workspace: string;
1334 /** A pull request's repository, or the repository a work item was filed from, if it was. */
1335 repo: RepoPath | null;
1336 number: number;
1337 title: string;
1338 /** A merged pull request is closed. */
1339 state: State;
1340 /** A pull request's own status; null on a work item. */
1341 status: PullStatus | null;
1342 /** Why a work item was closed. */
1343 reason: CloseReason | null;
1344 draft: boolean;
1345 merged: boolean;
1346 /** RFC 3339. */
1347 createdAt: string;
1348 /** RFC 3339. */
1349 updatedAt: string;
1350 /** RFC 3339. */
1351 mergedAt: string | null;
1352};
1353
1354/** Over every repository the viewer may read, whatever the filters. */
1355export type AuthoredCounts = {
1356 pullsMerged: number;
1357 pullsOpen: number;
1358 pulls: number;
1359 items: number;
1360 itemsOpen: number;
1361};
1362
1363export type Authored = {
1364 items: AuthoredItem[];
1365 /** Pass as `before` for the next page; null on the last. */
1366 next: string | null;
1367 counts: AuthoredCounts;
1368 /** The repositories they worked in that the viewer may read, most work first. */
1369 repos: { repo: RepoPath; count: number }[];
1370};
1371
1372// Mirrors `Contributions` in `crates/contracts/src/work.rs`.
1373
1374/** How many days `contributions` covers: today and the 364 before it. */
1375export const CONTRIBUTION_DAYS = 365;
1376
1377/** One day with something on it; days with nothing are left out. */
1378export type ContributionDay = {
1379 /** `YYYY-MM-DD`, UTC. */
1380 date: string;
1381 /** Everything that day, commits included. */
1382 count: number;
1383 /** How many of `count` are commits they pushed to a default branch. */
1384 commits?: number;
1385};
1386
1387/** A person's year, as far as the viewer may see. */
1388export type Contributions = {
1389 /** Oldest first. */
1390 days: ContributionDay[];
1391 total: number;
1392 /** The first day counted, `YYYY-MM-DD`; the last is today (UTC). */
1393 from: string;
1394};
1395
1396// --- Coordinating work: sessions, claims and dependencies -----------------
1397// Mirrors `crates/contracts/src/work/coordination.rs`.
1398
1399/** The harness a session runs in. */
1400export type Harness = "claude_code" | "claude_managed_agents" | "codex" | "cursor" | "copilot" | "other";
1401
1402/** A session as a mark on something it made. */
1403export type SessionRef = { id: string; harness: Harness };
1404
1405/** The sessions tied to one pull request (`pullSessions`). */
1406export type PullSessions = { number: number; sessions: SessionRef[] };
1407
1408/** Active: heard from in 15 minutes; idle: not since; ended: ended, or quiet for 30 minutes. */
1409export type SessionState = "active" | "idle" | "ended";
1410
1411/** One run of someone's own harness on a repository. */
1412export type WorkSession = {
1413 /** `ses_…`. */
1414 id: string;
1415 repoId: string;
1416 /** The work item it works on, by number in the repository's workspace. */
1417 item: number | null;
1418 pull: number | null;
1419 branch: string | null;
1420 actor: { id: string; username: string };
1421 harness: Harness;
1422 harnessSessionId: string | null;
1423 title: string | null;
1424 state: SessionState;
1425 headCommit: string | null;
1426 startedAt: string;
1427 lastSeenAt: string;
1428 endedAt: string | null;
1429 /** What the harness said, or `timed_out`. */
1430 endReason: string | null;
1431 /** For a subagent: the session that started it. */
1432 parentId?: string | null;
1433 /** For a subagent: its kind (Explore, security-reviewer). */
1434 agentType?: string | null;
1435 /** For a subagent: the task it was given, in a few words. */
1436 task?: string | null;
1437 /** For a subagent: the prompt its parent gave it. */
1438 prompt?: string | null;
1439 /** What it reported when it finished: a subagent's final message. */
1440 result?: string | null;
1441 model?: string | null;
1442 /** How many subagents it started. */
1443 subagents?: number;
1444 /** In a list, for one with no title: the start of the first prompt it recorded. */
1445 firstPrompt?: string | null;
1446 /** Where its title came from; none for one with no title, or from before titles said. */
1447 titleSource?: TitleSource | null;
1448 /** When its title was last settled. */
1449 titleSettledAt?: string | null;
1450 /**
1451 * What it cost, estimated at Anthropic's list price from the token usage
1452 * its harness reported, in US dollars; none until it reports usage for a
1453 * model with a known price. Only the workspace's members see it.
1454 */
1455 costUsd?: number | null;
1456 /** Some of its usage was for models priced as unknown, which `costUsd` leaves out. */
1457 costPartial?: boolean;
1458};
1459
1460/**
1461 * Where a session's title came from. g1t settles a title from the whole of
1462 * the session's record: `prompt` is provisional (its first prompt's
1463 * intent) until it does; `person` is pinned from the site and never
1464 * replaced; `agent` is its agent's own (`g1t session attach --title`);
1465 * `item`, `pull` and `summary` are g1t's.
1466 */
1467export type TitleSource = "prompt" | "agent" | "person" | "item" | "pull" | "summary";
1468
1469// --- Sessions across a workspace ------------------------------------------
1470// Mirrors `crates/contracts/src/work/workspace_sessions.rs`.
1471
1472/**
1473 * What kind of session one in a workspace's list is: someone's own
1474 * `harness`, one g1t started for someone on the workspace's Anthropic
1475 * provider (`hosted`), g1t's own `agent` on a pull request, or what a coding
1476 * agent `recorded` on a pull request with no session of its own.
1477 */
1478export type SessionKind = "harness" | "hosted" | "agent" | "recorded";
1479
1480/** Which of a workspace's sessions to list: those going now, every one, or those that ended. */
1481export type SessionsShown = "live" | "recent" | "ended";
1482
1483/** One session of a workspace, whatever its kind. */
1484export type WorkspaceSession = {
1485 kind: SessionKind;
1486 /** `ses_…`, `run_…` for g1t's agent, the pull request's id for a recorded one. */
1487 id: string;
1488 /** `owner/name`. */
1489 repo: string;
1490 repoId: string;
1491 title: string | null;
1492 /** Who runs it; `g1t` for g1t's own. */
1493 actor: string | null;
1494 harness: Harness | null;
1495 /** For g1t's agent and a recorded one: the agent label. */
1496 agent: string | null;
1497 branch: string | null;
1498 item: number | null;
1499 itemTitle: string | null;
1500 pull: number | null;
1501 pullTitle: string | null;
1502 pullStatus: PullStatus | null;
1503 state: SessionState;
1504 startedAt: string;
1505 lastActivityAt: string;
1506 subagents: number;
1507 /** For g1t's agent: what it is doing now. */
1508 step: string | null;
1509 /** For a harness or hosted session: the session itself. */
1510 session: WorkSession | null;
1511 /** With `subagents`: its subagents and theirs. */
1512 children?: WorkSession[];
1513};
1514
1515/** A repository of the workspace the viewer may read. */
1516export type SessionsRepo = { id: string; path: string; defaultBranch: string };
1517
1518/** One repository's merge queue, when something waits in it. */
1519export type RepoQueue = QueueView & { repo: string; defaultBranch: string };
1520
1521/** A pull request with the repository it is in. */
1522export type RepoPull = Pull & { repo: string };
1523
1524/** `listWorkspaceSessions`. */
1525export type WorkspaceSessions = {
1526 sessions: WorkspaceSession[];
1527 queue: RepoQueue[];
1528 landed: RepoPull[];
1529 repos: SessionsRepo[];
1530};
1531
1532export type WorkspaceSessionsFilter = {
1533 shown?: SessionsShown;
1534 kind?: SessionKind;
1535 harness?: Harness;
1536 person?: string;
1537 repo?: string;
1538 item?: number;
1539 subagents?: boolean;
1540 sessionsOnly?: boolean;
1541 limit?: number;
1542};
1543
1544/** A session and the repository it runs in. */
1545export type WorkspaceSessionDetail = WorkSessionDetail & { repo: string };
1546
1547/** What kind of thing a session touched. */
1548export type TouchKind = "file" | "commit" | "branch" | "pull" | "item";
1549
1550/** Something a session touched, kept once per session. A work item's `value` is its number. */
1551export type SessionTouch = { sessionId: string; kind: TouchKind; value: string; at: string };
1552
1553/** How a session was tied to a work item or pull request. */
1554export type LinkVia = "attached" | "claim" | "branch" | "commit" | "trailer" | "opened" | "ref";
1555
1556/** A work item or pull request a session, or one of its subagents, worked on. */
1557export type SessionLink = {
1558 kind: "item" | "pull";
1559 number: number;
1560 title: string;
1561 /** The work item's state or the pull request's status. */
1562 state: string;
1563 via: LinkVia[];
1564 sessionIds: string[];
1565};
1566
1567/** A skill a session used, by name. */
1568export type SessionSkill = { sessionId: string; name: string; uses: number };
1569
1570export type ClaimState = "active" | "released" | "expired" | "handed_off";
1571
1572/** An advisory, expiring "I'm working on #N". Never a lock. */
1573export type Claim = {
1574 /** `clm_…`. */
1575 id: string;
1576 /** The repository of the checkout it was made from, if any. */
1577 repoId: string | null;
1578 /** The work item it is on: its workspace and number. */
1579 workspace: string;
1580 item: number;
1581 sessionId: string | null;
1582 actor: { id: string; username: string };
1583 harness: Harness | null;
1584 note: string | null;
1585 state: ClaimState;
1586 createdAt: string;
1587 expiresAt: string;
1588 endedAt: string | null;
1589 endReason: string | null;
1590 /** For a hand-off: who it was handed to; null for anyone. */
1591 handoffTo: string | null;
1592};
1593
1594/** `session.started` and `session.ended`. */
1595export type SessionEventData = { repoId: string; number?: number; item?: number; session: WorkSession };
1596
1597/** `work.claimed`, `work.released`, `work.claim_expired` and `work.handoff_requested`. */
1598export type ClaimEventData = {
1599 repoId?: string;
1600 workspace: string;
1601 number: number;
1602 item: number;
1603 /** The repositories the work item is linked to. */
1604 repoIds: string[];
1605 claim: Claim;
1606 others?: Claim[];
1607 tookOver?: string;
1608 reason?: string;
1609 from?: string;
1610 to?: string;
1611 note?: string;
1612};
1613
1614/** `dependency.changed`. */
1615export type DependencyChangedData = {
1616 workspace: string;
1617 number: number;
1618 item: number;
1619 repoIds: string[];
1620 blockedBy: number[];
1621 added: number[];
1622 removed: number[];
1623 cleared: number[];
1624 remaining: number[];
1625 mergedPull?: number;
1626};
1627
1628/** `overlap.detected` between two pull requests, sent once for each side of a new overlap. */
1629export type PullOverlapData = {
1630 repoId: string;
1631 number: number;
1632 pull: number;
1633 otherPull: number;
1634 files: string[];
1635 item?: number;
1636 otherItem?: number;
1637};
1638
1639/** `overlap.detected` between two sessions that touched the same files (siblings included), once for each side. */
1640export type SessionOverlapData = {
1641 repoId: string;
1642 number?: number;
1643 sessionId: string;
1644 otherSessionId: string;
1645 otherActor: string;
1646 files: string[];
1647 item?: number;
1648 otherItem?: number;
1649};
1650
1651/** `overlap.detected`: between pull requests, or between sessions (with `sessionId`). */
1652export type OverlapDetectedData = PullOverlapData | SessionOverlapData;
1653
1654/** `session.progressed`: a session recorded more (at most every few seconds). */
1655export type SessionProgressedData = { repoId: string; number?: number; sessionId: string; rootId?: string };
1656
1657/** A work item in brief. */
1658export type ItemBrief = { number: number; title: string; state: State };
1659
1660/** A pull request in brief. */
1661export type PullBrief = { number: number; title: string; status: PullStatus };
1662
1663/** A work item another is blocked by, and where it stands. */
1664export type Blocker = {
1665 number: number;
1666 title: string;
1667 state: State;
1668 /** Active claims on it. */
1669 claims: Claim[];
1670 pulls: PullBrief[];
1671};
1672
1673/** A pull request's place in the merge queue; `position` 1 is the front. */
1674export type QueuePlace = { state: QueueState; position: number };
1675
1676/** One of a work item's pull requests, with where it stands. */
1677export type WorkPull = {
1678 /** Its repository, `owner/name`. */
1679 repo: string;
1680 number: number;
1681 title: string;
1682 status: PullStatus;
1683 branch: string | null;
1684 headCommit: string | null;
1685 author: { id: string; username: string };
1686 /** `failed` when the merge queue took it out, until its head moves. */
1687 checkStatus: CheckStatus | null;
1688 requiredChecks: RequiredCheck[];
1689 mergeable: Mergeable;
1690 /** Other pull requests in progress changing the same files. */
1691 overlaps: Overlap[];
1692 /** Its place in the merge queue, while it is in it. */
1693 queue: QueuePlace | null;
1694};
1695
1696// --- Decision records (crates/contracts/src/decisions.rs) ------------------
1697
1698export type DecisionKind = "proposal" | "claim" | "integration_failure" | "integration_passed" | "fix" | "approval" | "merge" | "note";
1699
1700/** Claimed: someone said so. Tested: a check or the merge queue proved it. Approved: a person approved it. */
1701export type DecisionConfidence = "claimed" | "tested" | "approved";
1702
1703/** A structured reason kept on a work item or pull request, with how sure it is. */
1704export type Decision = {
1705 /** `dec_…`. */
1706 id: string;
1707 /** The work item it is about, by number in its workspace. */
1708 item: number | null;
1709 pull: number | null;
1710 sessionId: string | null;
1711 /** Username of who recorded it; null when g1t did. */
1712 actor: string | null;
1713 kind: DecisionKind;
1714 confidence: DecisionConfidence;
1715 summary: string;
1716 /**
1717 * What it rests on, kept as recorded, in snake_case: `commit`, `check`
1718 * (`name`, `conclusion`, `url`), `combination`, `other_pulls`,
1719 * `suspects`, `bisected`, `merge_queue`.
1720 */
1721 evidence: Record<string, unknown>;
1722 /** RFC 3339. */
1723 createdAt: string;
1724};
1725
1726/** One repository a work item is linked to, with what landed there and its pull requests for it. */
1727export type WorkRepo = ItemRepo & {
1728 /** The checks every pull request into its default branch must pass. */
1729 requiredChecks: string[];
1730 /** Its pull requests for the work item, oldest first. */
1731 pulls: WorkPull[];
1732};
1733
1734/** A repository of the workspace the work may belong in; confirming it is `linkWorkItem` as a target. */
1735export type SuggestedTarget = { repoId: string; path: string; why: string; score: number };
1736
1737/** Everything about a work item an agent needs to pick it up: `get_work`. */
1738export type WorkContext = {
1739 item: WorkItem;
1740 /** The items under its "Definition of done" heading. */
1741 definitionOfDone: string[];
1742 /** Its repositories: origin, then targets, then touched. */
1743 repos: WorkRepo[];
1744 /** The targets it has not landed in yet, by path. */
1745 missing: string[];
1746 /** Repositories it may belong in, likeliest first, each with why. */
1747 suggestedTargets: SuggestedTarget[];
1748 /** Active claims on it. */
1749 claims: Claim[];
1750 /** Sessions on it or its pull requests, most recently heard from first. */
1751 sessions: WorkSession[];
1752 blockedBy: Blocker[];
1753 /** The work items it blocks. */
1754 blocks: ItemBrief[];
1755 /** On it and its pull requests, newest first. */
1756 decisions: Decision[];
1757 /** The latest `integration_failure` decision among them. */
1758 lastIntegrationFailure: Decision | null;
1759 /** Its latest coordination events, newest first. */
1760 recent: G1tEvent[];
1761};
1762
1763/** A harness session and what it recorded. */
1764export type WorkSessionDetail = {
1765 /** Pending repository coordination events; reading does not acknowledge them. */
1766 updates?: { id: string; kind: string; data: unknown; at: string }[];
1767 session: WorkSession;
1768 entries: SessionEntry[];
1769 /** Its subagents and theirs, in the order they started. */
1770 children?: WorkSession[];
1771 /** What it and its subagents touched. */
1772 touched?: SessionTouch[];
1773 /** The work items and pull requests it and its subagents worked on. */
1774 links?: SessionLink[];
1775 skills?: SessionSkill[];
1776 /** For a subagent: the session at the top of its tree. */
1777 root?: WorkSession | null;
1778 /**
1779 * The viewer is not a member of the repository's workspace: the session
1780 * shows that it runs, who runs it and what it is tied to, nothing more.
1781 */
1782 limited?: boolean;
1783};
1784
1785/** Reading coordination: work service methods (services/work/src/coordination, decisions.rs). */
1786export interface CoordinationApi {
1787 getWork(workspace: string, number: number, viewer: Viewer): Promise<Result<WorkContext>>;
1788 /**
1789 * A workspace's claims on the work items the viewer may see, newest
1790 * first; active ones unless `state` says otherwise. With `repo`, those
1791 * made from it or on work items linked to it.
1792 */
1793 listClaims(workspace: string, viewer: Viewer, filter?: { state?: ClaimState; item?: number; repo?: RepoPath }): Promise<Result<Claim[]>>;
1794 /** Most recently heard from first; `item` is a work item of the repository's workspace. */
1795 listWorkSessions(
1796 repo: RepoPath,
1797 viewer: Viewer,
1798 filter?: { state?: SessionState; item?: number; pull?: number; path?: string; subagents?: boolean },
1799 ): Promise<Result<WorkSession[]>>;
1800 getWorkSession(repo: RepoPath, id: string, viewer: Viewer, afterSeq?: number): Promise<Result<WorkSessionDetail>>;
1801 /** The sessions of every repository of a workspace the viewer may read, with its merge queues and what landed. */
1802 listWorkspaceSessions(workspace: string, viewer: Viewer, filter?: WorkspaceSessionsFilter): Promise<Result<WorkspaceSessions>>;
1803 /** One harness or hosted session of a workspace, in whichever of its repositories it runs. */
1804 getWorkspaceSession(workspace: string, id: string, viewer: Viewer, afterSeq?: number): Promise<Result<WorkspaceSessionDetail>>;
1805 /**
1806 * Ends a session by hand, from the site: the actor's own, or anyone's
1807 * when they have write access to the repository. It ends as
1808 * `ended by hand by @actor`, with its subagents still going
1809 * (`end_session` with `by_hand`; g1t_contracts::work::session_end_reason).
1810 */
1811 endWorkSession(actor: User, repo: RepoPath, id: string): Promise<Result<WorkSession>>;
1812 /**
1813 * A person names a session from the site, pinning its title: neither
1814 * g1t nor its agent replaces it. No title unpins it, and g1t names it
1815 * again from its record. The actor's own, or anyone's with write access
1816 * to the repository (`title_session`).
1817 */
1818 titleWorkSession(actor: User, repo: RepoPath, id: string, title: string | null): Promise<Result<WorkSession>>;
1819 /**
1820 * The harness sessions tied to some pull requests (at most 100), any way
1821 * a session is tied to one; a subagent's as the session that started it.
1822 * Only pull requests with any are listed.
1823 */
1824 pullSessions(repo: RepoPath, viewer: Viewer, numbers: number[]): Promise<Result<PullSessions[]>>;
1825 /** A work item's (with its pull requests') or a pull request's, oldest first. */
1826 listDecisions(thread: Thread, viewer: Viewer): Promise<Result<Decision[]>>;
1827}
1828
1829/**
1830 * Delivery counts cover the requested UTC days; waiting counts are current.
1831 * `checking` and `failed` count each open pull request once, by its head's
1832 * checks (statuses, check runs and g1t Actions jobs); one with none is in neither.
1833 */
1834export type DeliveryMetrics = {
1835 repoId: string;
1836 opened: number;
1837 merged: number;
1838 active: number;
1839 unreviewed: number;
1840 checking: number;
1841 failed: number;
1842 queued: number;
1843 leadSeconds: number;
1844 reviewSeconds: number;
1845 reviewed: number;
1846};
1847/** `checksUnavailable`: g1t Actions could not be read, so `checking` and `failed` are not known. */
1848export type DeliveryInsights = { projects: DeliveryMetrics[]; days: { day: string; merged: number }[]; checksUnavailable: boolean };
1849
1850/**
1851 * What a workspace's work says over the last weeks (crates/contracts
1852 * work/signals.rs). A pull request is an agent's when g1t's agent opened
1853 * it, it was opened as an agent's, or a harness session is tied to it; its
1854 * owner is the person who asked for it, else its author.
1855 */
1856export type WorkspaceSignalsArgs = {
1857 workspace: string;
1858 repoIds: string[];
1859 /** The last UTC day covered, `YYYY-MM-DD`. */
1860 until: string;
1861 /** Weeks of seven UTC days ending with `until`: 1 through 13. */
1862 weeks: number;
1863 /** UTC days ending with `until` for `days`: 1 through 28. */
1864 days: number;
1865 /** The first UTC day `pulls` reaches back to: at most 400 days before `until`. */
1866 since: string;
1867};
1868/**
1869 * One repository's week from UTC day `start`; empty weeks are left out.
1870 * `stalled`: open when the week ended (now, for this week), opened three
1871 * days or more before, and no activity (comment, review, timeline event,
1872 * its latest push, a check or status on its current head, an acceptance
1873 * check, a session entry, a tied session or its subagent running) in the
1874 * three days before it ended.
1875 * `sessionCostUsd`: what the harness sessions started in the week cost,
1876 * estimated at list price from the token usage they reported.
1877 */
1878export type SignalWeek = {
1879 repoId: string;
1880 start: string;
1881 merged: number;
1882 agentMerged: number;
1883 /** Of `agentMerged`, the ones a person asked for changes on first. */
1884 agentReworked: number;
1885 agentDrafted: number;
1886 agentDraftSeconds: number;
1887 reviewed: number;
1888 reviewSeconds: number;
1889 stalled: number;
1890 sessionCostUsd: number;
1891 /** Commits agent sessions pushed outside any pull request (to the default branch, most of all), each once. */
1892 agentCommits: number;
1893};
1894export type SignalDay = {
1895 repoId: string;
1896 day: string;
1897 opened: number;
1898 merged: number;
1899 agentMerged: number;
1900 reworked: number;
1901 reviews: number;
1902 sessions: number;
1903 agentCommits: number;
1904};
1905/** An open pull request no one has reviewed, oldest first; `blocks`: the open work items waiting on its work item. */
1906export type WaitingPull = {
1907 repoId: string;
1908 number: number;
1909 title: string;
1910 createdAt: string;
1911 agent: boolean;
1912 draftSeconds: number | null;
1913 item: number | null;
1914 blocks: number[];
1915};
1916export type SignalPull = {
1917 repoId: string;
1918 number: number;
1919 title: string;
1920 status: "draft" | "open" | "merged" | "closed";
1921 agent: boolean;
1922 ownerId: string;
1923 ownerName: string;
1924 createdAt: string;
1925 mergedAt: string | null;
1926 item: number | null;
1927 milestone: number | null;
1928 /**
1929 * Its share of its harness sessions' cost: each session's split evenly
1930 * among the pull requests it is tied to (a subagent tied to none counts
1931 * with its root session's).
1932 */
1933 sessionCostUsd: number;
1934};
1935/** One person's use of one harness in one repository, root sessions only. */
1936export type SignalHarness = { repoId: string; ownerId: string; ownerName: string; harness: string; firstAt: string; sessions: number };
1937/** One person's merged pull requests in one repository over its history; `recent`: in the 90 days ending with `until`. */
1938export type SignalAuthor = { repoId: string; ownerId: string; ownerName: string; firstMergedAt: string; lastMergedAt: string; merged: number; recent: number };
1939export type SignalItem = { number: number; title: string; state: string; milestone: number | null };
1940export type SignalMilestone = { number: number; title: string; state: string; dueOn: string | null; createdAt: string; closedAt: string | null };
1941export type WorkspaceSignals = {
1942 weeks: SignalWeek[];
1943 days: SignalDay[];
1944 waiting: WaitingPull[];
1945 pulls: SignalPull[];
1946 pullsCapped: boolean;
1947 authors: SignalAuthor[];
1948 harnesses: SignalHarness[];
1949 items: SignalItem[];
1950 milestones: SignalMilestone[];
1951};