Skip to content
1,422 linesCodeBlameRaw

Pick any line to see why it is the way it is: the commit, the pull request and issue it came from, and what the agent was thinking.

The statement is a month at a time, a line per kind of charge1//! The OpenAPI document, generated from the same list the routes are.
2//!
3//! The docs site builds its API reference from a copy of this document,
4//! `apps/docs/src/data/openapi.json`. A test keeps the copy current: run
5//! `G1T_WRITE_OPENAPI=1 cargo test -p g1t-api openapi` to rewrite it.
6
Thirteen MCP tools and classic token scopes; agents rate their confidence and can be put on an issue in one step7use g1t_contracts::scopes::scope_for;
The statement is a month at a time, a line per kind of charge8use serde_json::{Map, Value, json};
9
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb9710use crate::about::AboutOp;
The artifacts service is services/artifacts, the Worker g1t-artifacts, bound as ARTIFACTS by the API, the site and the agents; its live rooms move to it with a Durable Object transfer from g1t-docs-service, and its database, bucket, indexes and queue keep their names. The git store's binding and settings are GITSTORE, its ops scripts gitstore-*, and workflow run artifacts keep their compatible API under run_artifacts modules. The deploy tool puts a Worker that has never deployed before the Workers in its stage that bind to it, and the deploy guide gives the cutover runbook.11use crate::run_artifacts::ArtifactsOp;
Merge branch 'customize' into main: Customize replaces the Marketplace, the Artifacts trash and spaces changes, the Security-free cleanup, with the desktop app's own title bar kept in the shell.12use crate::customize::CustomizeOp;
Merge branch 'worktree-agent-a5a5de74d8863d554' into worktree-agent-a16631325aecf58ca13use crate::deploy_keys::DeployKeysOp;
Merge branch 'mirroring' into artifacts-mode14use crate::mirrors::MirrorsOp;
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb9715use crate::deployments::DeploymentsOp;
Merge packages: roles, Actions access, source label, soft delete, API16use crate::packages::PackagesOp;
Merge main into Artifacts Phase 217use crate::folios::FoliosOp;
Merge branch 'worktree-agent-a3abfcce648e87dca'18use crate::protection::ProtectionOp;
API and MCP for a workspace's personal access token rules, members' tokens and approvals19use crate::token_policy::TokenOp;
The statement is a month at a time, a line per kind of charge20use crate::operations::Op;
Merge checks: statuses and check runs on every commit21use crate::checks::ChecksOp;
Merge rulesets: branch and tag rules, agent-first, enforced on push and merge22use crate::rules::RulesOp;
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar23use crate::security::SecurityOp;
The statement is a month at a time, a line per kind of charge24use crate::rest::{ROUTES, Route};
25
26/// The sections of the API reference: a name, what it covers, and its
27/// operations in the order a reader meets them.
28const SECTIONS: &[(&str, &str, &[Op])] = &[
29 (
30 "Accounts",
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look31 "Signing in from a tool, who a token acts as, and your email addresses.",
Merge email confirmation gate: a code and a link, nothing until confirmed (identity 0036)32 &[Op::Whoami, Op::ListEmails, Op::AddEmail, Op::ConfirmEmail, Op::RemoveEmail, Op::UpdateEmailSettings],
The statement is a month at a time, a line per kind of charge33 ),
34 (
API: notifications over REST and MCP, with notifications scopes35 "Notifications",
36 "Your inbox: a thread for each thing you were told about (an issue, a pull request, a workflow on a branch, a deployment), why you were told, and what you subscribe to and watch. Your own: personal tokens and sessions only.",
37 &[
38 Op::ListNotifications,
39 Op::MarkNotificationsRead,
40 Op::GetNotificationThread,
41 Op::MarkThreadRead,
42 Op::MarkThreadDone,
43 Op::SaveThread,
44 Op::SnoozeThread,
45 Op::GetThreadSubscription,
46 Op::SetThreadSubscription,
47 Op::DeleteThreadSubscription,
48 Op::GetRepoSubscription,
49 Op::SetRepoSubscription,
50 Op::DeleteRepoSubscription,
51 Op::ListWatchedRepos,
52 ],
53 ),
54 (
API: pinned projects over REST and MCP55 "Pinned projects",
56 "The projects you keep at the top of a workspace's sidebar, in your order, up to eight a workspace. Your own: personal tokens and sessions only.",
57 &[Op::ListPinnedProjects, Op::PinProject, Op::UnpinProject, Op::ReorderPinnedProjects],
58 ),
59 (
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb9760 "Projects",
61 "A project is what a workspace builds and runs, from a repository or a root directory in one. Each says what it is, where it runs and where to find it: its homepage, docs and other links.",
62 &[Op::ListProjects, Op::GetProject, Op::UpdateProject],
63 ),
64 (
The statement is a month at a time, a line per kind of charge65 "Workspaces",
66 "A workspace owns repositories and is the first part of their address. People and agents work in workspaces.",
Merge branch 'worktree-agent-ad7c6d88d93adc817'67 &[Op::GetWorkspace, Op::CreateWorkspace, Op::UpdateWorkspace, Op::DeleteWorkspace],
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look68 ),
69 (
API and MCP for a workspace's personal access token rules, members' tokens and approvals70 "Personal access tokens",
One kind of access token; presence and status; usernames keep their case; the tour is a miniature of the real app; icons for password managers71 "A workspace's rules for its members' personal access tokens: whether tokens made for all of a member's workspaces reach it, whether tokens may be made for it alone and wait for an owner's approval, and how long a token may last; the tokens that reach it, approving or denying the ones that wait, and revoking one there. Owners only, as people.",
API and MCP for a workspace's personal access token rules, members' tokens and approvals72 &[
73 Op::Tokens(TokenOp::GetTokenPolicy),
74 Op::Tokens(TokenOp::SetTokenPolicy),
75 Op::Tokens(TokenOp::ListMemberTokens),
76 Op::Tokens(TokenOp::ListTokenRequests),
77 Op::Tokens(TokenOp::ReviewTokenRequest),
78 Op::Tokens(TokenOp::RevokeMemberToken),
79 ],
80 ),
81 (
Merge main (membership, two-factor, GitHub repo roles) into tokens82 "Members",
83 "A workspace's members and owners: who belongs to it, their roles (owner or member, with billing manager and security manager on top), handing it to another member, and leaving it.",
84 &[Op::ListMembers, Op::UpdateMember, Op::RemoveMember, Op::TransferOwnership, Op::LeaveWorkspace],
85 ),
86 (
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look87 "Invites",
Merge workspace invitations: nobody joins a workspace without saying yes, people are found by username, your own invites can bring someone in, and nobody is left without a workspace (identity 0040)88 "While g1t is invite-only, every new account needs an invite. Your invites, inviting people into a workspace by username or email, and answering the invitations to workspaces sent to you.",
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look89 &[
90 Op::ListInvites,
91 Op::CreateInvite,
92 Op::RevokeInvite,
93 Op::ListWorkspaceInvites,
94 Op::InviteMember,
95 Op::RevokeWorkspaceInvite,
Merge workspace invitations: nobody joins a workspace without saying yes, people are found by username, your own invites can bring someone in, and nobody is left without a workspace (identity 0040)96 Op::ListInvitations,
97 Op::AcceptInvitation,
98 Op::DeclineInvitation,
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look99 ],
The statement is a month at a time, a line per kind of charge100 ),
101 (
Usage, Billing settings and prepaid AI credit; fixes from the UX audit102 "Billing",
Merge the AI Gateway: Anthropic's Messages API on a workspace's tokens103 "A workspace's usage, its budget, its AI credit, its invoices and its AI Gateway requests. Members read them; owners change the budget and buy credit, as people. g1t's agents never change billing.",
Usage, Billing settings and prepaid AI credit; fixes from the UX audit104 &[
105 Op::GetUsage,
106 Op::GetBudget,
107 Op::SetBudget,
108 Op::GetAiCredit,
109 Op::BuyAiCredit,
110 Op::ListInvoices,
111 Op::GetBillingDetails,
Merge the AI Gateway: Anthropic's Messages API on a workspace's tokens112 Op::ListGatewayRequests,
Usage, Billing settings and prepaid AI credit; fixes from the UX audit113 ],
114 ),
115 (
The statement is a month at a time, a line per kind of charge116 "Repositories",
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look117 "A repository, how it handles pull requests, and its timeline: renaming, archiving, moving and deleting it.",
The statement is a month at a time, a line per kind of charge118 &[
119 Op::ListRepos,
120 Op::CreateRepo,
121 Op::GetRepo,
122 Op::UpdateRepo,
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look123 Op::RenameRepo,
124 Op::RenameBranch,
125 Op::SetRepoVisibility,
126 Op::ArchiveRepo,
127 Op::UnarchiveRepo,
128 Op::TransferRepo,
129 Op::DeleteRepo,
130 Op::ListDeletedRepos,
131 Op::RestoreRepo,
132 Op::PurgeRepo,
The statement is a month at a time, a line per kind of charge133 Op::GetRepoSettings,
134 Op::UpdateRepoSettings,
Fast pages, required checks on the branch, self-hosted runners, honest incidents135 Op::ListCheckNames,
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar136 Op::GetCodeownersErrors,
The statement is a month at a time, a line per kind of charge137 Op::ListEvents,
138 ],
139 ),
140 (
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97141 "Repository insights",
142 "What a repository's default branch says about it, read in the background and kept by commit: the languages it is written in, who made it, and its license.",
143 &[Op::About(AboutOp::GetLanguages), Op::About(AboutOp::ListContributors), Op::About(AboutOp::GetLicense)],
144 ),
145 (
146 "Stars",
147 "Starring a repository, to keep it and to say you like it: who starred one, and what you starred.",
148 &[
149 Op::About(AboutOp::ListStargazers),
150 Op::About(AboutOp::ListStarred),
151 Op::About(AboutOp::CheckStarred),
152 Op::About(AboutOp::Star),
153 Op::About(AboutOp::Unstar),
154 ],
155 ),
156 (
157 "Releases",
158 "A release is a tag published with a title and notes. The latest is the newest published one that is neither a draft nor a prerelease.",
159 &[
160 Op::About(AboutOp::ListReleases),
161 Op::About(AboutOp::CreateRelease),
162 Op::About(AboutOp::GetLatestRelease),
163 Op::About(AboutOp::GetReleaseByTag),
164 Op::About(AboutOp::GetRelease),
165 Op::About(AboutOp::UpdateRelease),
166 Op::About(AboutOp::DeleteRelease),
167 ],
168 ),
169 (
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look170 "Access",
Merge branch 'worktree-agent-a5a5de74d8863d554' into worktree-agent-a16631325aecf58ca171 "Who can do what in a repository: repository roles, people given a role on one repository (outside collaborators when they are not members), invitations, a workspace's base permission, and deploy keys: SSH keys that reach one repository.",
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look172 &[
173 Op::ListCollaborators,
174 Op::AddCollaborator,
175 Op::UpdateCollaborator,
176 Op::RemoveCollaborator,
177 Op::GetCollaboratorPermission,
178 Op::ListRepoInvitations,
179 Op::RevokeRepoInvitation,
180 Op::ListMyRepoInvitations,
181 Op::AcceptRepoInvitation,
182 Op::DeclineRepoInvitation,
183 Op::SetBasePermission,
184 Op::ListOutsideCollaborators,
Merge branch 'worktree-agent-a5a5de74d8863d554' into worktree-agent-a16631325aecf58ca185 Op::DeployKeys(DeployKeysOp::ListDeployKeys),
186 Op::DeployKeys(DeployKeysOp::GetDeployKey),
187 Op::DeployKeys(DeployKeysOp::CreateDeployKey),
188 Op::DeployKeys(DeployKeysOp::DeleteDeployKey),
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look189 ],
190 ),
191 (
Merge branch 'mirroring' into artifacts-mode192 "Mirroring",
193 "A repository's links to copies of it on other hosts: a mirror stands by as a read-only copy of a remote that leads, until someone takes over on g1t and later hands back, or moves it to g1t for good. A repository g1t leads can be mirrored to other hosts.",
194 &[
195 Op::Mirrors(MirrorsOp::GetMirror),
196 Op::Mirrors(MirrorsOp::GetHandBackPlan),
197 Op::Mirrors(MirrorsOp::TakeOver),
198 Op::Mirrors(MirrorsOp::SetCiFailover),
199 Op::Mirrors(MirrorsOp::HandBack),
200 Op::Mirrors(MirrorsOp::MoveToG1t),
201 Op::Mirrors(MirrorsOp::SyncMirror),
202 Op::Mirrors(MirrorsOp::AddRemote),
203 Op::Mirrors(MirrorsOp::UpdateRemote),
204 Op::Mirrors(MirrorsOp::RemoveRemote),
205 ],
206 ),
207 (
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar208 "Teams",
209 "Groups of a workspace's members: given a role on repositories together, mentioned together as @workspace/team, and asked to review together. Any member may create a team; the workspace's owners and the team's maintainers manage it.",
210 &[
211 Op::ListTeams,
212 Op::CreateTeam,
213 Op::GetTeam,
214 Op::UpdateTeam,
215 Op::DeleteTeam,
216 Op::ListTeamMembers,
217 Op::SetTeamMember,
218 Op::RemoveTeamMember,
219 Op::ListChildTeams,
220 Op::ListTeamRepos,
221 Op::SetTeamRepo,
222 Op::RemoveTeamRepo,
223 Op::SetTeamReviewAssignment,
224 Op::ListUserTeams,
225 ],
226 ),
227 (
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily228 "Security",
229 "Secrets found in what is pushed and in a repository's history, and dependencies with known vulnerabilities: listing the alerts, and dismissing or reopening them.",
230 &[Op::ListSecurityAlerts, Op::DismissSecurityAlert, Op::ReopenSecurityAlert],
231 ),
232 (
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar233 "Secret scanning",
234 "Secrets found in pushes and history, where each one is, pushing past push protection with a reason (and asking for approval when the workspace delegates bypasses), checking with a secret's issuer whether it still works, and custom patterns.",
235 &[
236 Op::Security(SecurityOp::ListSecretAlerts),
237 Op::Security(SecurityOp::GetSecretAlert),
238 Op::Security(SecurityOp::UpdateSecretAlert),
239 Op::Security(SecurityOp::ListSecretLocations),
240 Op::Security(SecurityOp::BypassPushProtection),
241 Op::Security(SecurityOp::CheckSecretValidity),
242 Op::Security(SecurityOp::ListBypassRequests),
243 Op::Security(SecurityOp::ReviewBypassRequest),
244 Op::Security(SecurityOp::ListCustomPatterns),
245 Op::Security(SecurityOp::CreateCustomPattern),
246 Op::Security(SecurityOp::UpdateCustomPattern),
247 Op::Security(SecurityOp::DeleteCustomPattern),
248 Op::Security(SecurityOp::DryRunCustomPattern),
249 ],
250 ),
251 (
252 "Code scanning",
253 "Results of static analysis tools, uploaded as SARIF: alerts on the default branch, the analyses that made them, uploads, and putting g1t on an alert to fix it.",
254 &[
255 Op::Security(SecurityOp::ListCodeAlerts),
256 Op::Security(SecurityOp::GetCodeAlert),
257 Op::Security(SecurityOp::UpdateCodeAlert),
258 Op::Security(SecurityOp::ListAnalyses),
259 Op::Security(SecurityOp::UploadSarif),
260 Op::Security(SecurityOp::GetSarifUpload),
261 Op::Security(SecurityOp::FixAlert),
262 ],
263 ),
264 (
265 "Supply chain",
266 "What a repository depends on: vulnerability alerts, the dependency graph, an SPDX SBOM of it, and comparing two commits' dependencies as dependency review does.",
267 &[
268 Op::Security(SecurityOp::ListVulnerabilityAlerts),
269 Op::Security(SecurityOp::GetVulnerabilityAlert),
270 Op::Security(SecurityOp::UpdateVulnerabilityAlert),
271 Op::Security(SecurityOp::GetDependencyGraph),
272 Op::Security(SecurityOp::GetSbom),
273 Op::Security(SecurityOp::CompareDependencies),
274 ],
275 ),
276 (
277 "Security settings",
278 "When pull request checks fail, dependency review's policy, delegated bypass and validity checks, and a workspace's security overview.",
279 &[
280 Op::Security(SecurityOp::GetSettings),
281 Op::Security(SecurityOp::UpdateSettings),
282 Op::Security(SecurityOp::GetWorkspaceSettings),
283 Op::Security(SecurityOp::UpdateWorkspaceSettings),
284 Op::Security(SecurityOp::GetOverview),
285 ],
286 ),
287 (
The API carries the advisory and report operations the service gained, with the two new repository security settings (history_alerts, private_vulnerability_reporting) on get_security_settings and update_security_settings, and the docs' OpenAPI copy is regenerated to match.288 "Security advisories",
289 "What a repository says about vulnerabilities in what it ships: advisories drafted, published and withdrawn by its maintainers, and the vulnerability reports people send them privately when the repository takes them.",
290 &[
291 Op::Security(SecurityOp::ListAdvisories),
292 Op::Security(SecurityOp::CreateAdvisory),
293 Op::Security(SecurityOp::GetAdvisory),
294 Op::Security(SecurityOp::UpdateAdvisory),
295 Op::Security(SecurityOp::SetAdvisoryState),
296 Op::Security(SecurityOp::ListReports),
297 Op::Security(SecurityOp::ReportVulnerability),
298 Op::Security(SecurityOp::ReviewReport),
299 ],
300 ),
301 (
Merge rulesets: branch and tag rules, agent-first, enforced on push and merge302 "Rules",
303 "Rulesets: what may happen to a repository's branches and tags and what a pull request needs before it merges, for a repository or across a workspace; the rules that hold for one branch; and how they judged each push and merge, with insights.",
304 &[
305 Op::Rules(RulesOp::ListRepoRulesets),
306 Op::Rules(RulesOp::CreateRepoRuleset),
307 Op::Rules(RulesOp::GetRepoRuleset),
308 Op::Rules(RulesOp::UpdateRepoRuleset),
309 Op::Rules(RulesOp::DeleteRepoRuleset),
310 Op::Rules(RulesOp::GetBranchRules),
311 Op::Rules(RulesOp::ListRuleEvaluations),
312 Op::Rules(RulesOp::ListWorkspaceRulesets),
313 Op::Rules(RulesOp::CreateWorkspaceRuleset),
314 Op::Rules(RulesOp::GetWorkspaceRuleset),
315 Op::Rules(RulesOp::UpdateWorkspaceRuleset),
316 Op::Rules(RulesOp::DeleteWorkspaceRuleset),
317 Op::Rules(RulesOp::ListWorkspaceRuleEvaluations),
318 ],
319 ),
320 (
Merge checks: statuses and check runs on every commit321 "Checks",
322 "What CI, integrations and g1t Actions say about a commit, in the shapes CI tools already send: statuses (a state per context) and check runs (a lifecycle, a conclusion, a Markdown report, annotations on lines and buttons), grouped per reporter into check suites. g1t Actions jobs are check runs too. Required checks are met by either.",
323 &[
324 Op::Checks(ChecksOp::CreateCommitStatus),
325 Op::Checks(ChecksOp::ListCommitStatuses),
326 Op::Checks(ChecksOp::GetCombinedStatus),
327 Op::Checks(ChecksOp::CreateCheckRun),
328 Op::Checks(ChecksOp::UpdateCheckRun),
329 Op::Checks(ChecksOp::GetCheckRun),
330 Op::Checks(ChecksOp::ListCheckRunAnnotations),
331 Op::Checks(ChecksOp::RerequestCheckRun),
332 Op::Checks(ChecksOp::ListCheckRunsForRef),
333 Op::Checks(ChecksOp::ListCheckSuitesForRef),
334 Op::Checks(ChecksOp::GetCheckSuite),
335 Op::Checks(ChecksOp::RerequestCheckSuite),
336 ],
337 ),
338 (
The statement is a month at a time, a line per kind of charge339 "Issues",
340 "What should change in a repository, with labels and comments. Issues and pull requests share one sequence of numbers.",
341 &[
342 Op::ListIssues,
343 Op::CreateIssue,
344 Op::GetIssue,
345 Op::UpdateIssue,
346 Op::CloseIssue,
347 Op::ReopenIssue,
348 Op::AssignIssue,
Thirteen MCP tools and classic token scopes; agents rate their confidence and can be put on an issue in one step349 Op::Delegate,
The statement is a month at a time, a line per kind of charge350 Op::AddComment,
Merge Actions: cross-repo workflows and actions, release and deployment triggers, step timeouts351 Op::EditComment,
352 Op::DeleteComment,
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar353 Op::ListIssueLabels,
354 Op::AddIssueLabels,
355 Op::SetIssueLabels,
356 Op::RemoveIssueLabels,
357 ],
358 ),
359 (
360 "Labels and milestones",
361 "A repository's labels, which issues and pull requests carry by name, and its milestones, which gather them under a goal and a due date.",
362 &[
The statement is a month at a time, a line per kind of charge363 Op::ListLabels,
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar364 Op::CreateLabel,
365 Op::UpdateLabel,
366 Op::DeleteLabel,
367 Op::AddDefaultLabels,
368 Op::ListMilestones,
369 Op::CreateMilestone,
370 Op::GetMilestone,
371 Op::UpdateMilestone,
372 Op::DeleteMilestone,
The statement is a month at a time, a line per kind of charge373 ],
374 ),
375 (
376 "Plans",
377 "An outcome turned into the issues that would get there, with the order they must merge in.",
378 &[Op::PlanWork, Op::GetPlan, Op::ApplyPlan],
379 ),
380 (
381 "Pull requests",
382 "A proposed change in its own fork or on a branch. Several can be made for one issue; the one merged resolves it.",
383 &[
384 Op::ListPullRequests,
385 Op::CreatePullRequest,
386 Op::GetPullRequest,
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar387 Op::UpdatePullRequest,
The statement is a month at a time, a line per kind of charge388 Op::GetPullRequestChanges,
389 Op::MarkPullRequestReady,
Merge Actions: cross-repo workflows and actions, release and deployment triggers, step timeouts390 Op::ConvertPullRequestToDraft,
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar391 Op::RequestReviewers,
392 Op::RemoveRequestedReviewers,
The statement is a month at a time, a line per kind of charge393 Op::ReviewPullRequest,
394 Op::MergePullRequest,
395 Op::ClosePullRequest,
Merge Actions: cross-repo workflows and actions, release and deployment triggers, step timeouts396 Op::ReopenPullRequest,
The statement is a month at a time, a line per kind of charge397 Op::GetMergeQueue,
398 Op::MessageAgent,
399 Op::AnswerMessage,
400 Op::TakeMessages,
401 ],
402 ),
403 (
404 "Sessions",
405 "The record of how a pull request was made: prompts, reasoning and the tools that ran.",
406 &[Op::ReadSession, Op::RecordSession],
407 ),
408 (
409 "Memory",
410 "What agents and people learned that the next agent should know, for one project or across a workspace. Members and g1t's agents only; never a secret.",
411 &[Op::Remember, Op::Recall],
412 ),
413 (
Search across all of g1t, Explore, and a command palette414 "Search",
415 "One search across all of g1t: repositories, code, issues, pull requests, people and workspaces. Public content for everyone, and private content in workspaces you belong to.",
416 &[Op::Search],
417 ),
418 (
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API419 "Context",
420 "A workspace's context hub: a catalog of what it builds and runs, built from its repositories, deployments and integrations, and one search across the catalog, docs, issues, pull requests and memory.",
421 &[Op::SearchContext, Op::GetEntity],
422 ),
423 (
The statement is a month at a time, a line per kind of charge424 "Actions",
425 "GitHub Actions workflows in .g1t/workflows, their runs, and their jobs' logs.",
426 &[
427 Op::ListWorkflows,
428 Op::ListWorkflowRuns,
429 Op::GetWorkflowRun,
430 Op::GetJobLogs,
431 Op::DispatchWorkflow,
432 Op::CancelWorkflowRun,
433 Op::RerunWorkflowRun,
434 Op::UpdateWorkflow,
Actions: OIDC tokens, the toolkit's cache and artifact services, and artifacts in R2435 Op::Artifacts(ArtifactsOp::ListArtifacts),
436 Op::Artifacts(ArtifactsOp::ListRunArtifacts),
437 Op::Artifacts(ArtifactsOp::GetArtifact),
438 Op::Artifacts(ArtifactsOp::DownloadArtifact),
439 Op::Artifacts(ArtifactsOp::DeleteArtifact),
440 Op::Artifacts(ArtifactsOp::GetArtifactRetention),
441 Op::Artifacts(ArtifactsOp::SetArtifactRetention),
The statement is a month at a time, a line per kind of charge442 ],
443 ),
444 (
Merge packages: roles, Actions access, source label, soft delete, API445 "Packages",
446 "A workspace's packages in every registry: their versions and download counts, deleting and restoring them within 30 days, their visibility and repository, the people and teams with a role on them, and which repositories' workflows may use them (Manage Actions access). A package is named by its type and its name, URL-encoded where it holds a slash.",
447 &[
448 Op::Packages(PackagesOp::ListPackages),
449 Op::Packages(PackagesOp::GetPackage),
450 Op::Packages(PackagesOp::UpdatePackage),
451 Op::Packages(PackagesOp::DeletePackage),
452 Op::Packages(PackagesOp::RestorePackage),
453 Op::Packages(PackagesOp::ListVersions),
454 Op::Packages(PackagesOp::GetVersion),
455 Op::Packages(PackagesOp::DeleteVersion),
456 Op::Packages(PackagesOp::RestoreVersion),
457 Op::Packages(PackagesOp::LinkPackage),
458 Op::Packages(PackagesOp::UnlinkPackage),
459 Op::Packages(PackagesOp::ListAccess),
460 Op::Packages(PackagesOp::SetAccess),
461 Op::Packages(PackagesOp::RemoveAccess),
462 Op::Packages(PackagesOp::ListActionsAccess),
463 Op::Packages(PackagesOp::SetActionsAccess),
464 Op::Packages(PackagesOp::RemoveActionsAccess),
465 ],
466 ),
467 (
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97468 "Deployments",
469 "A repository's deployments wherever they run: reported from any CI with these routes, made by g1t Actions jobs with an `environment:`, or built on g1t.page. Each has statuses, shows on its commit as the check `deploy / <environment>`, and belongs to an environment.",
470 &[
471 Op::Deployments(DeploymentsOp::ListDeployments),
472 Op::Deployments(DeploymentsOp::CreateDeployment),
473 Op::Deployments(DeploymentsOp::GetDeployment),
474 Op::Deployments(DeploymentsOp::ListDeploymentStatuses),
475 Op::Deployments(DeploymentsOp::CreateDeploymentStatus),
476 Op::Deployments(DeploymentsOp::ListEnvironments),
477 Op::Deployments(DeploymentsOp::GetEnvironment),
Merge branch 'claude/cool-newton-7nm8r9' (#36): real GitHub Actions on g1t, managed deploy workflows, g1t/deploy and the Sysbox runner 0.2.0. The merge keeps both sides' webhook events and API operations, says agent images need tar, guards the job engine's stop to Unix so the runner builds on Windows again, and keeps the moves tests' previews from going idle as the calendar passes their fixture's date.478 Op::Deployments(DeploymentsOp::StartDeploymentBuild),
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97479 ],
480 ),
481 (
Merge branch 'worktree-agent-a3abfcce648e87dca'482 "Run protection",
483 "What keeps workflow runs safe: environments' protection rules (required reviewers, a wait timer, which branches may deploy) and the reviews of the jobs they hold, approving a pull request's run from outside, what a job's token gets when its workflow writes no `permissions:`, and repository_dispatch, which a job's own token may send.",
484 &[
485 Op::Protection(ProtectionOp::UpdateEnvironment),
486 Op::Protection(ProtectionOp::DeleteEnvironment),
487 Op::Protection(ProtectionOp::GetPendingDeployments),
488 Op::Protection(ProtectionOp::ReviewPendingDeployments),
489 Op::Protection(ProtectionOp::ApproveWorkflowRun),
490 Op::Protection(ProtectionOp::GetWorkflowPermissions),
491 Op::Protection(ProtectionOp::SetWorkflowPermissions),
492 Op::Protection(ProtectionOp::GetForkPrApproval),
493 Op::Protection(ProtectionOp::SetForkPrApproval),
Merge Actions: cross-repo workflows and actions, release and deployment triggers, step timeouts494 Op::Protection(ProtectionOp::GetActionsAccess),
495 Op::Protection(ProtectionOp::SetActionsAccess),
Merge branch 'worktree-agent-a3abfcce648e87dca'496 Op::Protection(ProtectionOp::CreateRepositoryDispatch),
497 Op::Protection(ProtectionOp::GetWorkspaceWorkflowPermissions),
498 Op::Protection(ProtectionOp::SetWorkspaceWorkflowPermissions),
499 ],
500 ),
501 (
The statement is a month at a time, a line per kind of charge502 "Secrets and variables",
503 "Values that workflows and deployments read, per repository or for a whole workspace, with a row per environment.",
504 &[
505 Op::ListActionsSecrets,
506 Op::SetActionsSecret,
507 Op::DeleteActionsSecret,
508 Op::ListActionsVariables,
509 Op::SetActionsVariable,
510 Op::DeleteActionsVariable,
511 ],
512 ),
513 (
Fast pages, required checks on the branch, self-hosted runners, honest incidents514 "Runners",
515 "Self-hosted runners: your own machines, which run your workflow jobs (and, if you choose, your agents' work) for $0 of g1t compute. They register with a short-lived token and only ever connect out.",
516 &[
517 Op::ListRunners,
518 Op::CreateRunnerRegistrationToken,
519 Op::RemoveRunner,
520 Op::ListRunnerGroups,
521 Op::CreateRunnerGroup,
522 Op::UpdateRunnerGroup,
523 Op::DeleteRunnerGroup,
524 Op::GetRunnerSettings,
525 Op::UpdateRunnerSettings,
526 ],
527 ),
528 (
Every agent can have its own computer. A session that needs one wakes it: a home of its own on g1t cloud, one per agent and never shared, where it runs commands, reads and writes files and keeps what it made, with each session working in its own folder under a shared home; after ten idle minutes it sleeps, its home kept as a snapshot and restored when it wakes, and Reset wipes the home while memory and artifacts stay. Its shell and files are abilities with the usual choices, Alone, Alone when asked, Ask first or Never, offered only inside sessions and never to a chat reply; every command shows on the session with its output, and the agent's new Computer tab shows the state, the disk used of the five gigabytes included, the recent commands, and Wake, Put to sleep and Reset. Machine time counts only while it is awake, on the sandbox lines of the ledger that name the agent and who asked, held to the same spend caps as the session; the disk itself costs nothing in this version. The runner gained a long-lived supervisor that answers the computer's requests inside the container, and the runner service a computer per agent that keeps its snapshot in the agent homes bucket when one is attached, and says so when none is. The REST API and the agent tool can read a computer, wake it, put it to sleep and reset it. The agents, abilities, sessions, runners, billing and deploy guides say how it works and what an operator sets up; pinning a computer to your own runner, its browser and take-over come next.529 "Agents' computers",
530 "Each workspace agent has a computer of its own on g1t cloud: a persistent home its sessions run commands on, which sleeps when idle and is saved as it was. Its state and recent commands, and waking, sleeping and resetting it.",
531 &[
532 Op::GetAgentComputer,
533 Op::WakeAgentComputer,
534 Op::SleepAgentComputer,
535 Op::ResetAgentComputer,
536 Op::ListAgentComputerCommands,
537 ],
538 ),
539 (
The statement is a month at a time, a line per kind of charge540 "Webhooks",
541 "Signed HTTPS requests sent to your own address as things happen, for a repository or a whole workspace.",
542 &[
543 Op::ListWebhooks,
544 Op::CreateWebhook,
545 Op::UpdateWebhook,
546 Op::DeleteWebhook,
547 Op::PingWebhook,
548 Op::ListWebhookDeliveries,
549 Op::RedeliverWebhook,
550 ],
551 ),
552 (
553 "Integrations",
554 "A workspace's connections to outside systems: model providers, alert sources and issue trackers.",
555 &[
556 Op::ListIntegrations,
557 Op::ConnectIntegration,
AI Gateway: OpenAI's format, open models, and your own providers558 Op::UpdateIntegration,
The statement is a month at a time, a line per kind of charge559 Op::DisconnectIntegration,
560 Op::TestIntegration,
561 Op::GetModelRoutes,
562 Op::SetModelRoutes,
563 Op::GetContext,
564 Op::ImportIssue,
565 ],
566 ),
Merge main into Artifacts Phase 2567 (
Merge branch 'customize' into main: Customize replaces the Marketplace, the Artifacts trash and spaces changes, the Security-free cleanup, with the desktop app's own title bar kept in the shell.568 "Customize",
569 "What shapes how a workspace's agents work, in the formats Claude Code reads: the marketplaces it reads plugins from, what Discover lists, the plugins it installed (their skills, subagents and remote MCP servers reach g1t's agents; the rest reaches Claude Code on a runner), and its connectors: MCP servers connected once, then given to every agent, a team or one agent. The bundle is what a job's Claude Code loads on a runner.",
570 &[
571 Op::Customize(CustomizeOp::ListMarketplaces),
572 Op::Customize(CustomizeOp::AddMarketplace),
573 Op::Customize(CustomizeOp::RemoveMarketplace),
574 Op::Customize(CustomizeOp::RefreshMarketplace),
575 Op::Customize(CustomizeOp::Discover),
576 Op::Customize(CustomizeOp::ListPlugins),
577 Op::Customize(CustomizeOp::GetPlugin),
578 Op::Customize(CustomizeOp::InstallPlugin),
579 Op::Customize(CustomizeOp::SetPluginEnabled),
580 Op::Customize(CustomizeOp::UninstallPlugin),
581 Op::Customize(CustomizeOp::UpdatePlugin),
582 Op::Customize(CustomizeOp::ListPluginAgents),
583 Op::Customize(CustomizeOp::ListConnectors),
584 Op::Customize(CustomizeOp::GetConnector),
585 Op::Customize(CustomizeOp::AddConnector),
586 Op::Customize(CustomizeOp::SetConnectorKey),
587 Op::Customize(CustomizeOp::RemoveConnector),
588 Op::Customize(CustomizeOp::RefreshConnector),
589 Op::Customize(CustomizeOp::GrantConnector),
590 Op::Customize(CustomizeOp::RevokeConnector),
591 Op::Customize(CustomizeOp::Bundle),
592 ],
593 ),
594 (
Merge main into Artifacts Phase 2595 "Artifacts",
596 "A workspace's docs, slides, designs and dashboards (Artifacts mode): listing and searching the ones you can open, reading and editing their content in Markdown, making, moving, trashing and restoring them, their versions, and who can open them. Each answers for your own role on it. Not workflow runs' artifacts, which are under Actions. Slides, designs and dashboards answer that they are not here yet.",
597 &[
598 Op::Folios(FoliosOp::List),
599 Op::Folios(FoliosOp::Search),
600 Op::Folios(FoliosOp::Get),
601 Op::Folios(FoliosOp::GetContent),
602 Op::Folios(FoliosOp::ListVersions),
603 Op::Folios(FoliosOp::GetAccess),
604 Op::Folios(FoliosOp::ListTemplates),
605 Op::Folios(FoliosOp::ListSpaces),
606 Op::Folios(FoliosOp::QueryDataset),
607 Op::Folios(FoliosOp::Create),
608 Op::Folios(FoliosOp::Update),
609 Op::Folios(FoliosOp::Edit),
610 Op::Folios(FoliosOp::Trash),
611 Op::Folios(FoliosOp::Restore),
612 Op::Folios(FoliosOp::RestoreVersion),
613 Op::Folios(FoliosOp::SetAccess),
614 Op::Folios(FoliosOp::Purge),
615 ],
616 ),
The statement is a month at a time, a line per kind of charge617];
618
619/// The section of the API reference an operation is listed under.
620fn tag(op: Op) -> &'static str {
621 SECTIONS
622 .iter()
623 .find(|(_, _, ops)| ops.contains(&op))
624 .map_or("Repositories", |(name, _, _)| name)
625}
626
627/// What an operation's page is called, as a short sentence.
628fn title(op: Op) -> &'static str {
629 match op {
630 Op::Whoami => "Get the current user",
Merge branch 'worktree-agent-ad7c6d88d93adc817'631 Op::GetWorkspace => "Get a workspace",
The statement is a month at a time, a line per kind of charge632 Op::CreateWorkspace => "Create a workspace",
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look633 Op::DeleteWorkspace => "Delete a workspace",
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily634 Op::UpdateWorkspace => "Update a workspace",
Merge main (membership, two-factor, GitHub repo roles) into tokens635 Op::ListMembers => "List a workspace's members",
636 Op::UpdateMember => "Change a member's role",
637 Op::RemoveMember => "Remove a member",
638 Op::TransferOwnership => "Transfer a workspace's ownership",
639 Op::LeaveWorkspace => "Leave a workspace",
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look640 Op::ListEmails => "List your email addresses",
641 Op::AddEmail => "Add an email address",
Merge email confirmation gate: a code and a link, nothing until confirmed (identity 0036)642 Op::ConfirmEmail => "Confirm an email address",
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look643 Op::RemoveEmail => "Remove an email address",
644 Op::UpdateEmailSettings => "Change your email settings",
645 Op::ListInvites => "List your invites",
646 Op::CreateInvite => "Create an invite",
647 Op::RevokeInvite => "Revoke an invite",
648 Op::ListWorkspaceInvites => "List a workspace's invites",
649 Op::InviteMember => "Invite someone to a workspace",
Merge workspace invitations: nobody joins a workspace without saying yes, people are found by username, your own invites can bring someone in, and nobody is left without a workspace (identity 0040)650 Op::ListInvitations => "List your workspace invitations",
651 Op::AcceptInvitation => "Accept a workspace invitation",
652 Op::DeclineInvitation => "Decline a workspace invitation",
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look653 Op::RevokeWorkspaceInvite => "Revoke a workspace's invite",
654 Op::TransferRepo => "Transfer a repository",
655 Op::RenameRepo => "Rename a repository",
656 Op::RenameBranch => "Rename a branch",
657 Op::ArchiveRepo => "Archive a repository",
658 Op::UnarchiveRepo => "Unarchive a repository",
659 Op::SetRepoVisibility => "Change a repository's visibility",
660 Op::DeleteRepo => "Delete a repository",
661 Op::ListDeletedRepos => "List recently deleted repositories",
662 Op::RestoreRepo => "Restore a deleted repository",
663 Op::PurgeRepo => "Purge a deleted repository",
The statement is a month at a time, a line per kind of charge664 Op::ListRepos => "List repositories",
665 Op::GetRepo => "Get a repository",
666 Op::CreateRepo => "Create a repository",
667 Op::UpdateRepo => "Update a repository",
668 Op::GetRepoSettings => "Get repository settings",
669 Op::UpdateRepoSettings => "Update repository settings",
Fast pages, required checks on the branch, self-hosted runners, honest incidents670 Op::ListCheckNames => "List check names",
The statement is a month at a time, a line per kind of charge671 Op::GetMergeQueue => "Get the merge queue",
672 Op::MessageAgent => "Message an agent",
673 Op::AnswerMessage => "Answer a message",
674 Op::TakeMessages => "Take new messages",
675 Op::Remember => "Remember something",
676 Op::Recall => "Recall memory",
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API677 Op::SearchContext => "Search the context hub",
678 Op::GetEntity => "Get a catalog entry",
Search across all of g1t, Explore, and a command palette679 Op::Search => "Search g1t",
The statement is a month at a time, a line per kind of charge680 Op::ListIssues => "List issues",
681 Op::GetIssue => "Get an issue",
682 Op::CreateIssue => "Create an issue",
683 Op::UpdateIssue => "Update an issue",
684 Op::CloseIssue => "Close an issue",
685 Op::ReopenIssue => "Reopen an issue",
g1t is one name: its agent's work, commits and comments show as @g1t, and nobody can claim g1t or g1t-agent686 Op::AssignIssue => "Assign an issue to g1t",
Thirteen MCP tools and classic token scopes; agents rate their confidence and can be put on an issue in one step687 Op::Delegate => "Put an agent on it",
The statement is a month at a time, a line per kind of charge688 Op::PlanWork => "Plan work",
689 Op::GetPlan => "Get a plan",
690 Op::ApplyPlan => "Apply a plan",
691 Op::ListLabels => "List labels",
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar692 Op::CreateLabel => "Create a label",
693 Op::UpdateLabel => "Update a label",
694 Op::DeleteLabel => "Delete a label",
695 Op::AddDefaultLabels => "Add the default labels",
696 Op::ListIssueLabels => "List an issue's labels",
697 Op::AddIssueLabels => "Add labels to an issue",
698 Op::SetIssueLabels => "Set an issue's labels",
699 Op::RemoveIssueLabels => "Remove labels from an issue",
700 Op::ListMilestones => "List milestones",
701 Op::GetMilestone => "Get a milestone",
702 Op::CreateMilestone => "Create a milestone",
703 Op::UpdateMilestone => "Update a milestone",
704 Op::DeleteMilestone => "Delete a milestone",
705 Op::UpdatePullRequest => "Update a pull request",
The statement is a month at a time, a line per kind of charge706 Op::AddComment => "Add a comment",
Merge Actions: cross-repo workflows and actions, release and deployment triggers, step timeouts707 Op::EditComment => "Edit a comment",
708 Op::DeleteComment => "Delete a comment",
The statement is a month at a time, a line per kind of charge709 Op::ReviewPullRequest => "Review a pull request",
710 Op::ListPullRequests => "List pull requests",
711 Op::GetPullRequest => "Get a pull request",
712 Op::CreatePullRequest => "Create a pull request",
713 Op::RecordSession => "Record session entries",
714 Op::ReadSession => "Read a session",
715 Op::MarkPullRequestReady => "Mark a pull request ready",
716 Op::ClosePullRequest => "Close a pull request",
Merge Actions: cross-repo workflows and actions, release and deployment triggers, step timeouts717 Op::ReopenPullRequest => "Reopen a pull request",
718 Op::ConvertPullRequestToDraft => "Convert a pull request to a draft",
The statement is a month at a time, a line per kind of charge719 Op::GetPullRequestChanges => "Get a pull request's changes",
720 Op::MergePullRequest => "Merge a pull request",
721 Op::ListEvents => "List repository events",
722 Op::ListIntegrations => "List integrations",
723 Op::ConnectIntegration => "Connect an integration",
AI Gateway: OpenAI's format, open models, and your own providers724 Op::UpdateIntegration => "Update an integration",
The statement is a month at a time, a line per kind of charge725 Op::DisconnectIntegration => "Disconnect an integration",
726 Op::TestIntegration => "Test an integration",
727 Op::GetContext => "Look up a ticket",
728 Op::ImportIssue => "Import an issue",
729 Op::GetModelRoutes => "Get model routes",
730 Op::SetModelRoutes => "Set model routes",
731 Op::ListWebhooks => "List webhooks",
732 Op::CreateWebhook => "Create a webhook",
733 Op::UpdateWebhook => "Update a webhook",
734 Op::DeleteWebhook => "Delete a webhook",
735 Op::PingWebhook => "Ping a webhook",
736 Op::ListWebhookDeliveries => "List webhook deliveries",
737 Op::RedeliverWebhook => "Redeliver a webhook delivery",
738 Op::ListWorkflows => "List workflows",
739 Op::ListWorkflowRuns => "List workflow runs",
740 Op::GetWorkflowRun => "Get a workflow run",
741 Op::GetJobLogs => "Get a job's log",
742 Op::DispatchWorkflow => "Run a workflow",
743 Op::CancelWorkflowRun => "Cancel a workflow run",
744 Op::RerunWorkflowRun => "Re-run a workflow run",
745 Op::UpdateWorkflow => "Turn a workflow on or off",
746 Op::ListActionsSecrets => "List secrets",
747 Op::SetActionsSecret => "Set a secret",
748 Op::DeleteActionsSecret => "Delete a secret",
749 Op::ListActionsVariables => "List variables",
750 Op::SetActionsVariable => "Set a variable",
751 Op::DeleteActionsVariable => "Delete a variable",
Fast pages, required checks on the branch, self-hosted runners, honest incidents752 Op::ListRunners => "List self-hosted runners",
753 Op::ListRunnerGroups => "List runner groups",
Every agent can have its own computer. A session that needs one wakes it: a home of its own on g1t cloud, one per agent and never shared, where it runs commands, reads and writes files and keeps what it made, with each session working in its own folder under a shared home; after ten idle minutes it sleeps, its home kept as a snapshot and restored when it wakes, and Reset wipes the home while memory and artifacts stay. Its shell and files are abilities with the usual choices, Alone, Alone when asked, Ask first or Never, offered only inside sessions and never to a chat reply; every command shows on the session with its output, and the agent's new Computer tab shows the state, the disk used of the five gigabytes included, the recent commands, and Wake, Put to sleep and Reset. Machine time counts only while it is awake, on the sandbox lines of the ledger that name the agent and who asked, held to the same spend caps as the session; the disk itself costs nothing in this version. The runner gained a long-lived supervisor that answers the computer's requests inside the container, and the runner service a computer per agent that keeps its snapshot in the agent homes bucket when one is attached, and says so when none is. The REST API and the agent tool can read a computer, wake it, put it to sleep and reset it. The agents, abilities, sessions, runners, billing and deploy guides say how it works and what an operator sets up; pinning a computer to your own runner, its browser and take-over come next.754 Op::GetAgentComputer => "Get an agent's computer",
755 Op::WakeAgentComputer => "Wake an agent's computer",
756 Op::SleepAgentComputer => "Put an agent's computer to sleep",
757 Op::ResetAgentComputer => "Reset an agent's computer",
758 Op::ListAgentComputerCommands => "List an agent's computer's commands",
Fast pages, required checks on the branch, self-hosted runners, honest incidents759 Op::GetRunnerSettings => "Get runner settings",
760 Op::CreateRunnerRegistrationToken => "Create a runner registration token",
761 Op::RemoveRunner => "Remove a self-hosted runner",
762 Op::CreateRunnerGroup => "Create a runner group",
763 Op::UpdateRunnerGroup => "Change a runner group",
764 Op::DeleteRunnerGroup => "Delete a runner group",
765 Op::UpdateRunnerSettings => "Change runner settings",
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look766 Op::ListCollaborators => "List who has access",
767 Op::AddCollaborator => "Add a collaborator",
768 Op::UpdateCollaborator => "Change a collaborator's role",
769 Op::RemoveCollaborator => "Remove a collaborator",
770 Op::GetCollaboratorPermission => "Get someone's permission",
771 Op::ListRepoInvitations => "List a repository's invitations",
772 Op::RevokeRepoInvitation => "Revoke a repository invitation",
773 Op::ListMyRepoInvitations => "List your repository invitations",
774 Op::AcceptRepoInvitation => "Accept a repository invitation",
775 Op::DeclineRepoInvitation => "Decline a repository invitation",
776 Op::SetBasePermission => "Set the base permission",
777 Op::ListOutsideCollaborators => "List outside collaborators",
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily778 Op::ListSecurityAlerts => "List security alerts",
779 Op::DismissSecurityAlert => "Dismiss a security alert",
780 Op::ReopenSecurityAlert => "Reopen a security alert",
API: notifications over REST and MCP, with notifications scopes781 Op::ListNotifications => "List notifications",
782 Op::MarkNotificationsRead => "Mark notifications read",
783 Op::GetNotificationThread => "Get a thread",
784 Op::MarkThreadRead => "Mark a thread read",
785 Op::MarkThreadDone => "Mark a thread done",
786 Op::SaveThread => "Save a thread",
787 Op::SnoozeThread => "Snooze a thread",
788 Op::GetThreadSubscription => "Get a thread subscription",
789 Op::SetThreadSubscription => "Set a thread subscription",
790 Op::DeleteThreadSubscription => "Unsubscribe from a thread",
791 Op::GetRepoSubscription => "Get how you watch a repository",
792 Op::SetRepoSubscription => "Watch a repository",
793 Op::DeleteRepoSubscription => "Stop watching a repository",
794 Op::ListWatchedRepos => "List repositories you watch",
API: pinned projects over REST and MCP795 Op::ListPinnedProjects => "List your pinned projects",
Usage, Billing settings and prepaid AI credit; fixes from the UX audit796 Op::GetUsage => "Get a workspace's usage",
797 Op::GetBudget => "Get a workspace's budget",
798 Op::SetBudget => "Change a workspace's budget",
799 Op::GetAiCredit => "Get a workspace's AI credit",
800 Op::BuyAiCredit => "Buy AI credit",
801 Op::ListInvoices => "List a workspace's invoices",
802 Op::GetBillingDetails => "Get a workspace's billing details",
Merge the AI Gateway: Anthropic's Messages API on a workspace's tokens803 Op::ListGatewayRequests => "List a workspace's AI Gateway requests",
API: pinned projects over REST and MCP804 Op::PinProject => "Pin a project",
805 Op::UnpinProject => "Unpin a project",
806 Op::ReorderPinnedProjects => "Reorder your pinned projects",
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97807 Op::ListProjects => "List a workspace's projects",
808 Op::GetProject => "Get a project",
809 Op::UpdateProject => "Update a project",
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar810 Op::ListTeams => "List teams",
811 Op::GetTeam => "Get a team",
812 Op::CreateTeam => "Create a team",
813 Op::UpdateTeam => "Update a team",
814 Op::DeleteTeam => "Delete a team",
815 Op::ListTeamMembers => "List a team's members",
816 Op::SetTeamMember => "Add or change a team member",
817 Op::RemoveTeamMember => "Remove a team member",
818 Op::ListChildTeams => "List child teams",
819 Op::ListTeamRepos => "List a team's repositories",
820 Op::SetTeamRepo => "Give a team a role on a repository",
821 Op::RemoveTeamRepo => "Remove a team from a repository",
822 Op::SetTeamReviewAssignment => "Set a team's review assignment",
823 Op::ListUserTeams => "List someone's teams",
824 Op::RequestReviewers => "Request reviewers",
825 Op::RemoveRequestedReviewers => "Remove requested reviewers",
826 Op::GetCodeownersErrors => "List CODEOWNERS errors",
827 Op::Security(op) => op.title(),
Merge rulesets: branch and tag rules, agent-first, enforced on push and merge828 Op::Rules(op) => op.title(),
Merge checks: statuses and check runs on every commit829 Op::Checks(op) => op.title(),
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97830 Op::About(op) => op.title(),
831 Op::Deployments(op) => op.title(),
Merge branch 'worktree-agent-a3abfcce648e87dca'832 Op::Protection(op) => op.title(),
API and MCP for a workspace's personal access token rules, members' tokens and approvals833 Op::Tokens(op) => op.title(),
Actions: OIDC tokens, the toolkit's cache and artifact services, and artifacts in R2834 Op::Artifacts(op) => op.title(),
Merge branch 'worktree-agent-a5a5de74d8863d554' into worktree-agent-a16631325aecf58ca835 Op::DeployKeys(op) => op.title(),
Merge branch 'mirroring' into artifacts-mode836 Op::Mirrors(op) => op.title(),
Merge packages: roles, Actions access, source label, soft delete, API837 Op::Packages(op) => op.title(),
Merge main into Artifacts Phase 2838 Op::Folios(op) => op.title(),
Merge branch 'customize' into main: Customize replaces the Marketplace, the Artifacts trash and spaces changes, the Security-free cleanup, with the desktop app's own title bar kept in the shell.839 Op::Customize(op) => op.title(),
The statement is a month at a time, a line per kind of charge840 }
841}
842
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look843/// Why an operation can be refused with `402 payment_required`, if it
844/// can: the ones that start an agent, when the workspace has no credit,
845/// and the ones that make a repository private in a workspace, when a free
846/// workspace's private storage has no room for it.
847fn may_need_payment(op: Op) -> Option<&'static str> {
848 match op {
849 Op::AssignIssue | Op::PlanWork | Op::ApplyPlan => Some("The workspace has no agent credit."),
Every agent can have its own computer. A session that needs one wakes it: a home of its own on g1t cloud, one per agent and never shared, where it runs commands, reads and writes files and keeps what it made, with each session working in its own folder under a shared home; after ten idle minutes it sleeps, its home kept as a snapshot and restored when it wakes, and Reset wipes the home while memory and artifacts stay. Its shell and files are abilities with the usual choices, Alone, Alone when asked, Ask first or Never, offered only inside sessions and never to a chat reply; every command shows on the session with its output, and the agent's new Computer tab shows the state, the disk used of the five gigabytes included, the recent commands, and Wake, Put to sleep and Reset. Machine time counts only while it is awake, on the sandbox lines of the ledger that name the agent and who asked, held to the same spend caps as the session; the disk itself costs nothing in this version. The runner gained a long-lived supervisor that answers the computer's requests inside the container, and the runner service a computer per agent that keeps its snapshot in the agent homes bucket when one is attached, and says so when none is. The REST API and the agent tool can read a computer, wake it, put it to sleep and reset it. The agents, abilities, sessions, runners, billing and deploy guides say how it works and what an operator sets up; pinning a computer to your own runner, its browser and take-over come next.850 Op::WakeAgentComputer => Some("The workspace's plan refuses compute: it is paused, or over its limit."),
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look851 Op::UpdateRepo | Op::SetRepoVisibility | Op::TransferRepo => Some(
852 "A free workspace's private storage has no room for this private repository.",
853 ),
854 _ => None,
855 }
The statement is a month at a time, a line per kind of charge856}
857
858/// What the reference says beyond each operation's own description, keyed
859/// by operation id, written by hand from what the services return: `notes`
860/// (Markdown, added to the description) and example `params` (path),
861/// `query`, `request` (body) and `response`.
862const REFERENCE: &str = include_str!("reference.json");
863
864fn examples() -> Map<String, Value> {
865 match serde_json::from_str(REFERENCE) {
866 Ok(Value::Object(examples)) => examples,
867 _ => Map::new(),
868 }
869}
870
871/// `/repos/:owner/:name` as OpenAPI writes it: `/repos/{owner}/{name}`.
872fn openapi_path(route: &Route) -> String {
873 route
874 .path
875 .split('/')
876 .map(|segment| match segment.strip_prefix(':') {
877 Some(name) => format!("{{{name}}}"),
878 None => segment.to_owned(),
879 })
880 .collect::<Vec<_>>()
881 .join("/")
882}
883
884fn error_response(description: &str) -> Value {
885 json!({
886 "description": description,
887 "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } },
888 })
889}
890
891/// A parameter in the path or the query, described by the operation's
892/// input schema where it has the same name.
893fn parameter(name: &str, place: &str, required: bool, schema: Option<&Value>) -> Value {
894 let mut schema = schema.cloned().unwrap_or_else(|| json!({ "type": "string" }));
895 let description = match name {
896 "owner" => Some(Value::from("The workspace that owns the repository.")),
897 "name" => Some(Value::from("The repository's name.")),
898 _ => schema.as_object_mut().and_then(|schema| schema.remove("description")),
899 };
900 let mut parameter = json!({
901 "name": name,
902 "in": place,
903 "required": required,
904 "schema": schema,
905 });
906 if let Some(description) = description {
907 parameter["description"] = description;
908 }
909 parameter
910}
911
912/// The operation id of a route. An operation reached at a workspace's
913/// address as well as a repository's is documented once for each, with its
914/// own id; GitHub's alternative addresses for one operation keep GitHub's
915/// names.
916fn operation_id(route: &Route) -> String {
917 let op = route.op;
918 let base = match (route.method, route.path.rsplit('/').next().unwrap_or_default()) {
919 ("PUT", "enable") => "enable_workflow".to_owned(),
920 ("PUT", "disable") => "disable_workflow".to_owned(),
921 ("POST", "rerun-failed-jobs") => "rerun_failed_jobs".to_owned(),
Merge Actions runs: summaries, attempts and re-runs, graceful cancel, log downloads, badges (actions 0009)922 ("POST", "rerun") if route.path.contains("/jobs/:job/") => "rerun_job".to_owned(),
923 ("POST", "force-cancel") => "force_cancel_workflow_run".to_owned(),
924 ("GET", ":attempt") => "get_workflow_run_attempt".to_owned(),
The statement is a month at a time, a line per kind of charge925 ("PATCH", ":setting") => "update_actions_variable".to_owned(),
926 ("GET", "runs") if route.path.contains("/workflows/:workflow/") => "list_runs_of_workflow".to_owned(),
API: notifications over REST and MCP, with notifications scopes927 // One repository's notifications, and an issue's subscription by
928 // its number rather than a thread's id.
929 (_, "notifications") if route.path.starts_with("/repos/") => match op {
930 Op::ListNotifications => "list_repo_notifications".to_owned(),
931 _ => "mark_repo_notifications_read".to_owned(),
932 },
933 (method, "subscription") if route.path.contains("/issues/:number/") => match method {
934 "GET" => "get_issue_subscription".to_owned(),
935 "PUT" => "set_issue_subscription".to_owned(),
936 _ => "delete_issue_subscription".to_owned(),
937 },
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar938 // One label off an issue, by its name in the path.
939 ("DELETE", ":label") if route.path.contains("/issues/:number/") => "remove_issue_label".to_owned(),
API: notifications over REST and MCP, with notifications scopes940 ("DELETE", "saved") => "unsave_thread".to_owned(),
941 ("DELETE", "snooze") => "unsnooze_thread".to_owned(),
The statement is a month at a time, a line per kind of charge942 _ => op.name().to_owned(),
943 };
944 if route.path.starts_with("/workspaces/") && ROUTES.iter().any(|other| other.op == op && other.path.starts_with("/repos/")) {
945 format!("{base}_for_workspace")
946 } else {
947 base
948 }
949}
950
951/// The summary of a route: its operation's title, or for one of GitHub's
952/// alternative addresses, what that address does.
953fn summary(route: &Route, id: &str) -> String {
954 let base = match id.trim_end_matches("_for_workspace") {
955 "enable_workflow" => "Turn a workflow on",
956 "disable_workflow" => "Turn a workflow off",
957 "rerun_failed_jobs" => "Re-run failed jobs",
Merge Actions runs: summaries, attempts and re-runs, graceful cancel, log downloads, badges (actions 0009)958 "rerun_job" => "Re-run a job",
959 "force_cancel_workflow_run" => "Force-cancel a workflow run",
960 "get_workflow_run_attempt" => "Get a workflow run attempt",
The statement is a month at a time, a line per kind of charge961 "update_actions_variable" => "Update a variable",
962 "list_runs_of_workflow" => "List a workflow's runs",
API: notifications over REST and MCP, with notifications scopes963 "list_repo_notifications" => "List a repository's notifications",
964 "mark_repo_notifications_read" => "Mark a repository's notifications read",
965 "get_issue_subscription" => "Get your subscription to an issue",
966 "set_issue_subscription" => "Subscribe to an issue",
967 "delete_issue_subscription" => "Unsubscribe from an issue",
968 "unsave_thread" => "Unsave a thread",
969 "unsnooze_thread" => "Bring a snoozed thread back",
The statement is a month at a time, a line per kind of charge970 _ => title(route.op),
971 };
972 if id.ends_with("_for_workspace") {
973 format!("{base} for a workspace")
974 } else {
975 base.to_owned()
976 }
977}
978
979fn operation(route: &Route) -> Value {
980 let op = route.op;
981 let path_params: Vec<&str> = route.params().collect();
Merge checks: statuses and check runs on every commit982 // `owner` and `name` in the path stand for the operation's `repo` input,
983 // so a `name` in the body, such as a check run's, is the body's own.
984 let stands_for_repo =
985 |name: &str| matches!(name, "owner" | "name") && path_params.contains(&"owner") && path_params.contains(&"name");
986 let covered = |name: &str| name == "repo" || (path_params.contains(&name) && !stands_for_repo(name));
The statement is a month at a time, a line per kind of charge987 let all_properties = op.properties();
988 let mut properties = all_properties.clone();
989 properties.retain(|name, _| !covered(name));
990 let required: Vec<String> = op
991 .required()
992 .into_iter()
993 .filter(|name| !covered(name))
994 .collect();
995
996 let mut parameters: Vec<Value> = path_params
997 .iter()
Merge checks: statuses and check runs on every commit998 .map(|name| parameter(name, "path", true, if stands_for_repo(name) { None } else { all_properties.get(*name) }))
The statement is a month at a time, a line per kind of charge999 .collect();
1000 let mut body = Value::Null;
1001 if route.method == "GET" {
1002 for (name, key) in route.query {
1003 parameters.push(parameter(
1004 name,
1005 "query",
1006 required.iter().any(|required| required == key),
1007 properties.get(*key),
1008 ));
1009 }
1010 } else if !properties.is_empty() {
1011 let mut schema = json!({ "type": "object", "properties": properties });
1012 if !required.is_empty() {
1013 schema["required"] = json!(required);
1014 }
1015 body = json!({
1016 "required": !required.is_empty(),
1017 "content": { "application/json": { "schema": schema } },
1018 });
1019 }
1020
1021 let id = operation_id(route);
1022 let mut responses = Map::new();
Merge Actions: cross-repo workflows and actions, release and deployment triggers, step timeouts1023 if route.no_content() {
1024 responses.insert("204".into(), json!({ "description": "Success. There is no body." }));
1025 } else {
1026 responses.insert(
1027 "200".into(),
1028 json!({
1029 "description": "Success.",
1030 "content": { "application/json": { "schema": {} } },
1031 }),
1032 );
1033 }
The statement is a month at a time, a line per kind of charge1034 responses.insert(
1035 "401".into(),
1036 error_response("A token is required, or the one sent is not valid."),
1037 );
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look1038 if let Some(reason) = may_need_payment(op) {
1039 responses.insert("402".into(), error_response(reason));
The statement is a month at a time, a line per kind of charge1040 }
Thirteen MCP tools and classic token scopes; agents rate their confidence and can be put on an issue in one step1041 responses.insert(
1042 "403".into(),
1043 error_response("Signed in, but not allowed to do this: the role you have is not enough, or the token lacks the scope it needs, which `needed_scope` names."),
1044 );
Search across all of g1t, Explore, and a command palette1045 if !matches!(op, Op::Whoami | Op::ListRepos | Op::Search) {
The statement is a month at a time, a line per kind of charge1046 responses.insert("404".into(), error_response("It does not exist, or you cannot see it."));
1047 }
1048 if route.method != "GET" {
1049 responses.insert(
1050 "409".into(),
1051 error_response("The request conflicts with the current state."),
1052 );
1053 }
1054 if op != Op::Whoami {
1055 responses.insert("422".into(), error_response("The input is not valid."));
1056 }
1057 // Public data can be read without a token; everything else needs one.
Thirteen MCP tools and classic token scopes; agents rate their confidence and can be put on an issue in one step1058 let scope: Vec<&str> = scope_for(op.name()).map(|scope| scope.as_str()).into_iter().collect();
The statement is a month at a time, a line per kind of charge1059 let security = if op.needs_user() {
Thirteen MCP tools and classic token scopes; agents rate their confidence and can be put on an issue in one step1060 json!([{ "token": scope }])
The statement is a month at a time, a line per kind of charge1061 } else {
Thirteen MCP tools and classic token scopes; agents rate their confidence and can be put on an issue in one step1062 json!([{ "token": scope }, {}])
The statement is a month at a time, a line per kind of charge1063 };
Thirteen MCP tools and classic token scopes; agents rate their confidence and can be put on an issue in one step1064 let (tool, action) = crate::tools::TOOLS
1065 .iter()
1066 .find_map(|tool| {
1067 tool.actions
1068 .iter()
1069 .find(|action| action.op == op)
1070 .map(|action| (tool.name, action.name))
1071 })
1072 .unwrap_or_default();
The statement is a month at a time, a line per kind of charge1073 let mut described = json!({
1074 "operationId": id,
1075 "tags": [tag(op)],
1076 "summary": summary(route, &id),
1077 "description": op.description(),
Thirteen MCP tools and classic token scopes; agents rate their confidence and can be put on an issue in one step1078 "x-operation": op.name(),
1079 "x-mcp-tool": tool,
1080 "x-mcp-action": action,
1081 "x-scope": scope.first().copied(),
The statement is a month at a time, a line per kind of charge1082 "security": security,
1083 "parameters": parameters,
1084 "responses": responses,
1085 });
1086 if !body.is_null() {
1087 described["requestBody"] = body;
1088 }
1089 described
1090}
1091
1092/// Entries for device sign-in, which is not an operation.
1093fn onboarding() -> Map<String, Value> {
1094 let paths = json!({
1095 "/device/code": {
1096 "post": {
1097 "operationId": "device_code",
1098 "tags": ["Accounts"],
1099 "summary": "Start signing in",
1100 "description": "Begins a device sign-in. Show the person `verification_uri_complete` and have them open it in a browser, where they sign in or register and approve the code. Then poll `/device/token`.",
1101 "security": [],
1102 "requestBody": {
1103 "content": { "application/json": { "schema": {
1104 "type": "object",
1105 "properties": {
1106 "client_name": {
1107 "type": "string",
1108 "description": "What is asking, shown to the person approving. For example, Claude Code.",
1109 },
1110 },
1111 } } },
1112 },
1113 "responses": { "200": {
1114 "description": "The codes for this sign-in.",
1115 "content": { "application/json": { "schema": {
1116 "type": "object",
1117 "properties": {
1118 "device_code": { "type": "string", "description": "Secret. Send it to /device/token." },
1119 "user_code": { "type": "string", "description": "Shown to the person, like WDJB-MJHT." },
1120 "verification_uri": { "type": "string" },
1121 "verification_uri_complete": {
1122 "type": "string",
1123 "description": "The link to give the person; it carries the code.",
1124 },
1125 "expires_in": { "type": "integer", "description": "Seconds until the codes expire." },
1126 "interval": { "type": "integer", "description": "Seconds to wait between polls." },
1127 },
1128 } } },
1129 } },
1130 },
1131 },
1132 "/device/token": {
1133 "post": {
1134 "operationId": "device_token",
1135 "tags": ["Accounts"],
1136 "summary": "Finish signing in",
1137 "description": "Asks whether the person has approved. Poll no faster than the interval. The token is returned once.",
1138 "security": [],
1139 "requestBody": {
1140 "required": true,
1141 "content": { "application/json": { "schema": {
1142 "type": "object",
1143 "required": ["device_code"],
1144 "properties": { "device_code": { "type": "string" } },
1145 } } },
1146 },
1147 "responses": { "200": {
1148 "description": "The state of the sign-in.",
1149 "content": { "application/json": { "schema": {
1150 "type": "object",
1151 "required": ["status"],
1152 "properties": {
1153 "status": { "type": "string", "enum": ["pending", "approved", "denied", "expired"] },
1154 "token": { "type": "string", "description": "Present when approved." },
Cards you act on in chat; agents comment and review as themselves; names shown cleanly; commits on the calendar1155 "username": { "type": "string", "description": "Lowercased: what the account is found and linked by." },
1156 "display_username": { "type": "string", "description": "The username as its owner wrote it; the same as username when they chose no case." },
The statement is a month at a time, a line per kind of charge1157 "verified": {
1158 "type": "boolean",
1159 "description": "Whether the account's email is confirmed.",
1160 },
1161 },
1162 } } },
1163 } },
1164 },
1165 },
1166 });
1167 match paths {
1168 Value::Object(paths) => paths,
1169 _ => Map::new(),
1170 }
1171}
1172
1173
1174/// Puts each operation's examples, where it has them, into its request
1175/// and response. Path and query values go under `x-example-params` and
1176/// `x-example-query`, which tools that build a request can use.
1177fn attach_examples(paths: &mut Map<String, Value>) {
1178 let examples = examples();
1179 for methods in paths.values_mut() {
1180 let Some(methods) = methods.as_object_mut() else { continue };
1181 for operation in methods.values_mut() {
1182 let id = operation["operationId"].as_str().unwrap_or_default().to_owned();
Thirteen MCP tools and classic token scopes; agents rate their confidence and can be put on an issue in one step1183 let name = operation["x-operation"].as_str().unwrap_or_default().to_owned();
1184 let Some(example) = examples.get(&id).or_else(|| examples.get(&name)) else {
The statement is a month at a time, a line per kind of charge1185 continue;
1186 };
1187 if let Some(notes) = example.get("notes").and_then(Value::as_str) {
1188 let description = operation["description"].as_str().unwrap_or_default();
1189 operation["description"] = json!(format!("{description}\n\n{notes}"));
1190 }
1191 if let Some(response) = example.get("response") {
1192 let content = &mut operation["responses"]["200"]["content"]["application/json"];
1193 if content.is_object() {
1194 content["example"] = response.clone();
1195 }
1196 }
1197 if let Some(request) = example.get("request") {
1198 let content = &mut operation["requestBody"]["content"]["application/json"];
1199 if content.is_object() {
1200 content["example"] = request.clone();
1201 }
1202 }
1203 for (key, extension) in [("params", "x-example-params"), ("query", "x-example-query")] {
1204 if let Some(values) = example.get(key) {
1205 operation[extension] = values.clone();
1206 }
1207 }
1208 }
1209 }
1210}
1211
1212pub fn document() -> Value {
1213 let mut paths = onboarding();
1214 for route in ROUTES {
1215 let entry = paths
1216 .entry(openapi_path(route))
1217 .or_insert_with(|| json!({}));
1218 entry[route.method.to_lowercase()] = operation(route);
1219 }
1220 attach_examples(&mut paths);
1221 let tags: Vec<Value> = SECTIONS
1222 .iter()
1223 .map(|(name, description, ops)| {
1224 json!({
1225 "name": name,
1226 "description": description,
1227 // The section's operations in reading order, by MCP tool name.
1228 "x-tools": ops.iter().map(|op| op.name()).collect::<Vec<_>>(),
1229 })
1230 })
1231 .collect();
1232 let codes = ["unauthenticated", "payment_required", "forbidden", "not_found", "conflict", "invalid"];
1233 json!({
1234 "openapi": "3.1.0",
1235 "info": {
1236 "title": "g1t API",
1237 "version": "1",
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1238 "description": "The REST API for g1t, a git forge built for agents. The same operations are available to agents as MCP tools at https://mcp.g1t.sh. Every name in a request or response body is `snake_case`; names you chose, such as a workflow's inputs or a secret's name, are returned as you wrote them.",
The statement is a month at a time, a line per kind of charge1239 "license": { "name": "MIT", "identifier": "MIT" },
1240 },
1241 "servers": [{ "url": "https://api.g1t.sh" }],
1242 "security": [{ "token": [] }, {}],
1243 "tags": tags,
1244 "paths": paths,
1245 "components": {
1246 "securitySchemes": {
1247 "token": {
1248 "type": "http",
1249 "scheme": "bearer",
Thirteen MCP tools and classic token scopes; agents rate their confidence and can be put on an issue in one step1250 "description": "An access token, `g1t_…`. Public data needs none. Each operation names the scope a token needs for it; see https://docs.g1t.sh/guides/authentication/#scopes.",
The statement is a month at a time, a line per kind of charge1251 },
1252 },
1253 "schemas": {
1254 "Error": {
1255 "type": "object",
1256 "required": ["error"],
1257 "properties": {
1258 "error": {
1259 "type": "object",
1260 "required": ["code", "message"],
1261 "properties": {
1262 "code": { "type": "string", "enum": codes },
1263 "message": { "type": "string" },
Thirteen MCP tools and classic token scopes; agents rate their confidence and can be put on an issue in one step1264 "needed_scope": {
1265 "type": "string",
1266 "description": "On a 403 for an access token without the scope the call needs: that scope, such as `issues:write`.",
1267 },
The statement is a month at a time, a line per kind of charge1268 },
1269 },
1270 },
1271 },
1272 },
1273 },
1274 })
1275}
1276
1277#[cfg(test)]
1278mod tests {
1279 use super::*;
1280
1281 #[test]
1282 fn every_route_is_documented_once() {
1283 let document = document();
1284 let mut ids = Vec::new();
1285 for (_, methods) in document["paths"].as_object().unwrap() {
1286 for (_, operation) in methods.as_object().unwrap() {
1287 ids.push(operation["operationId"].as_str().unwrap().to_owned());
1288 }
1289 }
1290 for op in Op::ALL {
1291 assert_eq!(
1292 ids.iter().filter(|id| *id == op.name()).count(),
1293 1,
1294 "{}",
1295 op.name()
1296 );
1297 }
1298 let mut unique = ids.clone();
1299 unique.sort();
1300 unique.dedup();
1301 assert_eq!(unique.len(), ids.len(), "operation ids repeat");
1302 }
1303
1304 #[test]
1305 fn path_and_query_inputs_are_not_repeated_in_the_body() {
1306 let document = document();
1307 let merge = &document["paths"]["/repos/{owner}/{name}/pulls/{number}/merge"]["post"];
1308 let body = &merge["requestBody"]["content"]["application/json"]["schema"]["properties"];
1309 assert!(body.get("keep_issue_open").is_some());
1310 assert!(body.get("repo").is_none() && body.get("number").is_none());
1311 let list = &document["paths"]["/repos"]["get"];
1312 assert_eq!(list["parameters"][0]["name"], "q");
1313 assert!(list.get("requestBody").is_none());
1314 }
1315
1316 #[test]
1317 fn every_operation_is_in_one_section() {
1318 for op in Op::ALL {
1319 let sections = SECTIONS
1320 .iter()
1321 .filter(|(_, _, ops)| ops.contains(&op))
1322 .count();
1323 assert_eq!(sections, 1, "{}", op.name());
1324 }
1325 }
1326
1327 #[test]
1328 fn titles_read_as_sentences() {
1329 assert_eq!(title(Op::CreateIssue), "Create an issue");
1330 assert_eq!(title(Op::Whoami), "Get the current user");
1331 }
1332
1333 #[test]
1334 fn every_operation_has_an_example_response() {
1335 let examples = examples();
1336 assert!(!examples.is_empty(), "reference.json does not parse");
1337 let document = document();
1338 let mut known = Vec::new();
1339 for (path, methods) in document["paths"].as_object().unwrap() {
1340 for (method, operation) in methods.as_object().unwrap() {
1341 known.push(operation["operationId"].as_str().unwrap().to_owned());
Merge Actions: cross-repo workflows and actions, release and deployment triggers, step timeouts1342 // Nothing to show for a success without a body.
1343 if operation["responses"]["204"].is_object() {
1344 continue;
1345 }
The statement is a month at a time, a line per kind of charge1346 let example = &operation["responses"]["200"]["content"]["application/json"]["example"];
1347 assert!(!example.is_null(), "{method} {path} has no example response");
1348 }
1349 }
1350 for id in examples.keys() {
1351 assert!(known.contains(id), "reference.json names {id}, which is not an operation");
1352 }
1353 }
1354
1355 #[test]
1356 fn example_requests_send_only_what_the_body_takes() {
1357 let document = document();
1358 for (path, methods) in document["paths"].as_object().unwrap() {
1359 for (method, operation) in methods.as_object().unwrap() {
1360 let content = &operation["requestBody"]["content"]["application/json"];
1361 let Some(example) = content["example"].as_object() else { continue };
1362 let properties = &content["schema"]["properties"];
1363 for key in example.keys() {
1364 assert!(!properties[key].is_null(), "{method} {path}: {key} is not in the body");
1365 }
1366 }
1367 }
1368 }
1369
1370 /// The docs site's copy of the document. Run with `G1T_WRITE_OPENAPI=1`
1371 /// to rewrite it after changing an operation.
1372 #[test]
1373 fn the_docs_copy_is_current() {
1374 let path = concat!(env!("CARGO_MANIFEST_DIR"), "/../docs/src/data/openapi.json");
1375 let current = serde_json::to_string_pretty(&document()).unwrap() + "\n";
1376 if std::env::var_os("G1T_WRITE_OPENAPI").is_some() {
1377 std::fs::write(path, &current).unwrap();
1378 return;
1379 }
1380 let copy = std::fs::read_to_string(path).unwrap_or_default().replace("\r\n", "\n");
1381 assert!(
1382 copy == current,
1383 "apps/docs/src/data/openapi.json is out of date: run G1T_WRITE_OPENAPI=1 cargo test -p g1t-api openapi"
1384 );
1385 }
1386
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1387 /// The reference shows responses as they are sent: `snake_case`.
1388 #[test]
1389 fn example_responses_are_snake_case() {
1390 let document = document();
1391 for (path, methods) in document["paths"].as_object().unwrap() {
1392 for (method, operation) in methods.as_object().unwrap() {
1393 let example = &operation["responses"]["200"]["content"]["application/json"]["example"];
1394 let leaked = g1t_kit::wire::camel_case_keys(example);
1395 assert!(leaked.is_empty(), "{method} {path} shows {leaked:?}");
1396 }
1397 }
1398 }
1399
The statement is a month at a time, a line per kind of charge1400 /// Examples never hold anything that reads as a real credential, which
1401 /// secret scanners rightly flag in a public repository: they end in `…`
1402 /// after the prefix, as `whsec_…` and `g1t_…` do.
1403 #[test]
1404 fn examples_hold_no_real_looking_secrets() {
Fast pages, required checks on the branch, self-hosted runners, honest incidents1405 let prefixes = ["whsec_", "g1t_", "g1tr_", "g1trt_", "sk_live_", "sk_test_", "ghp_", "github_pat_", "xoxb-", "AKIA"];
The statement is a month at a time, a line per kind of charge1406 for (line, text) in REFERENCE.lines().enumerate() {
1407 for prefix in prefixes {
1408 let mut rest = text;
1409 while let Some(at) = rest.find(prefix) {
1410 let after = &rest[at + prefix.len()..];
1411 let run = after.chars().take_while(|c| c.is_ascii_alphanumeric()).count();
1412 assert!(
1413 run < 12,
1414 "reference.json line {}: `{prefix}` followed by {run} characters reads as a real secret; write `{prefix}…`",
1415 line + 1
1416 );
1417 rest = after;
1418 }
1419 }
1420 }
1421 }
1422}

This file's history is long; its oldest lines are credited to the oldest commit read.