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