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.

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