Skip to content
156 linesCodeBlameRaw

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

Merge branch 'worktree-agent-a8385d293d42c913a'1/// The site's own routes: first path segments that are never a workspace's,
2/// so nobody may register them, and no alias can be reached at them.
3const ROUTES: &[&str] = &[
Workspaces own repositories4 "api",
5 "mcp",
6 "login",
7 "logout",
8 "register",
9 "new",
10 "settings",
11 "search",
12 "admin",
13 "auth",
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look14 // g1t.sh/integrations/github/setup: where GitHub returns after an install.
15 "integrations",
Issues and pull requests replace intents and attempts16 "pulls",
17 "issues",
Workspaces own repositories18 "oauth",
19 "assets",
Workspace names and icons, and a component kit for every control20 "avatars",
Workspaces own repositories21 "docs",
22 "explore",
23 "about",
24 "pricing",
25 "terms",
26 "privacy",
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look27 "policies",
28 "security",
Workspaces own repositories29 "help",
30 "support",
31 "status",
32 "blog",
33 "verify",
Merge email confirmation gate: a code and a link, nothing until confirmed (identity 0036)34 "confirm-email",
Workspaces own repositories35 "forgot",
36 "reset",
37 "device",
38 "workspaces",
39 "u",
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look40 // g1t.sh/invite/<code>: invite links; and the waitlist.
41 "invite",
42 "invites",
43 "waitlist",
Packages, with a container registry on g1t.sh; workspaces deleted whole and kept 30 days; Members for every member44 // g1t.sh/v2/: the container registry; `-` paths hold the others.
45 "v2",
Inbox: a bell in the top bar opens it beside the page, and /inbox holds it all46 // g1t.sh/inbox, and the name it might also go by.
47 "inbox",
48 "notifications",
API and MCP server, Rust identity service, registration, site redesign49];
50
Merge account deletion: soft delete for 30 days, staff restore and purge, ghost for what remains (identity 0037)51/// g1t itself, the name its agent once went by, and `ghost`, who wrote what
52/// a deleted account wrote (`account_deletion::GHOST_USERNAME`): everything
53/// g1t does is shown as `g1t`, so nobody else may be called any of these.
54/// Not routes: staff may point one at a workspace as an alias (identity's
55/// `aliases.rs`).
56const OWN: &[&str] = &["g1t", "g1t-agent", "ghost"];
Merge branch 'worktree-agent-a8385d293d42c913a'57
g1t is one name: its agent's work, commits and comments show as @g1t, and nobody can claim g1t or g1t-agent58/// Whether `value`, whatever its case, is a name nobody can register or
59/// rename a workspace to: a route, or g1t's own.
60pub fn is_reserved_name(value: &str) -> bool {
Merge branch 'worktree-agent-a8385d293d42c913a'61 is_route_name(value) || OWN.iter().any(|own| own.eq_ignore_ascii_case(value.trim()))
62}
63
64/// Whether `value`, whatever its case, is one of the site's own routes.
65pub fn is_route_name(value: &str) -> bool {
g1t is one name: its agent's work, commits and comments show as @g1t, and nobody can claim g1t or g1t-agent66 let value = value.trim();
Merge branch 'worktree-agent-a8385d293d42c913a'67 ROUTES.iter().any(|route| route.eq_ignore_ascii_case(value))
68}
69
70/// Whether `value` has a namespace's shape: letters, digits and single
71/// hyphens, not starting or ending with a hyphen, at most 39 characters.
72/// Reserved names have it too; see [`is_valid_namespace`].
73pub fn is_namespace_shaped(value: &str) -> bool {
74 let bytes = value.as_bytes();
75 !bytes.is_empty()
76 && bytes.len() <= 39
77 && bytes
78 .iter()
79 .all(|byte| byte.is_ascii_lowercase() || byte.is_ascii_digit() || *byte == b'-')
80 && !value.starts_with('-')
81 && !value.ends_with('-')
82 && !value.contains("--")
g1t is one name: its agent's work, commits and comments show as @g1t, and nobody can claim g1t or g1t-agent83}
84
Merge branch 'worktree-agent-a8385d293d42c913a'85/// A workspace alias as staff typed it, trimmed and lowercased, if it can
86/// be one: shaped like a namespace and not one of the site's routes, which
87/// would always answer first. Reserved names such as `g1t` can be; whether
88/// a person or workspace already has it is identity's to check.
89pub fn aliasable_name(value: &str) -> Option<String> {
90 let value = value.trim().to_lowercase();
91 (is_namespace_shaped(&value) && !is_route_name(&value)).then_some(value)
92}
93
g1t is one name: its agent's work, commits and comments show as @g1t, and nobody can claim g1t or g1t-agent94/// A username or workspace slug as someone typed it, trimmed and
95/// lowercased, if it can be registered: what signing up, signing up with
96/// GitHub and creating a workspace each take. None when it is malformed or
97/// reserved.
98pub fn claimable_namespace(value: &str) -> Option<String> {
99 let value = value.trim().to_lowercase();
100 is_valid_namespace(&value).then_some(value)
101}
102
API and MCP server, Rust identity service, registration, site redesign103/// Namespaces follow GitHub's rules: letters, digits and single hyphens,
104/// not starting or ending with a hyphen, at most 39 characters.
105pub fn is_valid_namespace(value: &str) -> bool {
Merge branch 'worktree-agent-a8385d293d42c913a'106 is_namespace_shaped(value) && !is_reserved_name(value)
API and MCP server, Rust identity service, registration, site redesign107}
108
109pub fn is_valid_repo_name(value: &str) -> bool {
110 !value.is_empty()
111 && value.len() <= 100
112 && value.bytes().all(|byte| {
113 byte.is_ascii_lowercase() || byte.is_ascii_digit() || matches!(byte, b'.' | b'_' | b'-')
114 })
115 && !value.starts_with('.')
116 && !value.ends_with(".git")
Agents as a team: lifecycle, merge queue, billing and a new shell117 // `/<workspace>/-/…` holds the workspace's own pages.
118 && value != "-"
API and MCP server, Rust identity service, registration, site redesign119}
g1t is one name: its agent's work, commits and comments show as @g1t, and nobody can claim g1t or g1t-agent120
121#[cfg(test)]
122mod tests {
123 use super::*;
124
125 #[test]
126 fn g1ts_own_names_are_reserved_in_any_case() {
127 for name in ["g1t", "G1T", "g1t-agent", "G1T-Agent", " g1t "] {
128 assert!(is_reserved_name(name), "{name}");
129 assert_eq!(claimable_namespace(name), None, "{name}");
130 }
131 assert!(!is_valid_namespace("g1t"));
132 assert!(!is_valid_namespace("g1t-agent"));
Merge account deletion: soft delete for 30 days, staff restore and purge, ghost for what remains (identity 0037)133 assert!(!is_valid_namespace("ghost"));
134 assert_eq!(claimable_namespace(" Ghost "), None);
g1t is one name: its agent's work, commits and comments show as @g1t, and nobody can claim g1t or g1t-agent135 }
136
137 #[test]
138 fn names_near_g1ts_are_anyones() {
139 assert!(!is_reserved_name("g1t-agents"));
140 assert_eq!(claimable_namespace("G1T-Bot").as_deref(), Some("g1t-bot"));
141 assert_eq!(claimable_namespace(" Ana ").as_deref(), Some("ana"));
142 assert_eq!(claimable_namespace("an--a"), None);
143 }
Merge branch 'worktree-agent-a8385d293d42c913a'144
145 #[test]
146 fn g1t_can_be_an_alias_but_a_route_cannot() {
147 assert_eq!(aliasable_name(" G1T ").as_deref(), Some("g1t"));
148 assert_eq!(aliasable_name("acme-corp").as_deref(), Some("acme-corp"));
149 for name in ["settings", "api", "login", "Explore", "-acme", "ac--me", "acme_inc", "", "a.b"] {
150 assert_eq!(aliasable_name(name), None, "{name}");
151 }
152 assert_eq!(aliasable_name(&"a".repeat(40)), None);
153 // Still nobody's to register.
154 assert!(is_reserved_name("g1t") && !is_route_name("g1t"));
155 }
g1t is one name: its agent's work, commits and comments show as @g1t, and nobody can claim g1t or g1t-agent156}

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