g1t/crates/contracts/src/events.rs

699 lines25,100 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.

Events service in Rust, with RFC 3339 times and accurate push events1//! Events published on the bus, and the events service that carries them.
2//! Mirrors `packages/contracts/src/events.ts`.
Rust repos service with shipping; pull requests kept in the model3
4use serde::Serialize;
5
6/// What a publisher supplies; the bus fills in the id and time.
7#[derive(Debug, Serialize)]
8#[serde(rename_all = "camelCase")]
9pub struct NewEvent<T: Serialize> {
10 #[serde(rename = "type")]
11 pub kind: &'static str,
12 /// The service that published it.
13 pub source: &'static str,
14 /// The repo the event concerns.
15 pub repo_id: Option<String>,
16 /// The user or agent that caused it, if any.
17 pub actor: Option<String>,
18 pub data: T,
19}
20
21#[derive(Debug, Serialize)]
22#[serde(rename_all = "camelCase")]
23pub struct RepoCreated {
24 pub repo_id: String,
25 pub namespace: String,
26 pub name: String,
27 pub is_private: bool,
28}
29
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look30/// The payload of `repo.collaborator_added`, `repo.collaborator_removed`
31/// and `repo.collaborator_role_changed`: a person's own role on one
32/// repository (see `access`). `role` is the role they have now (null once
33/// removed); `previous_role` what they had before (null when added).
Rust repos service with shipping; pull requests kept in the model34#[derive(Debug, Serialize)]
35#[serde(rename_all = "camelCase")]
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look36pub struct RepoCollaborator {
37 pub repo_id: String,
38 pub namespace: String,
39 pub name: String,
40 pub username: String,
41 pub role: Option<crate::access::RepoRole>,
42 pub previous_role: Option<crate::access::RepoRole>,
43}
44
45#[derive(Debug, Serialize)]
46#[serde(rename_all = "camelCase")]
Rust repos service with shipping; pull requests kept in the model47pub struct RepoForked {
48 pub repo_id: String,
49 pub source_repo_id: String,
Issues and pull requests replace intents and attempts50 pub pull_id: String,
Rust repos service with shipping; pull requests kept in the model51}
52
GitHub Actions on g1t, part one: reading workflows53/// One branch or tag moved by a push. `after` is the commit it points to now.
Rust repos service with shipping; pull requests kept in the model54#[derive(Debug, Serialize)]
55#[serde(rename_all = "camelCase")]
56pub struct GitPush {
57 pub repo_id: String,
Events service in Rust, with RFC 3339 times and accurate push events58 /// The full ref, such as `refs/heads/main`.
Rust repos service with shipping; pull requests kept in the model59 #[serde(rename = "ref")]
60 pub git_ref: String,
GitHub Actions on g1t, part one: reading workflows61 /// Where it pointed before; absent for a new branch or tag.
62 #[serde(skip_serializing_if = "Option::is_none")]
63 pub before: Option<String>,
Rust repos service with shipping; pull requests kept in the model64 pub after: String,
Events service in Rust, with RFC 3339 times and accurate push events65 /// Whether the ref is the repository's default branch.
66 pub default_branch: bool,
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily67 /// Set when the push was too large to scan for secrets before it was
68 /// stored, and was let through: the security service scans
69 /// `before..after` after it landed. Absent otherwise.
70 #[serde(skip_serializing_if = "std::ops::Not::not")]
71 pub unscanned: bool,
Rust repos service with shipping; pull requests kept in the model72}
Work service in Rust, with RFC 3339 timestamps73
Agents as a team: lifecycle, merge queue, billing and a new shell74/// The payload of `issue.opened`, `issue.updated`, `issue.assigned`,
75/// `issue.closed` and `issue.reopened`; each uses the fields that apply to it.
Issues and pull requests replace intents and attempts76#[derive(Debug, Default, Serialize)]
Work service in Rust, with RFC 3339 timestamps77#[serde(rename_all = "camelCase")]
Issues and pull requests replace intents and attempts78pub struct IssueEvent {
79 pub issue_id: String,
Work service in Rust, with RFC 3339 timestamps80 pub repo_id: String,
81 pub number: u32,
g1t is the stored author of what it opens; the person who asked is requested_by and keeps the author's rights82 /// Who opened it: g1t, for one its agent filed while at work.
83 #[serde(skip_serializing_if = "Option::is_none")]
84 pub author: Option<crate::credentials::Principal>,
85 /// For an issue g1t's agent filed: the person it was working for.
86 #[serde(skip_serializing_if = "Option::is_none")]
87 pub requested_by: Option<crate::credentials::Principal>,
Issues and pull requests replace intents and attempts88 #[serde(skip_serializing_if = "Option::is_none")]
89 pub title: Option<String>,
90 /// On close: `completed` or `not_planned`.
91 #[serde(skip_serializing_if = "Option::is_none")]
92 pub reason: Option<&'static str>,
93 /// On close: the number of the pull request whose merge closed it.
94 #[serde(skip_serializing_if = "Option::is_none")]
95 pub resolved_by: Option<u32>,
Agents as a team: lifecycle, merge queue, billing and a new shell96 /// On `issue.assigned`: the people it is now assigned to.
97 #[serde(skip_serializing_if = "Option::is_none")]
98 pub assignees: Option<Vec<String>>,
Work service in Rust, with RFC 3339 timestamps99}
100
Acceptance checks in sandboxes, line comments and review verdicts101/// The payload of `pull.opened`, `pull.ready`, `pull.updated` (its head
102/// moved), `pull.closed` and `pull.merged`; each uses the fields that apply to it.
Work service in Rust, with RFC 3339 timestamps103#[derive(Debug, Default, Serialize)]
104#[serde(rename_all = "camelCase")]
Issues and pull requests replace intents and attempts105pub struct PullEvent {
106 pub pull_id: String,
Work service in Rust, with RFC 3339 timestamps107 pub repo_id: String,
Issues and pull requests replace intents and attempts108 pub number: u32,
g1t is the stored author of what it opens; the person who asked is requested_by and keeps the author's rights109 /// Who opened it: g1t, for a change g1t made.
110 #[serde(skip_serializing_if = "Option::is_none")]
111 pub author: Option<crate::credentials::Principal>,
112 /// For a change g1t made: the person who asked for it.
113 #[serde(skip_serializing_if = "Option::is_none")]
114 pub requested_by: Option<crate::credentials::Principal>,
Issues and pull requests replace intents and attempts115 /// The number of the issue it is for.
116 #[serde(skip_serializing_if = "Option::is_none")]
117 pub issue: Option<u32>,
Work service in Rust, with RFC 3339 timestamps118 #[serde(skip_serializing_if = "Option::is_none")]
119 pub agent: Option<String>,
Workflows run when an agent's pull request is marked ready120 /// On merge: the commit the branch now points to. On update and when
121 /// marked ready: the head of the change.
Work service in Rust, with RFC 3339 timestamps122 #[serde(skip_serializing_if = "Option::is_none")]
Issues and pull requests replace intents and attempts123 pub commit: Option<String>,
124 /// On close: the pull request that was merged instead.
Work service in Rust, with RFC 3339 timestamps125 #[serde(skip_serializing_if = "Option::is_none")]
Issues and pull requests replace intents and attempts126 pub superseded_by: Option<u32>,
Thirteen MCP tools and classic token scopes; agents rate their confidence and can be put on an issue in one step127 /// How sure g1t is of a g1t agent's change, once it has worked that out.
128 #[serde(skip_serializing_if = "Option::is_none")]
129 pub confidence: Option<crate::work::Confidence>,
Work service in Rust, with RFC 3339 timestamps130}
131
Acceptance checks in sandboxes, line comments and review verdicts132/// `checks.completed`: a run of an issue's acceptance checks against a pull
133/// request finished.
134#[derive(Debug, Serialize)]
135#[serde(rename_all = "camelCase")]
136pub struct ChecksEvent {
137 pub pull_id: String,
138 pub repo_id: String,
139 pub number: u32,
140 /// `passed`, `failed` or `errored`.
141 pub status: &'static str,
142 /// The commit that was checked.
143 pub commit: String,
144}
145
Actions: workflow_run, workflow.completed, artifacts on the run page, Node 24146/// `workflow.completed`: a GitHub Actions run finished.
147#[derive(Debug, Serialize)]
148#[serde(rename_all = "camelCase")]
149pub struct WorkflowEvent {
150 pub run_id: String,
151 pub repo_id: String,
152 /// The workflow's name, and its file.
153 pub workflow: String,
154 pub path: String,
155 /// The run's number among the workflow's runs.
156 pub number: u64,
157 /// The GitHub event that started it, such as `push`.
158 pub event: String,
159 /// `success`, `failure`, `cancelled` or `skipped`.
160 pub conclusion: String,
161 #[serde(rename = "ref")]
162 pub git_ref: String,
163 pub sha: String,
164 /// The pull request it ran for, if any.
165 #[serde(skip_serializing_if = "Option::is_none")]
166 pub pull: Option<u32>,
167}
168
Agents as a team: lifecycle, merge queue, billing and a new shell169/// `review.completed`: a g1t agent finished reviewing a pull request, or
170/// could not.
171#[derive(Debug, Serialize)]
172#[serde(rename_all = "camelCase")]
173pub struct ReviewEvent {
174 pub pull_id: String,
175 pub repo_id: String,
176 pub number: u32,
177 /// `approve` or `request_changes`; absent when no review was written.
178 #[serde(skip_serializing_if = "Option::is_none")]
179 pub verdict: Option<&'static str>,
180}
181
Issues and pull requests replace intents and attempts182/// `comment.created`. `number` is the issue or pull request commented on.
Work service in Rust, with RFC 3339 timestamps183#[derive(Debug, Serialize)]
184#[serde(rename_all = "camelCase")]
Issues and pull requests replace intents and attempts185pub struct CommentCreated {
186 pub comment_id: String,
187 pub repo_id: String,
188 pub number: u32,
Agents as a team: lifecycle, merge queue, billing and a new shell189 /// Set when the comment is on a pull request.
190 #[serde(skip_serializing_if = "Option::is_none")]
191 pub pull_id: Option<String>,
192 /// Set when the comment is a review: approve or request changes.
193 #[serde(skip_serializing_if = "Option::is_none")]
194 pub verdict: Option<crate::work::Verdict>,
Issues and pull requests replace intents and attempts195}
196
197#[derive(Debug, Serialize)]
198#[serde(rename_all = "camelCase")]
Work service in Rust, with RFC 3339 timestamps199pub struct SessionAppended {
Issues and pull requests replace intents and attempts200 pub pull_id: String,
201 pub repo_id: String,
202 pub number: u32,
Work service in Rust, with RFC 3339 timestamps203 pub count: u32,
204}
205
Events service in Rust, with RFC 3339 times and accurate push events206/// An event as stored in the log and delivered to subscribers. `data` is
207/// left as JSON; each reader decodes the types it cares about.
208#[derive(Clone, Debug, Serialize, serde::Deserialize)]
209#[serde(rename_all = "camelCase")]
210pub struct Event {
211 /// Sorts by the time the event was published.
212 pub id: String,
213 #[serde(rename = "type")]
214 pub kind: String,
215 /// The service that published it.
216 pub source: String,
217 /// RFC 3339.
218 pub time: String,
219 /// The repo the event concerns.
220 pub repo_id: Option<String>,
221 /// The user or agent that caused it, if any.
222 pub actor: Option<String>,
223 pub data: serde_json::Value,
224}
225
226/// `publish`, as a publisher sends it. Returns nothing.
227#[derive(Debug, Serialize)]
228pub struct Publish<T: Serialize> {
229 pub events: Vec<NewEvent<T>>,
230}
231
232/// `publish`, as the events service reads it.
233#[derive(Debug, serde::Deserialize)]
234pub struct PublishArgs {
235 pub events: Vec<Published>,
236}
237
238/// A [`NewEvent`] of any type, as received.
Work service in Rust, with RFC 3339 timestamps239#[derive(Debug, serde::Deserialize)]
240#[serde(rename_all = "camelCase")]
Events service in Rust, with RFC 3339 times and accurate push events241pub struct Published {
Work service in Rust, with RFC 3339 timestamps242 #[serde(rename = "type")]
243 pub kind: String,
Events service in Rust, with RFC 3339 times and accurate push events244 pub source: String,
245 #[serde(default)]
Work service in Rust, with RFC 3339 timestamps246 pub repo_id: Option<String>,
Events service in Rust, with RFC 3339 times and accurate push events247 #[serde(default)]
248 pub actor: Option<String>,
Work service in Rust, with RFC 3339 timestamps249 pub data: serde_json::Value,
250}
Events service in Rust, with RFC 3339 times and accurate push events251
252/// `list`: events from the log, newest first. Returns `Vec<Event>`.
253#[derive(Debug, Default, Serialize, serde::Deserialize)]
254#[serde(rename_all = "camelCase")]
255pub struct ListArgs {
256 #[serde(default)]
257 pub repo_id: Option<String>,
258 /// Only these types; all types when empty.
259 #[serde(default)]
260 pub types: Vec<String>,
261 /// Only events older than this event id.
262 #[serde(default)]
263 pub before: Option<String>,
264 #[serde(default)]
265 pub limit: Option<u32>,
266}
Agents as a team: lifecycle, merge queue, billing and a new shell267
Agents and memory, checks and conflicts, profiles, slug renames, custom domains268/// `workspace.renamed`: a workspace's slug changed from `from` to `to`.
269/// Every service that stores a slug moves its rows to the workspace's
270/// *current* slug (ask identity by `workspace_id`), so that a repeated or
271/// late delivery after a second rename still lands in the right place.
272#[derive(Clone, Debug, Serialize, serde::Deserialize)]
273#[serde(rename_all = "camelCase")]
274pub struct WorkspaceRenamed {
275 pub workspace_id: String,
276 pub from: String,
277 pub to: String,
278}
279
280impl WorkspaceRenamed {
281 /// The slugs whose rows move to `current`: the two this rename names,
282 /// minus `current` itself. Moving rows keyed by either converges on the
283 /// current slug whatever order renames are delivered in.
284 pub fn stale_slugs(&self, current: &str) -> Vec<String> {
285 let mut slugs: Vec<String> = Vec::new();
286 for slug in [&self.from, &self.to] {
287 if slug != current && !slugs.contains(slug) {
288 slugs.push(slug.clone());
289 }
290 }
291 slugs
292 }
293}
294
Search across all of g1t, Explore, and a command palette295/// `repo.updated`: a repository's description, topics or visibility
296/// changed. `visibility_changed` says whether it went public or private,
297/// which `repo.visibility_changed` also announces on its own.
298#[derive(Debug, Serialize, serde::Deserialize)]
299#[serde(rename_all = "camelCase")]
300pub struct RepoUpdated {
301 pub repo_id: String,
302 pub namespace: String,
303 pub name: String,
304 pub is_private: bool,
305 #[serde(default)]
306 pub visibility_changed: bool,
307}
308
309/// `repo.visibility_changed`: a repository went public or private.
310#[derive(Debug, Serialize, serde::Deserialize)]
311#[serde(rename_all = "camelCase")]
312pub struct RepoVisibilityChanged {
313 pub repo_id: String,
314 pub is_private: bool,
315}
316
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look317/// `repo.renamed`: a repository's name changed within its workspace,
318/// keeping its id and its git store key. Like `repo.transferred`, a path
319/// change: every service that keeps rows under the repository's path moves
320/// them to its *current* path (ask repos `path_by_id`), so a repeated or
321/// late delivery after a second rename or a transfer still lands in the
322/// right place. `g1t_kit::transfer::on_event` handles both.
323#[derive(Clone, Debug, Serialize, serde::Deserialize)]
Search across all of g1t, Explore, and a command palette324#[serde(rename_all = "camelCase")]
325pub struct RepoRenamed {
326 pub repo_id: String,
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look327 /// The workspace it is in.
Search across all of g1t, Explore, and a command palette328 pub namespace: String,
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look329 /// Its old name.
330 pub from: String,
331 /// Its new name.
332 pub to: String,
333}
334
335impl RepoRenamed {
336 /// The paths whose rows move to `current` (`namespace/name`): the two
337 /// this rename names, minus `current`.
338 pub fn stale_paths(&self, current: &str) -> Vec<String> {
339 let mut paths: Vec<String> = Vec::new();
340 for name in [&self.from, &self.to] {
341 let path = format!("{}/{name}", self.namespace);
342 if path != current && !paths.contains(&path) {
343 paths.push(path);
344 }
345 }
346 paths
347 }
348}
349
350/// `repo.deleted`: a repository was deleted. It is hidden everywhere and
351/// git refuses it, but it can be restored until `purge_after`, so services
352/// stop what runs for it (agents, workflows, deployments, indexing,
353/// webhook deliveries) and hide it, and keep what they hold until
354/// `repo.purged`. `repo.restored` brings it back.
355#[derive(Clone, Debug, Serialize, serde::Deserialize)]
356#[serde(rename_all = "camelCase")]
357pub struct RepoDeleted {
358 pub repo_id: String,
359 #[serde(default)]
360 pub namespace: String,
361 #[serde(default)]
Search across all of g1t, Explore, and a command palette362 pub name: String,
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look363 #[serde(default)]
364 pub is_private: bool,
365 /// RFC 3339: when it is purged unless restored first.
366 #[serde(default)]
367 pub purge_after: String,
Packages, with a container registry on g1t.sh; workspaces deleted whole and kept 30 days; Members for every member368 /// It went with its workspace (`workspace.deleting`). Services that
369 /// handle the workspace as a whole (deployments pauses its apps rather
370 /// than taking them down) leave this one to that.
371 #[serde(default)]
372 pub with_workspace: bool,
Search across all of g1t, Explore, and a command palette373}
374
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look375/// `repo.restored`: a deleted repository is back, at its path, as it was.
376/// Services start again what `repo.deleted` stopped: index it, deploy its
377/// production, show it.
378#[derive(Clone, Debug, Serialize, serde::Deserialize)]
379#[serde(rename_all = "camelCase")]
380pub struct RepoRestored {
381 pub repo_id: String,
382 pub namespace: String,
383 pub name: String,
384 pub is_private: bool,
Packages, with a container registry on g1t.sh; workspaces deleted whole and kept 30 days; Members for every member385 /// It came back with its workspace (`workspace.restored`).
386 #[serde(default)]
387 pub with_workspace: bool,
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look388}
389
390/// `repo.purged`: a deleted repository is gone for good, its git data
391/// with it. Services drop every row they keep for it by `repo_id`, except
392/// history that belongs to its workspace: ledgers, invoices and the audit
393/// log. Its path is free for a new repository.
394#[derive(Clone, Debug, Serialize, serde::Deserialize)]
395#[serde(rename_all = "camelCase")]
396pub struct RepoPurged {
397 pub repo_id: String,
398 pub namespace: String,
399 pub name: String,
400}
401
402/// `repo.archived` and `repo.unarchived`: a repository became read-only,
403/// or writable again. While archived, pushes are refused, issues and pull
404/// requests are locked, and agents and workflows do not run for it; its
405/// deployments keep serving.
406#[derive(Clone, Debug, Serialize, serde::Deserialize)]
407#[serde(rename_all = "camelCase")]
408pub struct RepoArchived {
409 pub repo_id: String,
410 pub namespace: String,
411 pub name: String,
412 pub archived: bool,
413}
414
415/// `repo.default_branch_changed`: the branch everything lands on is now
416/// `to`. `renamed` says whether `from` was renamed to `to` (open pull
417/// requests into it now target `to`) rather than another branch chosen.
418#[derive(Clone, Debug, Serialize, serde::Deserialize)]
419#[serde(rename_all = "camelCase")]
420pub struct RepoDefaultBranchChanged {
421 pub repo_id: String,
422 pub from: String,
423 pub to: String,
424 #[serde(default)]
425 pub renamed: bool,
426}
427
428/// `branch.renamed`: a branch was renamed. Pull requests from or into
429/// `from` follow it to `to`, and web addresses that name `from` redirect.
430#[derive(Clone, Debug, Serialize, serde::Deserialize)]
431#[serde(rename_all = "camelCase")]
432pub struct BranchRenamed {
433 pub repo_id: String,
434 pub from: String,
435 pub to: String,
436 /// Whether it is the default branch.
437 #[serde(default)]
438 pub default_branch: bool,
439}
440
441/// `repo.transferred`: a repository moved from one workspace to another,
442/// keeping its id and its name. Every service that keeps a repository
443/// under its path (`namespace/name`) or its workspace's slug moves those
444/// rows to the repository's *current* path (ask repos `path_by_id`), so a
445/// repeated or late delivery after a second transfer still lands in the
446/// right place. What was charged or recorded before the transfer stays
447/// with the workspace it happened in.
448#[derive(Clone, Debug, Serialize, serde::Deserialize)]
Search across all of g1t, Explore, and a command palette449#[serde(rename_all = "camelCase")]
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look450pub struct RepoTransferred {
Search across all of g1t, Explore, and a command palette451 pub repo_id: String,
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look452 pub name: String,
453 /// The workspace it left.
454 pub from: String,
455 /// The workspace it went to.
456 pub to: String,
Search across all of g1t, Explore, and a command palette457}
458
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look459impl RepoTransferred {
460 /// The paths whose rows move to `current` (`namespace/name`): the two
461 /// this transfer names, minus `current`. Moving rows keyed by either
462 /// converges whatever order transfers are delivered in.
463 pub fn stale_paths(&self, current: &str) -> Vec<String> {
464 let mut paths: Vec<String> = Vec::new();
465 for namespace in [&self.from, &self.to] {
466 let path = format!("{namespace}/{}", self.name);
467 if path != current && !paths.contains(&path) {
468 paths.push(path);
469 }
470 }
471 paths
472 }
473}
474
475/// `workspace.deleted`: a workspace is gone. Services drop what they keep
476/// for it alone (its webhooks, integrations, secrets, memory, guardrails,
477/// agent queue) and keep what is history: ledgers, invoices and the audit
478/// log stay under its slug, which is never given to another workspace.
479#[derive(Clone, Debug, Serialize, serde::Deserialize)]
480#[serde(rename_all = "camelCase")]
481pub struct WorkspaceDeleted {
482 pub workspace_id: String,
483 pub slug: String,
484}
485
Packages, with a container registry on g1t.sh; workspaces deleted whole and kept 30 days; Members for every member486/// `workspace.deleting`: an owner deleted a workspace, and it can be
487/// restored by g1t's staff until `purge_after`. Nobody can reach it in the
488/// meantime. Services hide what they keep for it and stop what runs for it,
489/// keeping their rows: repos deletes its repositories softly (each with a
490/// `repo.deleted` whose `with_workspace` is set), deployments pauses its
491/// apps, search drops it from results. `workspace.restored` undoes exactly
492/// that; once `purge_after` passes, `workspace.deleted` follows and
493/// services purge as for any deleted workspace.
494#[derive(Clone, Debug, Serialize, serde::Deserialize)]
495#[serde(rename_all = "camelCase")]
496pub struct WorkspaceDeleting {
497 pub workspace_id: String,
498 pub slug: String,
499 /// The username of the owner who deleted it.
500 pub by: String,
501 /// RFC 3339: when it is purged unless restored first.
502 pub purge_after: String,
503}
504
505/// `workspace.restored`: staff brought a deleted workspace back, with its
506/// members and tokens. Services undo what they did on `workspace.deleting`,
507/// and only that: a repository deleted on its own before stays deleted.
508#[derive(Clone, Debug, Serialize, serde::Deserialize)]
509#[serde(rename_all = "camelCase")]
510pub struct WorkspaceRestored {
511 pub workspace_id: String,
512 pub slug: String,
513}
514
Search across all of g1t, Explore, and a command palette515/// `user.updated`: an account was made, or changed what its profile shows
516/// (name, bio, avatar). Nothing private: ask identity for the profile.
517#[derive(Debug, Serialize, serde::Deserialize)]
518#[serde(rename_all = "camelCase")]
519pub struct UserUpdated {
520 pub username: String,
521}
522
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look523/// `user.email_added`, `user.email_verified`, `user.email_removed` and
524/// `user.primary_email_changed`: an account's addresses changed. Never the
525/// address itself; ask identity, as the person, for that.
526#[derive(Debug, Serialize, serde::Deserialize)]
527#[serde(rename_all = "camelCase")]
528pub struct UserEmailChanged {
529 pub user_id: String,
530 /// Whether g1t staff made the change.
531 #[serde(default)]
532 pub by_staff: bool,
533}
534
Search across all of g1t, Explore, and a command palette535/// `workspace.updated`: a workspace was made, or its name, description or
536/// icon changed. Ask identity for it by slug.
537#[derive(Debug, Serialize, serde::Deserialize)]
538#[serde(rename_all = "camelCase")]
539pub struct WorkspaceUpdated {
540 pub workspace_id: String,
541 pub slug: String,
542}
543
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look544/// `invite.created`: someone (or staff) made an invite. Never the code or
545/// the address it is for.
546#[derive(Debug, Serialize, serde::Deserialize)]
547#[serde(rename_all = "camelCase")]
548pub struct InviteCreated {
549 pub invite_id: String,
550 /// The account that made it; null when staff did.
551 pub inviter_id: Option<String>,
552 /// The workspace it joins.
553 pub workspace_id: Option<String>,
554 /// Whether it is bound to one email address.
555 pub bound: bool,
556}
557
558/// `invite.redeemed`: an invite was used, by a new account or by an
559/// existing one joining a workspace.
560#[derive(Debug, Serialize, serde::Deserialize)]
561#[serde(rename_all = "camelCase")]
562pub struct InviteRedeemed {
563 pub invite_id: String,
564 pub user_id: String,
565 pub inviter_id: Option<String>,
566 pub workspace_id: Option<String>,
567 /// Whether it made the account.
568 pub created_account: bool,
569}
570
571/// `waitlist.requested`: someone asked for access. Ask identity's staff
572/// methods for the entry; the address is not in the event.
573#[derive(Debug, Serialize, serde::Deserialize)]
574#[serde(rename_all = "camelCase")]
575pub struct WaitlistRequested {
576 pub entry_id: String,
577}
578
Packages, with a container registry on g1t.sh; workspaces deleted whole and kept 30 days; Members for every member579/// The payload of `package.published`, `package.version_deleted`,
580/// `package.deleted` and `package.visibility_changed`; each uses the fields
581/// that apply to it. `repo_id` is the repository the package is linked to.
582#[derive(Clone, Debug, Default, Serialize)]
583#[serde(rename_all = "camelCase")]
584pub struct PackageEvent {
585 pub package_id: String,
586 pub workspace: String,
587 pub ecosystem: String,
588 pub name: String,
589 pub repo_id: Option<String>,
590 /// The version published or deleted: for a container image, its
591 /// manifest's digest.
592 #[serde(skip_serializing_if = "Option::is_none")]
593 pub version: Option<String>,
594 #[serde(skip_serializing_if = "Option::is_none")]
595 pub digest: Option<String>,
596 #[serde(skip_serializing_if = "Option::is_none")]
597 pub size: Option<u64>,
598 /// On publish: the tags that now point to the version.
599 #[serde(skip_serializing_if = "Option::is_none")]
600 pub tags: Option<Vec<String>>,
601 /// On `package.visibility_changed`: `public` or `private`.
602 #[serde(skip_serializing_if = "Option::is_none")]
603 pub visibility: Option<String>,
604}
605
Agents as a team: lifecycle, merge queue, billing and a new shell606/// `queue.changed`: a repository's merge queue gained, lost or settled an
607/// entry, so the next batch may be ready to test.
608#[derive(Debug, Serialize)]
609#[serde(rename_all = "camelCase")]
610pub struct QueueChanged {
611 pub repo_id: String,
612}
Agents and memory, checks and conflicts, profiles, slug renames, custom domains613
614#[cfg(test)]
615mod tests {
616 use super::*;
617
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily618 #[test]
619 fn a_push_says_it_was_unscanned_only_when_it_was() {
620 let push = |unscanned| GitPush {
621 repo_id: "rep_1".into(),
622 git_ref: "refs/heads/import".into(),
623 before: None,
624 after: "abc".into(),
625 default_branch: false,
626 unscanned,
627 };
628 let quiet = serde_json::to_value(push(false)).unwrap();
629 assert!(quiet.get("unscanned").is_none());
630 let flagged = serde_json::to_value(push(true)).unwrap();
631 assert_eq!(flagged["unscanned"], true);
632 assert_eq!(flagged["ref"], "refs/heads/import");
633 }
634
Agents and memory, checks and conflicts, profiles, slug renames, custom domains635 fn renamed(from: &str, to: &str) -> WorkspaceRenamed {
636 WorkspaceRenamed {
637 workspace_id: "wsp_1".into(),
638 from: from.into(),
639 to: to.into(),
640 }
641 }
642
643 #[test]
644 fn stale_slugs_leave_out_the_current_one() {
645 assert_eq!(renamed("a", "b").stale_slugs("b"), vec!["a"]);
646 // Delivered after a second rename, b → c: both move to c.
647 assert_eq!(renamed("a", "b").stale_slugs("c"), vec!["a", "b"]);
648 // Renamed back: a → b → a.
649 assert_eq!(renamed("a", "b").stale_slugs("a"), vec!["b"]);
650 }
651
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look652 fn transferred(from: &str, to: &str) -> RepoTransferred {
653 RepoTransferred {
654 repo_id: "rep_1".into(),
655 name: "rocket".into(),
656 from: from.into(),
657 to: to.into(),
658 }
659 }
660
661 #[test]
662 fn stale_paths_leave_out_the_current_one() {
663 assert_eq!(transferred("a", "b").stale_paths("b/rocket"), vec!["a/rocket"]);
664 // Delivered after a second transfer, b → c: both move to c.
665 assert_eq!(
666 transferred("a", "b").stale_paths("c/rocket"),
667 vec!["a/rocket", "b/rocket"]
668 );
669 // Transferred back: a → b → a.
670 assert_eq!(transferred("a", "b").stale_paths("a/rocket"), vec!["b/rocket"]);
671 }
672
673 #[test]
674 fn a_transfer_reads_as_published() {
675 let data = serde_json::json!({ "repoId": "rep_1", "name": "rocket", "from": "a", "to": "b" });
676 let event: RepoTransferred = serde_json::from_value(data).unwrap();
677 assert_eq!((event.from.as_str(), event.to.as_str()), ("a", "b"));
678 }
679
680 #[test]
681 fn a_rename_names_both_paths_in_its_workspace() {
682 let renamed = RepoRenamed {
683 repo_id: "rep_1".into(),
684 namespace: "acme".into(),
685 from: "old".into(),
686 to: "new".into(),
687 };
688 assert_eq!(renamed.stale_paths("acme/new"), vec!["acme/old"]);
689 // Delivered after a transfer: both names in acme move.
690 assert_eq!(renamed.stale_paths("flagon/new"), vec!["acme/old", "acme/new"]);
691 }
692
Agents and memory, checks and conflicts, profiles, slug renames, custom domains693 #[test]
694 fn reads_the_published_payload() {
695 let data = serde_json::json!({ "workspaceId": "wsp_1", "from": "a", "to": "b" });
696 let event: WorkspaceRenamed = serde_json::from_value(data).unwrap();
697 assert_eq!((event.from.as_str(), event.to.as_str()), ("a", "b"));
698 }
699}