g1t/crates/contracts/src/events.rs

635 lines22,309 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,
Search across all of g1t, Explore, and a command palette368}
369
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look370/// `repo.restored`: a deleted repository is back, at its path, as it was.
371/// Services start again what `repo.deleted` stopped: index it, deploy its
372/// production, show it.
373#[derive(Clone, Debug, Serialize, serde::Deserialize)]
374#[serde(rename_all = "camelCase")]
375pub struct RepoRestored {
376 pub repo_id: String,
377 pub namespace: String,
378 pub name: String,
379 pub is_private: bool,
380}
381
382/// `repo.purged`: a deleted repository is gone for good, its git data
383/// with it. Services drop every row they keep for it by `repo_id`, except
384/// history that belongs to its workspace: ledgers, invoices and the audit
385/// log. Its path is free for a new repository.
386#[derive(Clone, Debug, Serialize, serde::Deserialize)]
387#[serde(rename_all = "camelCase")]
388pub struct RepoPurged {
389 pub repo_id: String,
390 pub namespace: String,
391 pub name: String,
392}
393
394/// `repo.archived` and `repo.unarchived`: a repository became read-only,
395/// or writable again. While archived, pushes are refused, issues and pull
396/// requests are locked, and agents and workflows do not run for it; its
397/// deployments keep serving.
398#[derive(Clone, Debug, Serialize, serde::Deserialize)]
399#[serde(rename_all = "camelCase")]
400pub struct RepoArchived {
401 pub repo_id: String,
402 pub namespace: String,
403 pub name: String,
404 pub archived: bool,
405}
406
407/// `repo.default_branch_changed`: the branch everything lands on is now
408/// `to`. `renamed` says whether `from` was renamed to `to` (open pull
409/// requests into it now target `to`) rather than another branch chosen.
410#[derive(Clone, Debug, Serialize, serde::Deserialize)]
411#[serde(rename_all = "camelCase")]
412pub struct RepoDefaultBranchChanged {
413 pub repo_id: String,
414 pub from: String,
415 pub to: String,
416 #[serde(default)]
417 pub renamed: bool,
418}
419
420/// `branch.renamed`: a branch was renamed. Pull requests from or into
421/// `from` follow it to `to`, and web addresses that name `from` redirect.
422#[derive(Clone, Debug, Serialize, serde::Deserialize)]
423#[serde(rename_all = "camelCase")]
424pub struct BranchRenamed {
425 pub repo_id: String,
426 pub from: String,
427 pub to: String,
428 /// Whether it is the default branch.
429 #[serde(default)]
430 pub default_branch: bool,
431}
432
433/// `repo.transferred`: a repository moved from one workspace to another,
434/// keeping its id and its name. Every service that keeps a repository
435/// under its path (`namespace/name`) or its workspace's slug moves those
436/// rows to the repository's *current* path (ask repos `path_by_id`), so a
437/// repeated or late delivery after a second transfer still lands in the
438/// right place. What was charged or recorded before the transfer stays
439/// with the workspace it happened in.
440#[derive(Clone, Debug, Serialize, serde::Deserialize)]
Search across all of g1t, Explore, and a command palette441#[serde(rename_all = "camelCase")]
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look442pub struct RepoTransferred {
Search across all of g1t, Explore, and a command palette443 pub repo_id: String,
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look444 pub name: String,
445 /// The workspace it left.
446 pub from: String,
447 /// The workspace it went to.
448 pub to: String,
Search across all of g1t, Explore, and a command palette449}
450
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look451impl RepoTransferred {
452 /// The paths whose rows move to `current` (`namespace/name`): the two
453 /// this transfer names, minus `current`. Moving rows keyed by either
454 /// converges whatever order transfers are delivered in.
455 pub fn stale_paths(&self, current: &str) -> Vec<String> {
456 let mut paths: Vec<String> = Vec::new();
457 for namespace in [&self.from, &self.to] {
458 let path = format!("{namespace}/{}", self.name);
459 if path != current && !paths.contains(&path) {
460 paths.push(path);
461 }
462 }
463 paths
464 }
465}
466
467/// `workspace.deleted`: a workspace is gone. Services drop what they keep
468/// for it alone (its webhooks, integrations, secrets, memory, guardrails,
469/// agent queue) and keep what is history: ledgers, invoices and the audit
470/// log stay under its slug, which is never given to another workspace.
471#[derive(Clone, Debug, Serialize, serde::Deserialize)]
472#[serde(rename_all = "camelCase")]
473pub struct WorkspaceDeleted {
474 pub workspace_id: String,
475 pub slug: String,
476}
477
Search across all of g1t, Explore, and a command palette478/// `user.updated`: an account was made, or changed what its profile shows
479/// (name, bio, avatar). Nothing private: ask identity for the profile.
480#[derive(Debug, Serialize, serde::Deserialize)]
481#[serde(rename_all = "camelCase")]
482pub struct UserUpdated {
483 pub username: String,
484}
485
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look486/// `user.email_added`, `user.email_verified`, `user.email_removed` and
487/// `user.primary_email_changed`: an account's addresses changed. Never the
488/// address itself; ask identity, as the person, for that.
489#[derive(Debug, Serialize, serde::Deserialize)]
490#[serde(rename_all = "camelCase")]
491pub struct UserEmailChanged {
492 pub user_id: String,
493 /// Whether g1t staff made the change.
494 #[serde(default)]
495 pub by_staff: bool,
496}
497
Search across all of g1t, Explore, and a command palette498/// `workspace.updated`: a workspace was made, or its name, description or
499/// icon changed. Ask identity for it by slug.
500#[derive(Debug, Serialize, serde::Deserialize)]
501#[serde(rename_all = "camelCase")]
502pub struct WorkspaceUpdated {
503 pub workspace_id: String,
504 pub slug: String,
505}
506
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look507/// `invite.created`: someone (or staff) made an invite. Never the code or
508/// the address it is for.
509#[derive(Debug, Serialize, serde::Deserialize)]
510#[serde(rename_all = "camelCase")]
511pub struct InviteCreated {
512 pub invite_id: String,
513 /// The account that made it; null when staff did.
514 pub inviter_id: Option<String>,
515 /// The workspace it joins.
516 pub workspace_id: Option<String>,
517 /// Whether it is bound to one email address.
518 pub bound: bool,
519}
520
521/// `invite.redeemed`: an invite was used, by a new account or by an
522/// existing one joining a workspace.
523#[derive(Debug, Serialize, serde::Deserialize)]
524#[serde(rename_all = "camelCase")]
525pub struct InviteRedeemed {
526 pub invite_id: String,
527 pub user_id: String,
528 pub inviter_id: Option<String>,
529 pub workspace_id: Option<String>,
530 /// Whether it made the account.
531 pub created_account: bool,
532}
533
534/// `waitlist.requested`: someone asked for access. Ask identity's staff
535/// methods for the entry; the address is not in the event.
536#[derive(Debug, Serialize, serde::Deserialize)]
537#[serde(rename_all = "camelCase")]
538pub struct WaitlistRequested {
539 pub entry_id: String,
540}
541
Agents as a team: lifecycle, merge queue, billing and a new shell542/// `queue.changed`: a repository's merge queue gained, lost or settled an
543/// entry, so the next batch may be ready to test.
544#[derive(Debug, Serialize)]
545#[serde(rename_all = "camelCase")]
546pub struct QueueChanged {
547 pub repo_id: String,
548}
Agents and memory, checks and conflicts, profiles, slug renames, custom domains549
550#[cfg(test)]
551mod tests {
552 use super::*;
553
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily554 #[test]
555 fn a_push_says_it_was_unscanned_only_when_it_was() {
556 let push = |unscanned| GitPush {
557 repo_id: "rep_1".into(),
558 git_ref: "refs/heads/import".into(),
559 before: None,
560 after: "abc".into(),
561 default_branch: false,
562 unscanned,
563 };
564 let quiet = serde_json::to_value(push(false)).unwrap();
565 assert!(quiet.get("unscanned").is_none());
566 let flagged = serde_json::to_value(push(true)).unwrap();
567 assert_eq!(flagged["unscanned"], true);
568 assert_eq!(flagged["ref"], "refs/heads/import");
569 }
570
Agents and memory, checks and conflicts, profiles, slug renames, custom domains571 fn renamed(from: &str, to: &str) -> WorkspaceRenamed {
572 WorkspaceRenamed {
573 workspace_id: "wsp_1".into(),
574 from: from.into(),
575 to: to.into(),
576 }
577 }
578
579 #[test]
580 fn stale_slugs_leave_out_the_current_one() {
581 assert_eq!(renamed("a", "b").stale_slugs("b"), vec!["a"]);
582 // Delivered after a second rename, b → c: both move to c.
583 assert_eq!(renamed("a", "b").stale_slugs("c"), vec!["a", "b"]);
584 // Renamed back: a → b → a.
585 assert_eq!(renamed("a", "b").stale_slugs("a"), vec!["b"]);
586 }
587
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look588 fn transferred(from: &str, to: &str) -> RepoTransferred {
589 RepoTransferred {
590 repo_id: "rep_1".into(),
591 name: "rocket".into(),
592 from: from.into(),
593 to: to.into(),
594 }
595 }
596
597 #[test]
598 fn stale_paths_leave_out_the_current_one() {
599 assert_eq!(transferred("a", "b").stale_paths("b/rocket"), vec!["a/rocket"]);
600 // Delivered after a second transfer, b → c: both move to c.
601 assert_eq!(
602 transferred("a", "b").stale_paths("c/rocket"),
603 vec!["a/rocket", "b/rocket"]
604 );
605 // Transferred back: a → b → a.
606 assert_eq!(transferred("a", "b").stale_paths("a/rocket"), vec!["b/rocket"]);
607 }
608
609 #[test]
610 fn a_transfer_reads_as_published() {
611 let data = serde_json::json!({ "repoId": "rep_1", "name": "rocket", "from": "a", "to": "b" });
612 let event: RepoTransferred = serde_json::from_value(data).unwrap();
613 assert_eq!((event.from.as_str(), event.to.as_str()), ("a", "b"));
614 }
615
616 #[test]
617 fn a_rename_names_both_paths_in_its_workspace() {
618 let renamed = RepoRenamed {
619 repo_id: "rep_1".into(),
620 namespace: "acme".into(),
621 from: "old".into(),
622 to: "new".into(),
623 };
624 assert_eq!(renamed.stale_paths("acme/new"), vec!["acme/old"]);
625 // Delivered after a transfer: both names in acme move.
626 assert_eq!(renamed.stale_paths("flagon/new"), vec!["acme/old", "acme/new"]);
627 }
628
Agents and memory, checks and conflicts, profiles, slug renames, custom domains629 #[test]
630 fn reads_the_published_payload() {
631 let data = serde_json::json!({ "workspaceId": "wsp_1", "from": "a", "to": "b" });
632 let event: WorkspaceRenamed = serde_json::from_value(data).unwrap();
633 assert_eq!((event.from.as_str(), event.to.as_str()), ("a", "b"));
634 }
635}