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