Skip to content
158 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",
Merge workspace invitations: nobody joins a workspace without saying yes, people are found by username, your own invites can bring someone in, and nobody is left without a workspace (identity 0040)44 // g1t.sh/invitations: the workspace invitations waiting for an answer.
45 "invitations",
Packages, with a container registry on g1t.sh; workspaces deleted whole and kept 30 days; Members for every member46 // g1t.sh/v2/: the container registry; `-` paths hold the others.
47 "v2",
Inbox: a bell in the top bar opens it beside the page, and /inbox holds it all48 // g1t.sh/inbox, and the name it might also go by.
49 "inbox",
50 "notifications",
API and MCP server, Rust identity service, registration, site redesign51];
52
Merge account deletion: soft delete for 30 days, staff restore and purge, ghost for what remains (identity 0037)53/// g1t itself, the name its agent once went by, and `ghost`, who wrote what
54/// a deleted account wrote (`account_deletion::GHOST_USERNAME`): everything
55/// g1t does is shown as `g1t`, so nobody else may be called any of these.
56/// Not routes: staff may point one at a workspace as an alias (identity's
57/// `aliases.rs`).
58const OWN: &[&str] = &["g1t", "g1t-agent", "ghost"];
Merge branch 'worktree-agent-a8385d293d42c913a'59
g1t is one name: its agent's work, commits and comments show as @g1t, and nobody can claim g1t or g1t-agent60/// Whether `value`, whatever its case, is a name nobody can register or
61/// rename a workspace to: a route, or g1t's own.
62pub fn is_reserved_name(value: &str) -> bool {
Merge branch 'worktree-agent-a8385d293d42c913a'63 is_route_name(value) || OWN.iter().any(|own| own.eq_ignore_ascii_case(value.trim()))
64}
65
66/// Whether `value`, whatever its case, is one of the site's own routes.
67pub 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-agent68 let value = value.trim();
Merge branch 'worktree-agent-a8385d293d42c913a'69 ROUTES.iter().any(|route| route.eq_ignore_ascii_case(value))
70}
71
72/// Whether `value` has a namespace's shape: letters, digits and single
73/// hyphens, not starting or ending with a hyphen, at most 39 characters.
74/// Reserved names have it too; see [`is_valid_namespace`].
75pub fn is_namespace_shaped(value: &str) -> bool {
76 let bytes = value.as_bytes();
77 !bytes.is_empty()
78 && bytes.len() <= 39
79 && bytes
80 .iter()
81 .all(|byte| byte.is_ascii_lowercase() || byte.is_ascii_digit() || *byte == b'-')
82 && !value.starts_with('-')
83 && !value.ends_with('-')
84 && !value.contains("--")
g1t is one name: its agent's work, commits and comments show as @g1t, and nobody can claim g1t or g1t-agent85}
86
Merge branch 'worktree-agent-a8385d293d42c913a'87/// A workspace alias as staff typed it, trimmed and lowercased, if it can
88/// be one: shaped like a namespace and not one of the site's routes, which
89/// would always answer first. Reserved names such as `g1t` can be; whether
90/// a person or workspace already has it is identity's to check.
91pub fn aliasable_name(value: &str) -> Option<String> {
92 let value = value.trim().to_lowercase();
93 (is_namespace_shaped(&value) && !is_route_name(&value)).then_some(value)
94}
95
g1t is one name: its agent's work, commits and comments show as @g1t, and nobody can claim g1t or g1t-agent96/// A username or workspace slug as someone typed it, trimmed and
97/// lowercased, if it can be registered: what signing up, signing up with
98/// GitHub and creating a workspace each take. None when it is malformed or
99/// reserved.
100pub fn claimable_namespace(value: &str) -> Option<String> {
101 let value = value.trim().to_lowercase();
102 is_valid_namespace(&value).then_some(value)
103}
104
API and MCP server, Rust identity service, registration, site redesign105/// Namespaces follow GitHub's rules: letters, digits and single hyphens,
106/// not starting or ending with a hyphen, at most 39 characters.
107pub fn is_valid_namespace(value: &str) -> bool {
Merge branch 'worktree-agent-a8385d293d42c913a'108 is_namespace_shaped(value) && !is_reserved_name(value)
API and MCP server, Rust identity service, registration, site redesign109}
110
111pub fn is_valid_repo_name(value: &str) -> bool {
112 !value.is_empty()
113 && value.len() <= 100
114 && value.bytes().all(|byte| {
115 byte.is_ascii_lowercase() || byte.is_ascii_digit() || matches!(byte, b'.' | b'_' | b'-')
116 })
117 && !value.starts_with('.')
118 && !value.ends_with(".git")
Agents as a team: lifecycle, merge queue, billing and a new shell119 // `/<workspace>/-/…` holds the workspace's own pages.
120 && value != "-"
API and MCP server, Rust identity service, registration, site redesign121}
g1t is one name: its agent's work, commits and comments show as @g1t, and nobody can claim g1t or g1t-agent122
123#[cfg(test)]
124mod tests {
125 use super::*;
126
127 #[test]
128 fn g1ts_own_names_are_reserved_in_any_case() {
129 for name in ["g1t", "G1T", "g1t-agent", "G1T-Agent", " g1t "] {
130 assert!(is_reserved_name(name), "{name}");
131 assert_eq!(claimable_namespace(name), None, "{name}");
132 }
133 assert!(!is_valid_namespace("g1t"));
134 assert!(!is_valid_namespace("g1t-agent"));
Merge account deletion: soft delete for 30 days, staff restore and purge, ghost for what remains (identity 0037)135 assert!(!is_valid_namespace("ghost"));
136 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-agent137 }
138
139 #[test]
140 fn names_near_g1ts_are_anyones() {
141 assert!(!is_reserved_name("g1t-agents"));
142 assert_eq!(claimable_namespace("G1T-Bot").as_deref(), Some("g1t-bot"));
143 assert_eq!(claimable_namespace(" Ana ").as_deref(), Some("ana"));
144 assert_eq!(claimable_namespace("an--a"), None);
145 }
Merge branch 'worktree-agent-a8385d293d42c913a'146
147 #[test]
148 fn g1t_can_be_an_alias_but_a_route_cannot() {
149 assert_eq!(aliasable_name(" G1T ").as_deref(), Some("g1t"));
150 assert_eq!(aliasable_name("acme-corp").as_deref(), Some("acme-corp"));
151 for name in ["settings", "api", "login", "Explore", "-acme", "ac--me", "acme_inc", "", "a.b"] {
152 assert_eq!(aliasable_name(name), None, "{name}");
153 }
154 assert_eq!(aliasable_name(&"a".repeat(40)), None);
155 // Still nobody's to register.
156 assert!(is_reserved_name("g1t") && !is_route_name("g1t"));
157 }
g1t is one name: its agent's work, commits and comments show as @g1t, and nobody can claim g1t or g1t-agent158}

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