flagon-io/g1t

public

Where people and agents ship software together. The open-source git platform for the whole job: issues, agents, checks and deploys to the edge.

g1t/crates/contracts/src/events.rs

623 lines21,612 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,
Issues and pull requests replace intents and attempts82 #[serde(skip_serializing_if = "Option::is_none")]
83 pub title: Option<String>,
84 /// On close: `completed` or `not_planned`.
85 #[serde(skip_serializing_if = "Option::is_none")]
86 pub reason: Option<&'static str>,
87 /// On close: the number of the pull request whose merge closed it.
88 #[serde(skip_serializing_if = "Option::is_none")]
89 pub resolved_by: Option<u32>,
Agents as a team: lifecycle, merge queue, billing and a new shell90 /// On `issue.assigned`: the people it is now assigned to.
91 #[serde(skip_serializing_if = "Option::is_none")]
92 pub assignees: Option<Vec<String>>,
Work service in Rust, with RFC 3339 timestamps93}
94
Acceptance checks in sandboxes, line comments and review verdicts95/// The payload of `pull.opened`, `pull.ready`, `pull.updated` (its head
96/// moved), `pull.closed` and `pull.merged`; each uses the fields that apply to it.
Work service in Rust, with RFC 3339 timestamps97#[derive(Debug, Default, Serialize)]
98#[serde(rename_all = "camelCase")]
Issues and pull requests replace intents and attempts99pub struct PullEvent {
100 pub pull_id: String,
Work service in Rust, with RFC 3339 timestamps101 pub repo_id: String,
Issues and pull requests replace intents and attempts102 pub number: u32,
103 /// The number of the issue it is for.
104 #[serde(skip_serializing_if = "Option::is_none")]
105 pub issue: Option<u32>,
Work service in Rust, with RFC 3339 timestamps106 #[serde(skip_serializing_if = "Option::is_none")]
107 pub agent: Option<String>,
Workflows run when an agent's pull request is marked ready108 /// On merge: the commit the branch now points to. On update and when
109 /// marked ready: the head of the change.
Work service in Rust, with RFC 3339 timestamps110 #[serde(skip_serializing_if = "Option::is_none")]
Issues and pull requests replace intents and attempts111 pub commit: Option<String>,
112 /// On close: the pull request that was merged instead.
Work service in Rust, with RFC 3339 timestamps113 #[serde(skip_serializing_if = "Option::is_none")]
Issues and pull requests replace intents and attempts114 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 step115 /// How sure g1t is of a g1t agent's change, once it has worked that out.
116 #[serde(skip_serializing_if = "Option::is_none")]
117 pub confidence: Option<crate::work::Confidence>,
Work service in Rust, with RFC 3339 timestamps118}
119
Acceptance checks in sandboxes, line comments and review verdicts120/// `checks.completed`: a run of an issue's acceptance checks against a pull
121/// request finished.
122#[derive(Debug, Serialize)]
123#[serde(rename_all = "camelCase")]
124pub struct ChecksEvent {
125 pub pull_id: String,
126 pub repo_id: String,
127 pub number: u32,
128 /// `passed`, `failed` or `errored`.
129 pub status: &'static str,
130 /// The commit that was checked.
131 pub commit: String,
132}
133
Actions: workflow_run, workflow.completed, artifacts on the run page, Node 24134/// `workflow.completed`: a GitHub Actions run finished.
135#[derive(Debug, Serialize)]
136#[serde(rename_all = "camelCase")]
137pub struct WorkflowEvent {
138 pub run_id: String,
139 pub repo_id: String,
140 /// The workflow's name, and its file.
141 pub workflow: String,
142 pub path: String,
143 /// The run's number among the workflow's runs.
144 pub number: u64,
145 /// The GitHub event that started it, such as `push`.
146 pub event: String,
147 /// `success`, `failure`, `cancelled` or `skipped`.
148 pub conclusion: String,
149 #[serde(rename = "ref")]
150 pub git_ref: String,
151 pub sha: String,
152 /// The pull request it ran for, if any.
153 #[serde(skip_serializing_if = "Option::is_none")]
154 pub pull: Option<u32>,
155}
156
Agents as a team: lifecycle, merge queue, billing and a new shell157/// `review.completed`: a g1t agent finished reviewing a pull request, or
158/// could not.
159#[derive(Debug, Serialize)]
160#[serde(rename_all = "camelCase")]
161pub struct ReviewEvent {
162 pub pull_id: String,
163 pub repo_id: String,
164 pub number: u32,
165 /// `approve` or `request_changes`; absent when no review was written.
166 #[serde(skip_serializing_if = "Option::is_none")]
167 pub verdict: Option<&'static str>,
168}
169
Issues and pull requests replace intents and attempts170/// `comment.created`. `number` is the issue or pull request commented on.
Work service in Rust, with RFC 3339 timestamps171#[derive(Debug, Serialize)]
172#[serde(rename_all = "camelCase")]
Issues and pull requests replace intents and attempts173pub struct CommentCreated {
174 pub comment_id: String,
175 pub repo_id: String,
176 pub number: u32,
Agents as a team: lifecycle, merge queue, billing and a new shell177 /// Set when the comment is on a pull request.
178 #[serde(skip_serializing_if = "Option::is_none")]
179 pub pull_id: Option<String>,
180 /// Set when the comment is a review: approve or request changes.
181 #[serde(skip_serializing_if = "Option::is_none")]
182 pub verdict: Option<crate::work::Verdict>,
Issues and pull requests replace intents and attempts183}
184
185#[derive(Debug, Serialize)]
186#[serde(rename_all = "camelCase")]
Work service in Rust, with RFC 3339 timestamps187pub struct SessionAppended {
Issues and pull requests replace intents and attempts188 pub pull_id: String,
189 pub repo_id: String,
190 pub number: u32,
Work service in Rust, with RFC 3339 timestamps191 pub count: u32,
192}
193
Events service in Rust, with RFC 3339 times and accurate push events194/// An event as stored in the log and delivered to subscribers. `data` is
195/// left as JSON; each reader decodes the types it cares about.
196#[derive(Clone, Debug, Serialize, serde::Deserialize)]
197#[serde(rename_all = "camelCase")]
198pub struct Event {
199 /// Sorts by the time the event was published.
200 pub id: String,
201 #[serde(rename = "type")]
202 pub kind: String,
203 /// The service that published it.
204 pub source: String,
205 /// RFC 3339.
206 pub time: String,
207 /// The repo the event concerns.
208 pub repo_id: Option<String>,
209 /// The user or agent that caused it, if any.
210 pub actor: Option<String>,
211 pub data: serde_json::Value,
212}
213
214/// `publish`, as a publisher sends it. Returns nothing.
215#[derive(Debug, Serialize)]
216pub struct Publish<T: Serialize> {
217 pub events: Vec<NewEvent<T>>,
218}
219
220/// `publish`, as the events service reads it.
221#[derive(Debug, serde::Deserialize)]
222pub struct PublishArgs {
223 pub events: Vec<Published>,
224}
225
226/// A [`NewEvent`] of any type, as received.
Work service in Rust, with RFC 3339 timestamps227#[derive(Debug, serde::Deserialize)]
228#[serde(rename_all = "camelCase")]
Events service in Rust, with RFC 3339 times and accurate push events229pub struct Published {
Work service in Rust, with RFC 3339 timestamps230 #[serde(rename = "type")]
231 pub kind: String,
Events service in Rust, with RFC 3339 times and accurate push events232 pub source: String,
233 #[serde(default)]
Work service in Rust, with RFC 3339 timestamps234 pub repo_id: Option<String>,
Events service in Rust, with RFC 3339 times and accurate push events235 #[serde(default)]
236 pub actor: Option<String>,
Work service in Rust, with RFC 3339 timestamps237 pub data: serde_json::Value,
238}
Events service in Rust, with RFC 3339 times and accurate push events239
240/// `list`: events from the log, newest first. Returns `Vec<Event>`.
241#[derive(Debug, Default, Serialize, serde::Deserialize)]
242#[serde(rename_all = "camelCase")]
243pub struct ListArgs {
244 #[serde(default)]
245 pub repo_id: Option<String>,
246 /// Only these types; all types when empty.
247 #[serde(default)]
248 pub types: Vec<String>,
249 /// Only events older than this event id.
250 #[serde(default)]
251 pub before: Option<String>,
252 #[serde(default)]
253 pub limit: Option<u32>,
254}
Agents as a team: lifecycle, merge queue, billing and a new shell255
Agents and memory, checks and conflicts, profiles, slug renames, custom domains256/// `workspace.renamed`: a workspace's slug changed from `from` to `to`.
257/// Every service that stores a slug moves its rows to the workspace's
258/// *current* slug (ask identity by `workspace_id`), so that a repeated or
259/// late delivery after a second rename still lands in the right place.
260#[derive(Clone, Debug, Serialize, serde::Deserialize)]
261#[serde(rename_all = "camelCase")]
262pub struct WorkspaceRenamed {
263 pub workspace_id: String,
264 pub from: String,
265 pub to: String,
266}
267
268impl WorkspaceRenamed {
269 /// The slugs whose rows move to `current`: the two this rename names,
270 /// minus `current` itself. Moving rows keyed by either converges on the
271 /// current slug whatever order renames are delivered in.
272 pub fn stale_slugs(&self, current: &str) -> Vec<String> {
273 let mut slugs: Vec<String> = Vec::new();
274 for slug in [&self.from, &self.to] {
275 if slug != current && !slugs.contains(slug) {
276 slugs.push(slug.clone());
277 }
278 }
279 slugs
280 }
281}
282
Search across all of g1t, Explore, and a command palette283/// `repo.updated`: a repository's description, topics or visibility
284/// changed. `visibility_changed` says whether it went public or private,
285/// which `repo.visibility_changed` also announces on its own.
286#[derive(Debug, Serialize, serde::Deserialize)]
287#[serde(rename_all = "camelCase")]
288pub struct RepoUpdated {
289 pub repo_id: String,
290 pub namespace: String,
291 pub name: String,
292 pub is_private: bool,
293 #[serde(default)]
294 pub visibility_changed: bool,
295}
296
297/// `repo.visibility_changed`: a repository went public or private.
298#[derive(Debug, Serialize, serde::Deserialize)]
299#[serde(rename_all = "camelCase")]
300pub struct RepoVisibilityChanged {
301 pub repo_id: String,
302 pub is_private: bool,
303}
304
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look305/// `repo.renamed`: a repository's name changed within its workspace,
306/// keeping its id and its git store key. Like `repo.transferred`, a path
307/// change: every service that keeps rows under the repository's path moves
308/// them to its *current* path (ask repos `path_by_id`), so a repeated or
309/// late delivery after a second rename or a transfer still lands in the
310/// right place. `g1t_kit::transfer::on_event` handles both.
311#[derive(Clone, Debug, Serialize, serde::Deserialize)]
Search across all of g1t, Explore, and a command palette312#[serde(rename_all = "camelCase")]
313pub struct RepoRenamed {
314 pub repo_id: String,
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look315 /// The workspace it is in.
Search across all of g1t, Explore, and a command palette316 pub namespace: String,
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look317 /// Its old name.
318 pub from: String,
319 /// Its new name.
320 pub to: String,
321}
322
323impl RepoRenamed {
324 /// The paths whose rows move to `current` (`namespace/name`): the two
325 /// this rename names, minus `current`.
326 pub fn stale_paths(&self, current: &str) -> Vec<String> {
327 let mut paths: Vec<String> = Vec::new();
328 for name in [&self.from, &self.to] {
329 let path = format!("{}/{name}", self.namespace);
330 if path != current && !paths.contains(&path) {
331 paths.push(path);
332 }
333 }
334 paths
335 }
336}
337
338/// `repo.deleted`: a repository was deleted. It is hidden everywhere and
339/// git refuses it, but it can be restored until `purge_after`, so services
340/// stop what runs for it (agents, workflows, deployments, indexing,
341/// webhook deliveries) and hide it, and keep what they hold until
342/// `repo.purged`. `repo.restored` brings it back.
343#[derive(Clone, Debug, Serialize, serde::Deserialize)]
344#[serde(rename_all = "camelCase")]
345pub struct RepoDeleted {
346 pub repo_id: String,
347 #[serde(default)]
348 pub namespace: String,
349 #[serde(default)]
Search across all of g1t, Explore, and a command palette350 pub name: String,
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look351 #[serde(default)]
352 pub is_private: bool,
353 /// RFC 3339: when it is purged unless restored first.
354 #[serde(default)]
355 pub purge_after: String,
Search across all of g1t, Explore, and a command palette356}
357
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look358/// `repo.restored`: a deleted repository is back, at its path, as it was.
359/// Services start again what `repo.deleted` stopped: index it, deploy its
360/// production, show it.
361#[derive(Clone, Debug, Serialize, serde::Deserialize)]
362#[serde(rename_all = "camelCase")]
363pub struct RepoRestored {
364 pub repo_id: String,
365 pub namespace: String,
366 pub name: String,
367 pub is_private: bool,
368}
369
370/// `repo.purged`: a deleted repository is gone for good, its git data
371/// with it. Services drop every row they keep for it by `repo_id`, except
372/// history that belongs to its workspace: ledgers, invoices and the audit
373/// log. Its path is free for a new repository.
374#[derive(Clone, Debug, Serialize, serde::Deserialize)]
375#[serde(rename_all = "camelCase")]
376pub struct RepoPurged {
377 pub repo_id: String,
378 pub namespace: String,
379 pub name: String,
380}
381
382/// `repo.archived` and `repo.unarchived`: a repository became read-only,
383/// or writable again. While archived, pushes are refused, issues and pull
384/// requests are locked, and agents and workflows do not run for it; its
385/// deployments keep serving.
386#[derive(Clone, Debug, Serialize, serde::Deserialize)]
387#[serde(rename_all = "camelCase")]
388pub struct RepoArchived {
389 pub repo_id: String,
390 pub namespace: String,
391 pub name: String,
392 pub archived: bool,
393}
394
395/// `repo.default_branch_changed`: the branch everything lands on is now
396/// `to`. `renamed` says whether `from` was renamed to `to` (open pull
397/// requests into it now target `to`) rather than another branch chosen.
398#[derive(Clone, Debug, Serialize, serde::Deserialize)]
399#[serde(rename_all = "camelCase")]
400pub struct RepoDefaultBranchChanged {
401 pub repo_id: String,
402 pub from: String,
403 pub to: String,
404 #[serde(default)]
405 pub renamed: bool,
406}
407
408/// `branch.renamed`: a branch was renamed. Pull requests from or into
409/// `from` follow it to `to`, and web addresses that name `from` redirect.
410#[derive(Clone, Debug, Serialize, serde::Deserialize)]
411#[serde(rename_all = "camelCase")]
412pub struct BranchRenamed {
413 pub repo_id: String,
414 pub from: String,
415 pub to: String,
416 /// Whether it is the default branch.
417 #[serde(default)]
418 pub default_branch: bool,
419}
420
421/// `repo.transferred`: a repository moved from one workspace to another,
422/// keeping its id and its name. Every service that keeps a repository
423/// under its path (`namespace/name`) or its workspace's slug moves those
424/// rows to the repository's *current* path (ask repos `path_by_id`), so a
425/// repeated or late delivery after a second transfer still lands in the
426/// right place. What was charged or recorded before the transfer stays
427/// with the workspace it happened in.
428#[derive(Clone, Debug, Serialize, serde::Deserialize)]
Search across all of g1t, Explore, and a command palette429#[serde(rename_all = "camelCase")]
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look430pub struct RepoTransferred {
Search across all of g1t, Explore, and a command palette431 pub repo_id: String,
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look432 pub name: String,
433 /// The workspace it left.
434 pub from: String,
435 /// The workspace it went to.
436 pub to: String,
Search across all of g1t, Explore, and a command palette437}
438
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look439impl RepoTransferred {
440 /// The paths whose rows move to `current` (`namespace/name`): the two
441 /// this transfer names, minus `current`. Moving rows keyed by either
442 /// converges whatever order transfers are delivered in.
443 pub fn stale_paths(&self, current: &str) -> Vec<String> {
444 let mut paths: Vec<String> = Vec::new();
445 for namespace in [&self.from, &self.to] {
446 let path = format!("{namespace}/{}", self.name);
447 if path != current && !paths.contains(&path) {
448 paths.push(path);
449 }
450 }
451 paths
452 }
453}
454
455/// `workspace.deleted`: a workspace is gone. Services drop what they keep
456/// for it alone (its webhooks, integrations, secrets, memory, guardrails,
457/// agent queue) and keep what is history: ledgers, invoices and the audit
458/// log stay under its slug, which is never given to another workspace.
459#[derive(Clone, Debug, Serialize, serde::Deserialize)]
460#[serde(rename_all = "camelCase")]
461pub struct WorkspaceDeleted {
462 pub workspace_id: String,
463 pub slug: String,
464}
465
Search across all of g1t, Explore, and a command palette466/// `user.updated`: an account was made, or changed what its profile shows
467/// (name, bio, avatar). Nothing private: ask identity for the profile.
468#[derive(Debug, Serialize, serde::Deserialize)]
469#[serde(rename_all = "camelCase")]
470pub struct UserUpdated {
471 pub username: String,
472}
473
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look474/// `user.email_added`, `user.email_verified`, `user.email_removed` and
475/// `user.primary_email_changed`: an account's addresses changed. Never the
476/// address itself; ask identity, as the person, for that.
477#[derive(Debug, Serialize, serde::Deserialize)]
478#[serde(rename_all = "camelCase")]
479pub struct UserEmailChanged {
480 pub user_id: String,
481 /// Whether g1t staff made the change.
482 #[serde(default)]
483 pub by_staff: bool,
484}
485
Search across all of g1t, Explore, and a command palette486/// `workspace.updated`: a workspace was made, or its name, description or
487/// icon changed. Ask identity for it by slug.
488#[derive(Debug, Serialize, serde::Deserialize)]
489#[serde(rename_all = "camelCase")]
490pub struct WorkspaceUpdated {
491 pub workspace_id: String,
492 pub slug: String,
493}
494
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look495/// `invite.created`: someone (or staff) made an invite. Never the code or
496/// the address it is for.
497#[derive(Debug, Serialize, serde::Deserialize)]
498#[serde(rename_all = "camelCase")]
499pub struct InviteCreated {
500 pub invite_id: String,
501 /// The account that made it; null when staff did.
502 pub inviter_id: Option<String>,
503 /// The workspace it joins.
504 pub workspace_id: Option<String>,
505 /// Whether it is bound to one email address.
506 pub bound: bool,
507}
508
509/// `invite.redeemed`: an invite was used, by a new account or by an
510/// existing one joining a workspace.
511#[derive(Debug, Serialize, serde::Deserialize)]
512#[serde(rename_all = "camelCase")]
513pub struct InviteRedeemed {
514 pub invite_id: String,
515 pub user_id: String,
516 pub inviter_id: Option<String>,
517 pub workspace_id: Option<String>,
518 /// Whether it made the account.
519 pub created_account: bool,
520}
521
522/// `waitlist.requested`: someone asked for access. Ask identity's staff
523/// methods for the entry; the address is not in the event.
524#[derive(Debug, Serialize, serde::Deserialize)]
525#[serde(rename_all = "camelCase")]
526pub struct WaitlistRequested {
527 pub entry_id: String,
528}
529
Agents as a team: lifecycle, merge queue, billing and a new shell530/// `queue.changed`: a repository's merge queue gained, lost or settled an
531/// entry, so the next batch may be ready to test.
532#[derive(Debug, Serialize)]
533#[serde(rename_all = "camelCase")]
534pub struct QueueChanged {
535 pub repo_id: String,
536}
Agents and memory, checks and conflicts, profiles, slug renames, custom domains537
538#[cfg(test)]
539mod tests {
540 use super::*;
541
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily542 #[test]
543 fn a_push_says_it_was_unscanned_only_when_it_was() {
544 let push = |unscanned| GitPush {
545 repo_id: "rep_1".into(),
546 git_ref: "refs/heads/import".into(),
547 before: None,
548 after: "abc".into(),
549 default_branch: false,
550 unscanned,
551 };
552 let quiet = serde_json::to_value(push(false)).unwrap();
553 assert!(quiet.get("unscanned").is_none());
554 let flagged = serde_json::to_value(push(true)).unwrap();
555 assert_eq!(flagged["unscanned"], true);
556 assert_eq!(flagged["ref"], "refs/heads/import");
557 }
558
Agents and memory, checks and conflicts, profiles, slug renames, custom domains559 fn renamed(from: &str, to: &str) -> WorkspaceRenamed {
560 WorkspaceRenamed {
561 workspace_id: "wsp_1".into(),
562 from: from.into(),
563 to: to.into(),
564 }
565 }
566
567 #[test]
568 fn stale_slugs_leave_out_the_current_one() {
569 assert_eq!(renamed("a", "b").stale_slugs("b"), vec!["a"]);
570 // Delivered after a second rename, b → c: both move to c.
571 assert_eq!(renamed("a", "b").stale_slugs("c"), vec!["a", "b"]);
572 // Renamed back: a → b → a.
573 assert_eq!(renamed("a", "b").stale_slugs("a"), vec!["b"]);
574 }
575
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look576 fn transferred(from: &str, to: &str) -> RepoTransferred {
577 RepoTransferred {
578 repo_id: "rep_1".into(),
579 name: "rocket".into(),
580 from: from.into(),
581 to: to.into(),
582 }
583 }
584
585 #[test]
586 fn stale_paths_leave_out_the_current_one() {
587 assert_eq!(transferred("a", "b").stale_paths("b/rocket"), vec!["a/rocket"]);
588 // Delivered after a second transfer, b → c: both move to c.
589 assert_eq!(
590 transferred("a", "b").stale_paths("c/rocket"),
591 vec!["a/rocket", "b/rocket"]
592 );
593 // Transferred back: a → b → a.
594 assert_eq!(transferred("a", "b").stale_paths("a/rocket"), vec!["b/rocket"]);
595 }
596
597 #[test]
598 fn a_transfer_reads_as_published() {
599 let data = serde_json::json!({ "repoId": "rep_1", "name": "rocket", "from": "a", "to": "b" });
600 let event: RepoTransferred = serde_json::from_value(data).unwrap();
601 assert_eq!((event.from.as_str(), event.to.as_str()), ("a", "b"));
602 }
603
604 #[test]
605 fn a_rename_names_both_paths_in_its_workspace() {
606 let renamed = RepoRenamed {
607 repo_id: "rep_1".into(),
608 namespace: "acme".into(),
609 from: "old".into(),
610 to: "new".into(),
611 };
612 assert_eq!(renamed.stale_paths("acme/new"), vec!["acme/old"]);
613 // Delivered after a transfer: both names in acme move.
614 assert_eq!(renamed.stale_paths("flagon/new"), vec!["acme/old", "acme/new"]);
615 }
616
Agents and memory, checks and conflicts, profiles, slug renames, custom domains617 #[test]
618 fn reads_the_published_payload() {
619 let data = serde_json::json!({ "workspaceId": "wsp_1", "from": "a", "to": "b" });
620 let event: WorkspaceRenamed = serde_json::from_value(data).unwrap();
621 assert_eq!((event.from.as_str(), event.to.as_str()), ("a", "b"));
622 }
623}