Skip to content

g1t/apps/api/src/openapi.rs

1,099 lines46,740 bytesCodeBlame

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.

API and MCP server in Rust; a public index at the API root1//! The OpenAPI document, generated from the same list the routes are.
Merge branch 'worktree-agent-ab2e39e11a6493412'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.
API and MCP server in Rust; a public index at the API root6
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;
API and MCP server in Rust; a public index at the API root8use serde_json::{Map, Value, json};
9
10use crate::operations::Op;
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar11use crate::security::SecurityOp;
API and MCP server in Rust; a public index at the API root12use crate::rest::{ROUTES, Route};
13
Merge branch 'worktree-agent-ab2e39e11a6493412'14/// The sections of the API reference: a name, what it covers, and its
15/// operations in the order a reader meets them.
16const SECTIONS: &[(&str, &str, &[Op])] = &[
17 (
18 "Accounts",
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look19 "Signing in from a tool, who a token acts as, and your email addresses.",
20 &[Op::Whoami, Op::ListEmails, Op::AddEmail, Op::RemoveEmail, Op::UpdateEmailSettings],
Merge branch 'worktree-agent-ab2e39e11a6493412'21 ),
22 (
API: notifications over REST and MCP, with notifications scopes23 "Notifications",
24 "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.",
25 &[
26 Op::ListNotifications,
27 Op::MarkNotificationsRead,
28 Op::GetNotificationThread,
29 Op::MarkThreadRead,
30 Op::MarkThreadDone,
31 Op::SaveThread,
32 Op::SnoozeThread,
33 Op::GetThreadSubscription,
34 Op::SetThreadSubscription,
35 Op::DeleteThreadSubscription,
36 Op::GetRepoSubscription,
37 Op::SetRepoSubscription,
38 Op::DeleteRepoSubscription,
39 Op::ListWatchedRepos,
40 ],
41 ),
42 (
API: pinned projects over REST and MCP43 "Pinned projects",
44 "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.",
45 &[Op::ListPinnedProjects, Op::PinProject, Op::UnpinProject, Op::ReorderPinnedProjects],
46 ),
47 (
Merge branch 'worktree-agent-ab2e39e11a6493412'48 "Workspaces",
49 "A workspace owns repositories and is the first part of their address. People and agents work in workspaces.",
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily50 &[Op::CreateWorkspace, Op::UpdateWorkspace, Op::DeleteWorkspace],
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look51 ),
52 (
53 "Invites",
54 "While g1t is invite-only, every new account needs an invite. Your invites, and inviting people into a workspace by email.",
55 &[
56 Op::ListInvites,
57 Op::CreateInvite,
58 Op::RevokeInvite,
59 Op::ListWorkspaceInvites,
60 Op::InviteMember,
61 Op::RevokeWorkspaceInvite,
62 ],
Merge branch 'worktree-agent-ab2e39e11a6493412'63 ),
64 (
Usage, Billing settings and prepaid AI credit; fixes from the UX audit65 "Billing",
66 "A workspace's usage, its budget, its AI credit and its invoices. Members read them; owners change the budget and buy credit, as people. g1t's agents never change billing.",
67 &[
68 Op::GetUsage,
69 Op::GetBudget,
70 Op::SetBudget,
71 Op::GetAiCredit,
72 Op::BuyAiCredit,
73 Op::ListInvoices,
74 Op::GetBillingDetails,
75 ],
76 ),
77 (
Merge branch 'worktree-agent-ab2e39e11a6493412'78 "Repositories",
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look79 "A repository, how it handles pull requests, and its timeline: renaming, archiving, moving and deleting it.",
Merge branch 'worktree-agent-ab2e39e11a6493412'80 &[
81 Op::ListRepos,
82 Op::CreateRepo,
83 Op::GetRepo,
84 Op::UpdateRepo,
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look85 Op::RenameRepo,
86 Op::RenameBranch,
87 Op::SetRepoVisibility,
88 Op::ArchiveRepo,
89 Op::UnarchiveRepo,
90 Op::TransferRepo,
91 Op::DeleteRepo,
92 Op::ListDeletedRepos,
93 Op::RestoreRepo,
94 Op::PurgeRepo,
Merge branch 'worktree-agent-ab2e39e11a6493412'95 Op::GetRepoSettings,
96 Op::UpdateRepoSettings,
Fast pages, required checks on the branch, self-hosted runners, honest incidents97 Op::ListCheckNames,
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar98 Op::GetCodeownersErrors,
Merge branch 'worktree-agent-ab2e39e11a6493412'99 Op::ListEvents,
100 ],
101 ),
102 (
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look103 "Access",
104 "Who can do what in a repository: repository roles, people given a role on one repository (outside collaborators when they are not members), invitations, and a workspace's base permission.",
105 &[
106 Op::ListCollaborators,
107 Op::AddCollaborator,
108 Op::UpdateCollaborator,
109 Op::RemoveCollaborator,
110 Op::GetCollaboratorPermission,
111 Op::ListRepoInvitations,
112 Op::RevokeRepoInvitation,
113 Op::ListMyRepoInvitations,
114 Op::AcceptRepoInvitation,
115 Op::DeclineRepoInvitation,
116 Op::SetBasePermission,
117 Op::ListOutsideCollaborators,
118 ],
119 ),
120 (
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar121 "Teams",
122 "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.",
123 &[
124 Op::ListTeams,
125 Op::CreateTeam,
126 Op::GetTeam,
127 Op::UpdateTeam,
128 Op::DeleteTeam,
129 Op::ListTeamMembers,
130 Op::SetTeamMember,
131 Op::RemoveTeamMember,
132 Op::ListChildTeams,
133 Op::ListTeamRepos,
134 Op::SetTeamRepo,
135 Op::RemoveTeamRepo,
136 Op::SetTeamReviewAssignment,
137 Op::ListUserTeams,
138 ],
139 ),
140 (
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily141 "Security",
142 "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.",
143 &[Op::ListSecurityAlerts, Op::DismissSecurityAlert, Op::ReopenSecurityAlert],
144 ),
145 (
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar146 "Secret scanning",
147 "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.",
148 &[
149 Op::Security(SecurityOp::ListSecretAlerts),
150 Op::Security(SecurityOp::GetSecretAlert),
151 Op::Security(SecurityOp::UpdateSecretAlert),
152 Op::Security(SecurityOp::ListSecretLocations),
153 Op::Security(SecurityOp::BypassPushProtection),
154 Op::Security(SecurityOp::CheckSecretValidity),
155 Op::Security(SecurityOp::ListBypassRequests),
156 Op::Security(SecurityOp::ReviewBypassRequest),
157 Op::Security(SecurityOp::ListCustomPatterns),
158 Op::Security(SecurityOp::CreateCustomPattern),
159 Op::Security(SecurityOp::UpdateCustomPattern),
160 Op::Security(SecurityOp::DeleteCustomPattern),
161 Op::Security(SecurityOp::DryRunCustomPattern),
162 ],
163 ),
164 (
165 "Code scanning",
166 "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.",
167 &[
168 Op::Security(SecurityOp::ListCodeAlerts),
169 Op::Security(SecurityOp::GetCodeAlert),
170 Op::Security(SecurityOp::UpdateCodeAlert),
171 Op::Security(SecurityOp::ListAnalyses),
172 Op::Security(SecurityOp::UploadSarif),
173 Op::Security(SecurityOp::GetSarifUpload),
174 Op::Security(SecurityOp::FixAlert),
175 ],
176 ),
177 (
178 "Supply chain",
179 "What a repository depends on: vulnerability alerts, the dependency graph, an SPDX SBOM of it, and comparing two commits' dependencies as dependency review does.",
180 &[
181 Op::Security(SecurityOp::ListVulnerabilityAlerts),
182 Op::Security(SecurityOp::GetVulnerabilityAlert),
183 Op::Security(SecurityOp::UpdateVulnerabilityAlert),
184 Op::Security(SecurityOp::GetDependencyGraph),
185 Op::Security(SecurityOp::GetSbom),
186 Op::Security(SecurityOp::CompareDependencies),
187 ],
188 ),
189 (
190 "Security settings",
191 "When pull request checks fail, dependency review's policy, delegated bypass and validity checks, and a workspace's security overview.",
192 &[
193 Op::Security(SecurityOp::GetSettings),
194 Op::Security(SecurityOp::UpdateSettings),
195 Op::Security(SecurityOp::GetWorkspaceSettings),
196 Op::Security(SecurityOp::UpdateWorkspaceSettings),
197 Op::Security(SecurityOp::GetOverview),
198 ],
199 ),
200 (
Merge branch 'worktree-agent-ab2e39e11a6493412'201 "Issues",
202 "What should change in a repository, with labels and comments. Issues and pull requests share one sequence of numbers.",
203 &[
204 Op::ListIssues,
205 Op::CreateIssue,
206 Op::GetIssue,
207 Op::UpdateIssue,
208 Op::CloseIssue,
209 Op::ReopenIssue,
210 Op::AssignIssue,
Thirteen MCP tools and classic token scopes; agents rate their confidence and can be put on an issue in one step211 Op::Delegate,
Merge branch 'worktree-agent-ab2e39e11a6493412'212 Op::AddComment,
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar213 Op::ListIssueLabels,
214 Op::AddIssueLabels,
215 Op::SetIssueLabels,
216 Op::RemoveIssueLabels,
217 ],
218 ),
219 (
220 "Labels and milestones",
221 "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.",
222 &[
Merge branch 'worktree-agent-ab2e39e11a6493412'223 Op::ListLabels,
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar224 Op::CreateLabel,
225 Op::UpdateLabel,
226 Op::DeleteLabel,
227 Op::AddDefaultLabels,
228 Op::ListMilestones,
229 Op::CreateMilestone,
230 Op::GetMilestone,
231 Op::UpdateMilestone,
232 Op::DeleteMilestone,
Merge branch 'worktree-agent-ab2e39e11a6493412'233 ],
234 ),
235 (
236 "Plans",
237 "An outcome turned into the issues that would get there, with the order they must merge in.",
238 &[Op::PlanWork, Op::GetPlan, Op::ApplyPlan],
239 ),
240 (
241 "Pull requests",
242 "A proposed change in its own fork or on a branch. Several can be made for one issue; the one merged resolves it.",
243 &[
244 Op::ListPullRequests,
245 Op::CreatePullRequest,
246 Op::GetPullRequest,
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar247 Op::UpdatePullRequest,
Merge branch 'worktree-agent-ab2e39e11a6493412'248 Op::GetPullRequestChanges,
249 Op::MarkPullRequestReady,
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar250 Op::RequestReviewers,
251 Op::RemoveRequestedReviewers,
Merge branch 'worktree-agent-ab2e39e11a6493412'252 Op::ReviewPullRequest,
253 Op::MergePullRequest,
254 Op::ClosePullRequest,
255 Op::GetMergeQueue,
256 Op::MessageAgent,
257 Op::AnswerMessage,
258 Op::TakeMessages,
259 ],
260 ),
261 (
262 "Sessions",
263 "The record of how a pull request was made: prompts, reasoning and the tools that ran.",
264 &[Op::ReadSession, Op::RecordSession],
265 ),
266 (
Agents and memory, checks and conflicts, profiles, slug renames, custom domains267 "Memory",
268 "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.",
269 &[Op::Remember, Op::Recall],
270 ),
271 (
Search across all of g1t, Explore, and a command palette272 "Search",
273 "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.",
274 &[Op::Search],
275 ),
276 (
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API277 "Context",
278 "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.",
279 &[Op::SearchContext, Op::GetEntity],
280 ),
281 (
Merge branch 'worktree-agent-ab2e39e11a6493412'282 "Actions",
283 "GitHub Actions workflows in .g1t/workflows, their runs, and their jobs' logs.",
284 &[
285 Op::ListWorkflows,
286 Op::ListWorkflowRuns,
287 Op::GetWorkflowRun,
288 Op::GetJobLogs,
289 Op::DispatchWorkflow,
290 Op::CancelWorkflowRun,
291 Op::RerunWorkflowRun,
292 Op::UpdateWorkflow,
293 ],
294 ),
295 (
296 "Secrets and variables",
297 "Values that workflows and deployments read, per repository or for a whole workspace, with a row per environment.",
298 &[
299 Op::ListActionsSecrets,
300 Op::SetActionsSecret,
301 Op::DeleteActionsSecret,
302 Op::ListActionsVariables,
303 Op::SetActionsVariable,
304 Op::DeleteActionsVariable,
305 ],
306 ),
307 (
Fast pages, required checks on the branch, self-hosted runners, honest incidents308 "Runners",
309 "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.",
310 &[
311 Op::ListRunners,
312 Op::CreateRunnerRegistrationToken,
313 Op::RemoveRunner,
314 Op::ListRunnerGroups,
315 Op::CreateRunnerGroup,
316 Op::UpdateRunnerGroup,
317 Op::DeleteRunnerGroup,
318 Op::GetRunnerSettings,
319 Op::UpdateRunnerSettings,
320 ],
321 ),
322 (
Merge branch 'worktree-agent-ab2e39e11a6493412'323 "Webhooks",
324 "Signed HTTPS requests sent to your own address as things happen, for a repository or a whole workspace.",
325 &[
326 Op::ListWebhooks,
327 Op::CreateWebhook,
328 Op::UpdateWebhook,
329 Op::DeleteWebhook,
330 Op::PingWebhook,
331 Op::ListWebhookDeliveries,
332 Op::RedeliverWebhook,
333 ],
334 ),
335 (
336 "Integrations",
337 "A workspace's connections to outside systems: model providers, alert sources and issue trackers.",
338 &[
339 Op::ListIntegrations,
340 Op::ConnectIntegration,
341 Op::DisconnectIntegration,
342 Op::TestIntegration,
343 Op::GetModelRoutes,
344 Op::SetModelRoutes,
345 Op::GetContext,
346 Op::ImportIssue,
347 ],
348 ),
349];
350
API and MCP server in Rust; a public index at the API root351/// The section of the API reference an operation is listed under.
352fn tag(op: Op) -> &'static str {
Merge branch 'worktree-agent-ab2e39e11a6493412'353 SECTIONS
API and MCP server in Rust; a public index at the API root354 .iter()
Merge branch 'worktree-agent-ab2e39e11a6493412'355 .find(|(_, _, ops)| ops.contains(&op))
356 .map_or("Repositories", |(name, _, _)| name)
357}
358
359/// What an operation's page is called, as a short sentence.
360fn title(op: Op) -> &'static str {
361 match op {
362 Op::Whoami => "Get the current user",
363 Op::CreateWorkspace => "Create a workspace",
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look364 Op::DeleteWorkspace => "Delete a workspace",
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily365 Op::UpdateWorkspace => "Update a workspace",
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look366 Op::ListEmails => "List your email addresses",
367 Op::AddEmail => "Add an email address",
368 Op::RemoveEmail => "Remove an email address",
369 Op::UpdateEmailSettings => "Change your email settings",
370 Op::ListInvites => "List your invites",
371 Op::CreateInvite => "Create an invite",
372 Op::RevokeInvite => "Revoke an invite",
373 Op::ListWorkspaceInvites => "List a workspace's invites",
374 Op::InviteMember => "Invite someone to a workspace",
375 Op::RevokeWorkspaceInvite => "Revoke a workspace's invite",
376 Op::TransferRepo => "Transfer a repository",
377 Op::RenameRepo => "Rename a repository",
378 Op::RenameBranch => "Rename a branch",
379 Op::ArchiveRepo => "Archive a repository",
380 Op::UnarchiveRepo => "Unarchive a repository",
381 Op::SetRepoVisibility => "Change a repository's visibility",
382 Op::DeleteRepo => "Delete a repository",
383 Op::ListDeletedRepos => "List recently deleted repositories",
384 Op::RestoreRepo => "Restore a deleted repository",
385 Op::PurgeRepo => "Purge a deleted repository",
Merge branch 'worktree-agent-ab2e39e11a6493412'386 Op::ListRepos => "List repositories",
387 Op::GetRepo => "Get a repository",
388 Op::CreateRepo => "Create a repository",
389 Op::UpdateRepo => "Update a repository",
390 Op::GetRepoSettings => "Get repository settings",
391 Op::UpdateRepoSettings => "Update repository settings",
Fast pages, required checks on the branch, self-hosted runners, honest incidents392 Op::ListCheckNames => "List check names",
Merge branch 'worktree-agent-ab2e39e11a6493412'393 Op::GetMergeQueue => "Get the merge queue",
394 Op::MessageAgent => "Message an agent",
395 Op::AnswerMessage => "Answer a message",
396 Op::TakeMessages => "Take new messages",
Agents and memory, checks and conflicts, profiles, slug renames, custom domains397 Op::Remember => "Remember something",
398 Op::Recall => "Recall memory",
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API399 Op::SearchContext => "Search the context hub",
400 Op::GetEntity => "Get a catalog entry",
Search across all of g1t, Explore, and a command palette401 Op::Search => "Search g1t",
Merge branch 'worktree-agent-ab2e39e11a6493412'402 Op::ListIssues => "List issues",
403 Op::GetIssue => "Get an issue",
404 Op::CreateIssue => "Create an issue",
405 Op::UpdateIssue => "Update an issue",
406 Op::CloseIssue => "Close an issue",
407 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-agent408 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 step409 Op::Delegate => "Put an agent on it",
Merge branch 'worktree-agent-ab2e39e11a6493412'410 Op::PlanWork => "Plan work",
411 Op::GetPlan => "Get a plan",
412 Op::ApplyPlan => "Apply a plan",
413 Op::ListLabels => "List labels",
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar414 Op::CreateLabel => "Create a label",
415 Op::UpdateLabel => "Update a label",
416 Op::DeleteLabel => "Delete a label",
417 Op::AddDefaultLabels => "Add the default labels",
418 Op::ListIssueLabels => "List an issue's labels",
419 Op::AddIssueLabels => "Add labels to an issue",
420 Op::SetIssueLabels => "Set an issue's labels",
421 Op::RemoveIssueLabels => "Remove labels from an issue",
422 Op::ListMilestones => "List milestones",
423 Op::GetMilestone => "Get a milestone",
424 Op::CreateMilestone => "Create a milestone",
425 Op::UpdateMilestone => "Update a milestone",
426 Op::DeleteMilestone => "Delete a milestone",
427 Op::UpdatePullRequest => "Update a pull request",
Merge branch 'worktree-agent-ab2e39e11a6493412'428 Op::AddComment => "Add a comment",
429 Op::ReviewPullRequest => "Review a pull request",
430 Op::ListPullRequests => "List pull requests",
431 Op::GetPullRequest => "Get a pull request",
432 Op::CreatePullRequest => "Create a pull request",
433 Op::RecordSession => "Record session entries",
434 Op::ReadSession => "Read a session",
435 Op::MarkPullRequestReady => "Mark a pull request ready",
436 Op::ClosePullRequest => "Close a pull request",
437 Op::GetPullRequestChanges => "Get a pull request's changes",
438 Op::MergePullRequest => "Merge a pull request",
439 Op::ListEvents => "List repository events",
440 Op::ListIntegrations => "List integrations",
441 Op::ConnectIntegration => "Connect an integration",
442 Op::DisconnectIntegration => "Disconnect an integration",
443 Op::TestIntegration => "Test an integration",
444 Op::GetContext => "Look up a ticket",
445 Op::ImportIssue => "Import an issue",
446 Op::GetModelRoutes => "Get model routes",
447 Op::SetModelRoutes => "Set model routes",
448 Op::ListWebhooks => "List webhooks",
449 Op::CreateWebhook => "Create a webhook",
450 Op::UpdateWebhook => "Update a webhook",
451 Op::DeleteWebhook => "Delete a webhook",
452 Op::PingWebhook => "Ping a webhook",
453 Op::ListWebhookDeliveries => "List webhook deliveries",
454 Op::RedeliverWebhook => "Redeliver a webhook delivery",
455 Op::ListWorkflows => "List workflows",
456 Op::ListWorkflowRuns => "List workflow runs",
457 Op::GetWorkflowRun => "Get a workflow run",
458 Op::GetJobLogs => "Get a job's log",
459 Op::DispatchWorkflow => "Run a workflow",
460 Op::CancelWorkflowRun => "Cancel a workflow run",
461 Op::RerunWorkflowRun => "Re-run a workflow run",
462 Op::UpdateWorkflow => "Turn a workflow on or off",
463 Op::ListActionsSecrets => "List secrets",
464 Op::SetActionsSecret => "Set a secret",
465 Op::DeleteActionsSecret => "Delete a secret",
466 Op::ListActionsVariables => "List variables",
467 Op::SetActionsVariable => "Set a variable",
468 Op::DeleteActionsVariable => "Delete a variable",
Fast pages, required checks on the branch, self-hosted runners, honest incidents469 Op::ListRunners => "List self-hosted runners",
470 Op::ListRunnerGroups => "List runner groups",
471 Op::GetRunnerSettings => "Get runner settings",
472 Op::CreateRunnerRegistrationToken => "Create a runner registration token",
473 Op::RemoveRunner => "Remove a self-hosted runner",
474 Op::CreateRunnerGroup => "Create a runner group",
475 Op::UpdateRunnerGroup => "Change a runner group",
476 Op::DeleteRunnerGroup => "Delete a runner group",
477 Op::UpdateRunnerSettings => "Change runner settings",
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look478 Op::ListCollaborators => "List who has access",
479 Op::AddCollaborator => "Add a collaborator",
480 Op::UpdateCollaborator => "Change a collaborator's role",
481 Op::RemoveCollaborator => "Remove a collaborator",
482 Op::GetCollaboratorPermission => "Get someone's permission",
483 Op::ListRepoInvitations => "List a repository's invitations",
484 Op::RevokeRepoInvitation => "Revoke a repository invitation",
485 Op::ListMyRepoInvitations => "List your repository invitations",
486 Op::AcceptRepoInvitation => "Accept a repository invitation",
487 Op::DeclineRepoInvitation => "Decline a repository invitation",
488 Op::SetBasePermission => "Set the base permission",
489 Op::ListOutsideCollaborators => "List outside collaborators",
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily490 Op::ListSecurityAlerts => "List security alerts",
491 Op::DismissSecurityAlert => "Dismiss a security alert",
492 Op::ReopenSecurityAlert => "Reopen a security alert",
API: notifications over REST and MCP, with notifications scopes493 Op::ListNotifications => "List notifications",
494 Op::MarkNotificationsRead => "Mark notifications read",
495 Op::GetNotificationThread => "Get a thread",
496 Op::MarkThreadRead => "Mark a thread read",
497 Op::MarkThreadDone => "Mark a thread done",
498 Op::SaveThread => "Save a thread",
499 Op::SnoozeThread => "Snooze a thread",
500 Op::GetThreadSubscription => "Get a thread subscription",
501 Op::SetThreadSubscription => "Set a thread subscription",
502 Op::DeleteThreadSubscription => "Unsubscribe from a thread",
503 Op::GetRepoSubscription => "Get how you watch a repository",
504 Op::SetRepoSubscription => "Watch a repository",
505 Op::DeleteRepoSubscription => "Stop watching a repository",
506 Op::ListWatchedRepos => "List repositories you watch",
API: pinned projects over REST and MCP507 Op::ListPinnedProjects => "List your pinned projects",
Usage, Billing settings and prepaid AI credit; fixes from the UX audit508 Op::GetUsage => "Get a workspace's usage",
509 Op::GetBudget => "Get a workspace's budget",
510 Op::SetBudget => "Change a workspace's budget",
511 Op::GetAiCredit => "Get a workspace's AI credit",
512 Op::BuyAiCredit => "Buy AI credit",
513 Op::ListInvoices => "List a workspace's invoices",
514 Op::GetBillingDetails => "Get a workspace's billing details",
API: pinned projects over REST and MCP515 Op::PinProject => "Pin a project",
516 Op::UnpinProject => "Unpin a project",
517 Op::ReorderPinnedProjects => "Reorder your pinned projects",
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar518 Op::ListTeams => "List teams",
519 Op::GetTeam => "Get a team",
520 Op::CreateTeam => "Create a team",
521 Op::UpdateTeam => "Update a team",
522 Op::DeleteTeam => "Delete a team",
523 Op::ListTeamMembers => "List a team's members",
524 Op::SetTeamMember => "Add or change a team member",
525 Op::RemoveTeamMember => "Remove a team member",
526 Op::ListChildTeams => "List child teams",
527 Op::ListTeamRepos => "List a team's repositories",
528 Op::SetTeamRepo => "Give a team a role on a repository",
529 Op::RemoveTeamRepo => "Remove a team from a repository",
530 Op::SetTeamReviewAssignment => "Set a team's review assignment",
531 Op::ListUserTeams => "List someone's teams",
532 Op::RequestReviewers => "Request reviewers",
533 Op::RemoveRequestedReviewers => "Remove requested reviewers",
534 Op::GetCodeownersErrors => "List CODEOWNERS errors",
535 Op::Security(op) => op.title(),
API and MCP server in Rust; a public index at the API root536 }
537}
538
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look539/// Why an operation can be refused with `402 payment_required`, if it
540/// can: the ones that start an agent, when the workspace has no credit,
541/// and the ones that make a repository private in a workspace, when a free
542/// workspace's private storage has no room for it.
543fn may_need_payment(op: Op) -> Option<&'static str> {
544 match op {
545 Op::AssignIssue | Op::PlanWork | Op::ApplyPlan => Some("The workspace has no agent credit."),
546 Op::UpdateRepo | Op::SetRepoVisibility | Op::TransferRepo => Some(
547 "A free workspace's private storage has no room for this private repository.",
548 ),
549 _ => None,
550 }
Merge branch 'worktree-agent-ab2e39e11a6493412'551}
552
553/// What the reference says beyond each operation's own description, keyed
554/// by operation id, written by hand from what the services return: `notes`
555/// (Markdown, added to the description) and example `params` (path),
556/// `query`, `request` (body) and `response`.
557const REFERENCE: &str = include_str!("reference.json");
558
559fn examples() -> Map<String, Value> {
560 match serde_json::from_str(REFERENCE) {
561 Ok(Value::Object(examples)) => examples,
562 _ => Map::new(),
API and MCP server in Rust; a public index at the API root563 }
564}
565
Agents as a team: lifecycle, merge queue, billing and a new shell566/// `/repos/:owner/:name` as OpenAPI writes it: `/repos/{owner}/{name}`.
API and MCP server in Rust; a public index at the API root567fn openapi_path(route: &Route) -> String {
568 route
569 .path
570 .split('/')
571 .map(|segment| match segment.strip_prefix(':') {
572 Some(name) => format!("{{{name}}}"),
573 None => segment.to_owned(),
574 })
575 .collect::<Vec<_>>()
576 .join("/")
577}
578
579fn error_response(description: &str) -> Value {
580 json!({
581 "description": description,
582 "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } },
583 })
584}
585
Merge branch 'worktree-agent-ab2e39e11a6493412'586/// A parameter in the path or the query, described by the operation's
587/// input schema where it has the same name.
588fn parameter(name: &str, place: &str, required: bool, schema: Option<&Value>) -> Value {
589 let mut schema = schema.cloned().unwrap_or_else(|| json!({ "type": "string" }));
590 let description = match name {
591 "owner" => Some(Value::from("The workspace that owns the repository.")),
592 "name" => Some(Value::from("The repository's name.")),
593 _ => schema.as_object_mut().and_then(|schema| schema.remove("description")),
594 };
595 let mut parameter = json!({
596 "name": name,
597 "in": place,
598 "required": required,
599 "schema": schema,
600 });
601 if let Some(description) = description {
602 parameter["description"] = description;
603 }
604 parameter
605}
606
607/// The operation id of a route. An operation reached at a workspace's
608/// address as well as a repository's is documented once for each, with its
609/// own id; GitHub's alternative addresses for one operation keep GitHub's
610/// names.
611fn operation_id(route: &Route) -> String {
612 let op = route.op;
613 let base = match (route.method, route.path.rsplit('/').next().unwrap_or_default()) {
614 ("PUT", "enable") => "enable_workflow".to_owned(),
615 ("PUT", "disable") => "disable_workflow".to_owned(),
616 ("POST", "rerun-failed-jobs") => "rerun_failed_jobs".to_owned(),
617 ("PATCH", ":setting") => "update_actions_variable".to_owned(),
618 ("GET", "runs") if route.path.contains("/workflows/:workflow/") => "list_runs_of_workflow".to_owned(),
API: notifications over REST and MCP, with notifications scopes619 // One repository's notifications, and an issue's subscription by
620 // its number rather than a thread's id.
621 (_, "notifications") if route.path.starts_with("/repos/") => match op {
622 Op::ListNotifications => "list_repo_notifications".to_owned(),
623 _ => "mark_repo_notifications_read".to_owned(),
624 },
625 (method, "subscription") if route.path.contains("/issues/:number/") => match method {
626 "GET" => "get_issue_subscription".to_owned(),
627 "PUT" => "set_issue_subscription".to_owned(),
628 _ => "delete_issue_subscription".to_owned(),
629 },
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar630 // One label off an issue, by its name in the path.
631 ("DELETE", ":label") if route.path.contains("/issues/:number/") => "remove_issue_label".to_owned(),
API: notifications over REST and MCP, with notifications scopes632 ("DELETE", "saved") => "unsave_thread".to_owned(),
633 ("DELETE", "snooze") => "unsnooze_thread".to_owned(),
Merge branch 'worktree-agent-ab2e39e11a6493412'634 _ => op.name().to_owned(),
635 };
636 if route.path.starts_with("/workspaces/") && ROUTES.iter().any(|other| other.op == op && other.path.starts_with("/repos/")) {
637 format!("{base}_for_workspace")
638 } else {
639 base
640 }
641}
642
643/// The summary of a route: its operation's title, or for one of GitHub's
644/// alternative addresses, what that address does.
645fn summary(route: &Route, id: &str) -> String {
646 let base = match id.trim_end_matches("_for_workspace") {
647 "enable_workflow" => "Turn a workflow on",
648 "disable_workflow" => "Turn a workflow off",
649 "rerun_failed_jobs" => "Re-run failed jobs",
650 "update_actions_variable" => "Update a variable",
651 "list_runs_of_workflow" => "List a workflow's runs",
API: notifications over REST and MCP, with notifications scopes652 "list_repo_notifications" => "List a repository's notifications",
653 "mark_repo_notifications_read" => "Mark a repository's notifications read",
654 "get_issue_subscription" => "Get your subscription to an issue",
655 "set_issue_subscription" => "Subscribe to an issue",
656 "delete_issue_subscription" => "Unsubscribe from an issue",
657 "unsave_thread" => "Unsave a thread",
658 "unsnooze_thread" => "Bring a snoozed thread back",
Merge branch 'worktree-agent-ab2e39e11a6493412'659 _ => title(route.op),
660 };
661 if id.ends_with("_for_workspace") {
662 format!("{base} for a workspace")
663 } else {
664 base.to_owned()
665 }
666}
667
API and MCP server in Rust; a public index at the API root668fn operation(route: &Route) -> Value {
669 let op = route.op;
670 let path_params: Vec<&str> = route.params().collect();
671 // `owner` and `name` in the path stand for the operation's `repo` input.
672 let covered = |name: &str| name == "repo" || path_params.contains(&name);
Merge branch 'worktree-agent-ab2e39e11a6493412'673 let all_properties = op.properties();
674 let mut properties = all_properties.clone();
API and MCP server in Rust; a public index at the API root675 properties.retain(|name, _| !covered(name));
676 let required: Vec<String> = op
677 .required()
678 .into_iter()
679 .filter(|name| !covered(name))
680 .collect();
681
682 let mut parameters: Vec<Value> = path_params
683 .iter()
Merge branch 'worktree-agent-ab2e39e11a6493412'684 .map(|name| parameter(name, "path", true, all_properties.get(*name)))
API and MCP server in Rust; a public index at the API root685 .collect();
686 let mut body = Value::Null;
687 if route.method == "GET" {
688 for (name, key) in route.query {
Merge branch 'worktree-agent-ab2e39e11a6493412'689 parameters.push(parameter(
690 name,
691 "query",
692 required.iter().any(|required| required == key),
693 properties.get(*key),
694 ));
API and MCP server in Rust; a public index at the API root695 }
696 } else if !properties.is_empty() {
697 let mut schema = json!({ "type": "object", "properties": properties });
698 if !required.is_empty() {
699 schema["required"] = json!(required);
700 }
701 body = json!({
702 "required": !required.is_empty(),
703 "content": { "application/json": { "schema": schema } },
704 });
705 }
706
Merge branch 'worktree-agent-ab2e39e11a6493412'707 let id = operation_id(route);
708 let mut responses = Map::new();
709 responses.insert(
710 "200".into(),
711 json!({
712 "description": "Success.",
713 "content": { "application/json": { "schema": {} } },
714 }),
715 );
716 responses.insert(
717 "401".into(),
718 error_response("A token is required, or the one sent is not valid."),
719 );
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look720 if let Some(reason) = may_need_payment(op) {
721 responses.insert("402".into(), error_response(reason));
Merge branch 'worktree-agent-ab2e39e11a6493412'722 }
Thirteen MCP tools and classic token scopes; agents rate their confidence and can be put on an issue in one step723 responses.insert(
724 "403".into(),
725 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."),
726 );
Search across all of g1t, Explore, and a command palette727 if !matches!(op, Op::Whoami | Op::ListRepos | Op::Search) {
Merge branch 'worktree-agent-ab2e39e11a6493412'728 responses.insert("404".into(), error_response("It does not exist, or you cannot see it."));
729 }
730 if route.method != "GET" {
731 responses.insert(
732 "409".into(),
733 error_response("The request conflicts with the current state."),
734 );
735 }
736 if op != Op::Whoami {
737 responses.insert("422".into(), error_response("The input is not valid."));
738 }
739 // 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 step740 let scope: Vec<&str> = scope_for(op.name()).map(|scope| scope.as_str()).into_iter().collect();
Merge branch 'worktree-agent-ab2e39e11a6493412'741 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 step742 json!([{ "token": scope }])
Webhooks: every event, to your own addresses, signed and retried743 } else {
Thirteen MCP tools and classic token scopes; agents rate their confidence and can be put on an issue in one step744 json!([{ "token": scope }, {}])
Webhooks: every event, to your own addresses, signed and retried745 };
Thirteen MCP tools and classic token scopes; agents rate their confidence and can be put on an issue in one step746 let (tool, action) = crate::tools::TOOLS
747 .iter()
748 .find_map(|tool| {
749 tool.actions
750 .iter()
751 .find(|action| action.op == op)
752 .map(|action| (tool.name, action.name))
753 })
754 .unwrap_or_default();
API and MCP server in Rust; a public index at the API root755 let mut described = json!({
Webhooks: every event, to your own addresses, signed and retried756 "operationId": id,
API and MCP server in Rust; a public index at the API root757 "tags": [tag(op)],
Merge branch 'worktree-agent-ab2e39e11a6493412'758 "summary": summary(route, &id),
API and MCP server in Rust; a public index at the API root759 "description": op.description(),
Thirteen MCP tools and classic token scopes; agents rate their confidence and can be put on an issue in one step760 "x-operation": op.name(),
761 "x-mcp-tool": tool,
762 "x-mcp-action": action,
763 "x-scope": scope.first().copied(),
Merge branch 'worktree-agent-ab2e39e11a6493412'764 "security": security,
API and MCP server in Rust; a public index at the API root765 "parameters": parameters,
Merge branch 'worktree-agent-ab2e39e11a6493412'766 "responses": responses,
API and MCP server in Rust; a public index at the API root767 });
768 if !body.is_null() {
769 described["requestBody"] = body;
770 }
771 described
772}
773
774/// Entries for device sign-in, which is not an operation.
775fn onboarding() -> Map<String, Value> {
776 let paths = json!({
Agents as a team: lifecycle, merge queue, billing and a new shell777 "/device/code": {
API and MCP server in Rust; a public index at the API root778 "post": {
779 "operationId": "device_code",
780 "tags": ["Accounts"],
781 "summary": "Start signing in",
Agents as a team: lifecycle, merge queue, billing and a new shell782 "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`.",
API and MCP server in Rust; a public index at the API root783 "security": [],
784 "requestBody": {
785 "content": { "application/json": { "schema": {
786 "type": "object",
787 "properties": {
788 "client_name": {
789 "type": "string",
790 "description": "What is asking, shown to the person approving. For example, Claude Code.",
791 },
792 },
793 } } },
794 },
795 "responses": { "200": {
796 "description": "The codes for this sign-in.",
797 "content": { "application/json": { "schema": {
798 "type": "object",
799 "properties": {
Agents as a team: lifecycle, merge queue, billing and a new shell800 "device_code": { "type": "string", "description": "Secret. Send it to /device/token." },
API and MCP server in Rust; a public index at the API root801 "user_code": { "type": "string", "description": "Shown to the person, like WDJB-MJHT." },
802 "verification_uri": { "type": "string" },
803 "verification_uri_complete": {
804 "type": "string",
805 "description": "The link to give the person; it carries the code.",
806 },
807 "expires_in": { "type": "integer", "description": "Seconds until the codes expire." },
808 "interval": { "type": "integer", "description": "Seconds to wait between polls." },
809 },
810 } } },
811 } },
812 },
813 },
Agents as a team: lifecycle, merge queue, billing and a new shell814 "/device/token": {
API and MCP server in Rust; a public index at the API root815 "post": {
816 "operationId": "device_token",
817 "tags": ["Accounts"],
818 "summary": "Finish signing in",
819 "description": "Asks whether the person has approved. Poll no faster than the interval. The token is returned once.",
820 "security": [],
821 "requestBody": {
822 "required": true,
823 "content": { "application/json": { "schema": {
824 "type": "object",
825 "required": ["device_code"],
826 "properties": { "device_code": { "type": "string" } },
827 } } },
828 },
829 "responses": { "200": {
830 "description": "The state of the sign-in.",
831 "content": { "application/json": { "schema": {
832 "type": "object",
833 "required": ["status"],
834 "properties": {
835 "status": { "type": "string", "enum": ["pending", "approved", "denied", "expired"] },
836 "token": { "type": "string", "description": "Present when approved." },
837 "username": { "type": "string" },
838 "verified": {
839 "type": "boolean",
840 "description": "Whether the account's email is confirmed.",
841 },
842 },
843 } } },
844 } },
845 },
846 },
847 });
848 match paths {
849 Value::Object(paths) => paths,
850 _ => Map::new(),
851 }
852}
853
Merge branch 'worktree-agent-ab2e39e11a6493412'854
855/// Puts each operation's examples, where it has them, into its request
856/// and response. Path and query values go under `x-example-params` and
857/// `x-example-query`, which tools that build a request can use.
858fn attach_examples(paths: &mut Map<String, Value>) {
859 let examples = examples();
860 for methods in paths.values_mut() {
861 let Some(methods) = methods.as_object_mut() else { continue };
862 for operation in methods.values_mut() {
863 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 step864 let name = operation["x-operation"].as_str().unwrap_or_default().to_owned();
865 let Some(example) = examples.get(&id).or_else(|| examples.get(&name)) else {
Merge branch 'worktree-agent-ab2e39e11a6493412'866 continue;
867 };
868 if let Some(notes) = example.get("notes").and_then(Value::as_str) {
869 let description = operation["description"].as_str().unwrap_or_default();
870 operation["description"] = json!(format!("{description}\n\n{notes}"));
871 }
872 if let Some(response) = example.get("response") {
873 let content = &mut operation["responses"]["200"]["content"]["application/json"];
874 if content.is_object() {
875 content["example"] = response.clone();
876 }
877 }
878 if let Some(request) = example.get("request") {
879 let content = &mut operation["requestBody"]["content"]["application/json"];
880 if content.is_object() {
881 content["example"] = request.clone();
882 }
883 }
884 for (key, extension) in [("params", "x-example-params"), ("query", "x-example-query")] {
885 if let Some(values) = example.get(key) {
886 operation[extension] = values.clone();
887 }
888 }
889 }
890 }
891}
892
API and MCP server in Rust; a public index at the API root893pub fn document() -> Value {
894 let mut paths = onboarding();
895 for route in ROUTES {
896 let entry = paths
897 .entry(openapi_path(route))
898 .or_insert_with(|| json!({}));
899 entry[route.method.to_lowercase()] = operation(route);
900 }
Merge branch 'worktree-agent-ab2e39e11a6493412'901 attach_examples(&mut paths);
902 let tags: Vec<Value> = SECTIONS
903 .iter()
904 .map(|(name, description, ops)| {
905 json!({
906 "name": name,
907 "description": description,
908 // The section's operations in reading order, by MCP tool name.
909 "x-tools": ops.iter().map(|op| op.name()).collect::<Vec<_>>(),
910 })
911 })
912 .collect();
913 let codes = ["unauthenticated", "payment_required", "forbidden", "not_found", "conflict", "invalid"];
API and MCP server in Rust; a public index at the API root914 json!({
915 "openapi": "3.1.0",
916 "info": {
917 "title": "g1t API",
918 "version": "1",
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API919 "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.",
API and MCP server in Rust; a public index at the API root920 "license": { "name": "MIT", "identifier": "MIT" },
921 },
922 "servers": [{ "url": "https://api.g1t.sh" }],
923 "security": [{ "token": [] }, {}],
Merge branch 'worktree-agent-ab2e39e11a6493412'924 "tags": tags,
API and MCP server in Rust; a public index at the API root925 "paths": paths,
926 "components": {
927 "securitySchemes": {
928 "token": {
929 "type": "http",
930 "scheme": "bearer",
Thirteen MCP tools and classic token scopes; agents rate their confidence and can be put on an issue in one step931 "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.",
API and MCP server in Rust; a public index at the API root932 },
933 },
934 "schemas": {
935 "Error": {
936 "type": "object",
937 "required": ["error"],
938 "properties": {
939 "error": {
940 "type": "object",
941 "required": ["code", "message"],
942 "properties": {
Merge branch 'worktree-agent-ab2e39e11a6493412'943 "code": { "type": "string", "enum": codes },
API and MCP server in Rust; a public index at the API root944 "message": { "type": "string" },
Thirteen MCP tools and classic token scopes; agents rate their confidence and can be put on an issue in one step945 "needed_scope": {
946 "type": "string",
947 "description": "On a 403 for an access token without the scope the call needs: that scope, such as `issues:write`.",
948 },
API and MCP server in Rust; a public index at the API root949 },
950 },
951 },
952 },
953 },
954 },
955 })
956}
957
958#[cfg(test)]
959mod tests {
960 use super::*;
961
962 #[test]
963 fn every_route_is_documented_once() {
964 let document = document();
965 let mut ids = Vec::new();
966 for (_, methods) in document["paths"].as_object().unwrap() {
967 for (_, operation) in methods.as_object().unwrap() {
968 ids.push(operation["operationId"].as_str().unwrap().to_owned());
969 }
970 }
971 for op in Op::ALL {
972 assert_eq!(
973 ids.iter().filter(|id| *id == op.name()).count(),
974 1,
975 "{}",
976 op.name()
977 );
978 }
Webhooks: every event, to your own addresses, signed and retried979 let mut unique = ids.clone();
980 unique.sort();
981 unique.dedup();
982 assert_eq!(unique.len(), ids.len(), "operation ids repeat");
API and MCP server in Rust; a public index at the API root983 }
984
985 #[test]
986 fn path_and_query_inputs_are_not_repeated_in_the_body() {
987 let document = document();
Agents as a team: lifecycle, merge queue, billing and a new shell988 let merge = &document["paths"]["/repos/{owner}/{name}/pulls/{number}/merge"]["post"];
API and MCP server in Rust; a public index at the API root989 let body = &merge["requestBody"]["content"]["application/json"]["schema"]["properties"];
990 assert!(body.get("keep_issue_open").is_some());
991 assert!(body.get("repo").is_none() && body.get("number").is_none());
Agents as a team: lifecycle, merge queue, billing and a new shell992 let list = &document["paths"]["/repos"]["get"];
API and MCP server in Rust; a public index at the API root993 assert_eq!(list["parameters"][0]["name"], "q");
994 assert!(list.get("requestBody").is_none());
995 }
996
997 #[test]
Merge branch 'worktree-agent-ab2e39e11a6493412'998 fn every_operation_is_in_one_section() {
999 for op in Op::ALL {
1000 let sections = SECTIONS
1001 .iter()
1002 .filter(|(_, _, ops)| ops.contains(&op))
1003 .count();
1004 assert_eq!(sections, 1, "{}", op.name());
1005 }
1006 }
1007
1008 #[test]
API and MCP server in Rust; a public index at the API root1009 fn titles_read_as_sentences() {
Merge branch 'worktree-agent-ab2e39e11a6493412'1010 assert_eq!(title(Op::CreateIssue), "Create an issue");
API and MCP server in Rust; a public index at the API root1011 assert_eq!(title(Op::Whoami), "Get the current user");
1012 }
Merge branch 'worktree-agent-ab2e39e11a6493412'1013
1014 #[test]
1015 fn every_operation_has_an_example_response() {
1016 let examples = examples();
1017 assert!(!examples.is_empty(), "reference.json does not parse");
1018 let document = document();
1019 let mut known = Vec::new();
1020 for (path, methods) in document["paths"].as_object().unwrap() {
1021 for (method, operation) in methods.as_object().unwrap() {
1022 known.push(operation["operationId"].as_str().unwrap().to_owned());
1023 let example = &operation["responses"]["200"]["content"]["application/json"]["example"];
1024 assert!(!example.is_null(), "{method} {path} has no example response");
1025 }
1026 }
1027 for id in examples.keys() {
1028 assert!(known.contains(id), "reference.json names {id}, which is not an operation");
1029 }
1030 }
1031
1032 #[test]
1033 fn example_requests_send_only_what_the_body_takes() {
1034 let document = document();
1035 for (path, methods) in document["paths"].as_object().unwrap() {
1036 for (method, operation) in methods.as_object().unwrap() {
1037 let content = &operation["requestBody"]["content"]["application/json"];
1038 let Some(example) = content["example"].as_object() else { continue };
1039 let properties = &content["schema"]["properties"];
1040 for key in example.keys() {
1041 assert!(!properties[key].is_null(), "{method} {path}: {key} is not in the body");
1042 }
1043 }
1044 }
1045 }
1046
1047 /// The docs site's copy of the document. Run with `G1T_WRITE_OPENAPI=1`
1048 /// to rewrite it after changing an operation.
1049 #[test]
1050 fn the_docs_copy_is_current() {
1051 let path = concat!(env!("CARGO_MANIFEST_DIR"), "/../docs/src/data/openapi.json");
1052 let current = serde_json::to_string_pretty(&document()).unwrap() + "\n";
1053 if std::env::var_os("G1T_WRITE_OPENAPI").is_some() {
1054 std::fs::write(path, &current).unwrap();
1055 return;
1056 }
1057 let copy = std::fs::read_to_string(path).unwrap_or_default().replace("\r\n", "\n");
1058 assert!(
1059 copy == current,
1060 "apps/docs/src/data/openapi.json is out of date: run G1T_WRITE_OPENAPI=1 cargo test -p g1t-api openapi"
1061 );
1062 }
API reference: no example reads as a real secret1063
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1064 /// The reference shows responses as they are sent: `snake_case`.
1065 #[test]
1066 fn example_responses_are_snake_case() {
1067 let document = document();
1068 for (path, methods) in document["paths"].as_object().unwrap() {
1069 for (method, operation) in methods.as_object().unwrap() {
1070 let example = &operation["responses"]["200"]["content"]["application/json"]["example"];
1071 let leaked = g1t_kit::wire::camel_case_keys(example);
1072 assert!(leaked.is_empty(), "{method} {path} shows {leaked:?}");
1073 }
1074 }
1075 }
1076
API reference: no example reads as a real secret1077 /// Examples never hold anything that reads as a real credential, which
1078 /// secret scanners rightly flag in a public repository: they end in `…`
1079 /// after the prefix, as `whsec_…` and `g1t_…` do.
1080 #[test]
1081 fn examples_hold_no_real_looking_secrets() {
Fast pages, required checks on the branch, self-hosted runners, honest incidents1082 let prefixes = ["whsec_", "g1t_", "g1tr_", "g1trt_", "sk_live_", "sk_test_", "ghp_", "github_pat_", "xoxb-", "AKIA"];
API reference: no example reads as a real secret1083 for (line, text) in REFERENCE.lines().enumerate() {
1084 for prefix in prefixes {
1085 let mut rest = text;
1086 while let Some(at) = rest.find(prefix) {
1087 let after = &rest[at + prefix.len()..];
1088 let run = after.chars().take_while(|c| c.is_ascii_alphanumeric()).count();
1089 assert!(
1090 run < 12,
1091 "reference.json line {}: `{prefix}` followed by {run} characters reads as a real secret; write `{prefix}…`",
1092 line + 1
1093 );
1094 rest = after;
1095 }
1096 }
1097 }
1098 }
API and MCP server in Rust; a public index at the API root1099}

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