Skip to content

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.sh
183 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.

The contracts no longer describe g1t's own agents, and workspaces can keep their AI providers. Every consumer had already stopped using them, so the TypeScript and Rust contracts drop the workspace-agent, ability, look, skill-library, context, runner-agent and mention modules, the chat and docs RPCs agents posted and read through, the folio agent methods, the agent_waiting notification, agent.asked and memory.changed events, AI credit, token and gateway billing types, model routes and sessions, team agents, agent tokens, plan, review, message and confidence types, command rules, cost caps and confidence rules. The agents:run and models:* scopes are no longer offered by any preset, OAuth default or permission form, but a stored token or grant that names them still reads; and the removed operations are gone from the scope table, so the API's check that every operation has exactly one scope is strict again. What stored data needs in order to parse (old run and credential kinds, an agent team lead, retired rules and webhook events, old guardrail and integration settings) still parses, and a run's token is still PrincipalKind::Agent, which now means a run's token and the @g1t rows written before. The customize, marketplace, skills and claude-plugin contracts stay only until the mobile app drops its Customize screens.1//! People: a workspace's directory of the people in it, and what each
2//! person's place is: a title and what they own. Kept by identity beside
3//! membership.
People and teams are front and centre: one directory of people and agents with presence, local time, titles, teams and what each owns; profiles with manager and reports and the agents they work with; an org chart with each team's agents beside the person who leads it; and teams of any mix, with a lead, a channel, a budget agents keep to and the agents on them. Every agent is told its teams each turn (who leads, who owns what, who's around and who to page), and the team page shows exactly what. Member management is Members and invites; the people and teams guide says how.4//!
5//! **Profiles.** A member's title and what they own are theirs to write,
Merge branch 'rm-orgchart-getstarted'6//! and the workspace's owners can write anyone's.
People and teams are front and centre: one directory of people and agents with presence, local time, titles, teams and what each owns; profiles with manager and reports and the agents they work with; an org chart with each team's agents beside the person who leads it; and teams of any mix, with a lead, a channel, a budget agents keep to and the agents on them. Every agent is told its teams each turn (who leads, who owns what, who's around and who to page), and the team page shows exactly what. Member management is Members and invites; the people and teams guide says how.7//!
8//! Every method is served by identity at `POST /rpc/<method>`.
9
10use serde::{Deserialize, Serialize};
11
12use crate::teams::{TeamChannel, TeamLead, TeamRef, TeamRole, TeamVisibility};
13use crate::{Role, User};
14
15/// The longest title.
16pub const MAX_TITLE_LENGTH: usize = 80;
17/// The most things one person owns, as their profile lists them.
18pub const MAX_OWNS: usize = 8;
19/// The longest of them.
20pub const MAX_OWNS_LENGTH: usize = 60;
21
Merge branch 'rm-orgchart-getstarted'22/// One member, as the directory and their profile show them.
People and teams are front and centre: one directory of people and agents with presence, local time, titles, teams and what each owns; profiles with manager and reports and the agents they work with; an org chart with each team's agents beside the person who leads it; and teams of any mix, with a lead, a channel, a budget agents keep to and the agents on them. Every agent is told its teams each turn (who leads, who owns what, who's around and who to page), and the team page shows exactly what. Member management is Members and invites; the people and teams guide says how.23#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
24pub struct DirectoryPerson {
25 pub user_id: String,
26 pub username: String,
27 /// The username as its owner wrote it, when that differs.
28 #[serde(default, skip_serializing_if = "Option::is_none")]
29 pub display_username: Option<String>,
30 pub name: Option<String>,
31 pub avatar: Option<String>,
32 /// From their public profile.
33 pub bio: Option<String>,
34 pub location: Option<String>,
35 pub pronouns: Option<String>,
36 /// An IANA name, such as `America/Denver`, for their local time.
37 pub timezone: Option<String>,
38 /// Owner or member.
39 pub role: Role,
40 /// Their title in this workspace.
41 pub title: Option<String>,
42 /// What they own: a few short phrases.
43 pub owns: Vec<String>,
44 /// When they joined the workspace (RFC 3339).
45 pub joined_at: String,
46}
47
48/// One person on a team, as the directory names them.
49#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
50pub struct TeamPersonRef {
51 pub username: String,
52 pub role: TeamRole,
53}
54
The contracts no longer describe g1t's own agents, and workspaces can keep their AI providers. Every consumer had already stopped using them, so the TypeScript and Rust contracts drop the workspace-agent, ability, look, skill-library, context, runner-agent and mention modules, the chat and docs RPCs agents posted and read through, the folio agent methods, the agent_waiting notification, agent.asked and memory.changed events, AI credit, token and gateway billing types, model routes and sessions, team agents, agent tokens, plan, review, message and confidence types, command rules, cost caps and confidence rules. The agents:run and models:* scopes are no longer offered by any preset, OAuth default or permission form, but a stored token or grant that names them still reads; and the removed operations are gone from the scope table, so the API's check that every operation has exactly one scope is strict again. What stored data needs in order to parse (old run and credential kinds, an agent team lead, retired rules and webhook events, old guardrail and integration settings) still parses, and a run's token is still PrincipalKind::Agent, which now means a run's token and the @g1t rows written before. The customize, marketplace, skills and claude-plugin contracts stay only until the mobile app drops its Customize screens.55/// A team as the directory needs it: who is on it, by username, and who
56/// leads it.
People and teams are front and centre: one directory of people and agents with presence, local time, titles, teams and what each owns; profiles with manager and reports and the agents they work with; an org chart with each team's agents beside the person who leads it; and teams of any mix, with a lead, a channel, a budget agents keep to and the agents on them. Every agent is told its teams each turn (who leads, who owns what, who's around and who to page), and the team page shows exactly what. Member management is Members and invites; the people and teams guide says how.57#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
58pub struct DirectoryTeam {
59 pub slug: String,
60 pub name: String,
61 pub description: Option<String>,
62 pub visibility: TeamVisibility,
63 pub parent: Option<TeamRef>,
64 pub lead: Option<TeamLead>,
65 pub channel: Option<TeamChannel>,
66 /// Its own people, not its child teams'.
67 pub people: Vec<TeamPersonRef>,
68 /// Repositories it has a role on itself.
69 pub repos_count: u32,
70}
71
72/// `people_directory`: everyone in a workspace and its teams, as the
73/// viewer may see them (secret teams only for their people and owners).
74/// Members only. Returns `Outcome<PeopleDirectory>`.
75#[derive(Debug, Serialize, Deserialize)]
76pub struct PeopleArgs {
77 pub viewer: crate::Viewer,
78 pub workspace: String,
79}
80
81#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
82pub struct PeopleDirectory {
83 pub people: Vec<DirectoryPerson>,
84 pub teams: Vec<DirectoryTeam>,
85 /// What members get on every repository: `none`, `read`, `write` or `admin`.
86 pub base_permission: String,
87 /// Whether the viewer owns the workspace: they can set anyone's
Merge branch 'rm-orgchart-getstarted'88 /// title and what they own.
People and teams are front and centre: one directory of people and agents with presence, local time, titles, teams and what each owns; profiles with manager and reports and the agents they work with; an org chart with each team's agents beside the person who leads it; and teams of any mix, with a lead, a channel, a budget agents keep to and the agents on them. Every agent is told its teams each turn (who leads, who owns what, who's around and who to page), and the team page shows exactly what. Member management is Members and invites; the people and teams guide says how.89 pub can_manage: bool,
90}
91
Merge branch 'rm-orgchart-getstarted'92/// `set_member_profile`: what is given changes; the rest stays. Returns
93/// `Outcome<DirectoryPerson>`.
People and teams are front and centre: one directory of people and agents with presence, local time, titles, teams and what each owns; profiles with manager and reports and the agents they work with; an org chart with each team's agents beside the person who leads it; and teams of any mix, with a lead, a channel, a budget agents keep to and the agents on them. Every agent is told its teams each turn (who leads, who owns what, who's around and who to page), and the team page shows exactly what. Member management is Members and invites; the people and teams guide says how.94#[derive(Debug, Default, Serialize, Deserialize)]
95pub struct SetMemberProfileArgs {
96 pub actor: User,
97 pub workspace: String,
98 pub username: String,
99 #[serde(default)]
100 pub title: Option<String>,
101 #[serde(default)]
102 pub owns: Option<Vec<String>>,
103 #[serde(default)]
104 pub surface: Option<crate::audit::Surface>,
105}
106
107/// A title as stored: trimmed, at most [`MAX_TITLE_LENGTH`] characters,
108/// `None` when nothing is left.
109pub fn clean_title(title: &str) -> Option<String> {
110 let title: String = title.split_whitespace().collect::<Vec<_>>().join(" ").chars().take(MAX_TITLE_LENGTH).collect();
111 let title = title.trim().to_owned();
112 (!title.is_empty()).then_some(title)
113}
114
115/// What someone owns, as stored: each phrase trimmed and cut to
116/// [`MAX_OWNS_LENGTH`], blanks and repeats (ignoring case) left out, at
117/// most [`MAX_OWNS`].
118pub fn clean_owns(owns: &[String]) -> Vec<String> {
119 let mut out: Vec<String> = Vec::new();
120 for phrase in owns {
121 let phrase: String = phrase.split_whitespace().collect::<Vec<_>>().join(" ").chars().take(MAX_OWNS_LENGTH).collect();
122 let phrase = phrase.trim().trim_end_matches(['.', ',', ';']).trim().to_owned();
123 if phrase.is_empty() || out.iter().any(|kept| kept.eq_ignore_ascii_case(&phrase)) {
124 continue;
125 }
126 out.push(phrase);
127 if out.len() == MAX_OWNS {
128 break;
129 }
130 }
131 out
132}
133
Merge branch 'rm-orgchart-getstarted'134/// What may change on someone's profile: their title and what they own,
135/// by themselves or an owner.
People and teams are front and centre: one directory of people and agents with presence, local time, titles, teams and what each owns; profiles with manager and reports and the agents they work with; an org chart with each team's agents beside the person who leads it; and teams of any mix, with a lead, a channel, a budget agents keep to and the agents on them. Every agent is told its teams each turn (who leads, who owns what, who's around and who to page), and the team page shows exactly what. Member management is Members and invites; the people and teams guide says how.136#[derive(Clone, Copy, Debug, PartialEq, Eq)]
137pub enum ProfileField {
138 Title,
139 Owns,
140}
141
142/// Whether `actor` may change `field` on someone's profile.
143pub fn may_edit_profile(field: ProfileField, is_self: bool, owner: bool) -> bool {
144 match field {
145 ProfileField::Title | ProfileField::Owns => is_self || owner,
146 }
147}
148
149#[cfg(test)]
150mod tests {
151 use super::*;
152
153 #[test]
154 fn titles_are_tidied() {
155 assert_eq!(clean_title(" Staff Engineer "), Some("Staff Engineer".to_owned()));
156 assert_eq!(clean_title(" "), None);
157 assert_eq!(clean_title(&"a".repeat(200)).map(|t| t.len()), Some(MAX_TITLE_LENGTH));
158 }
159
160 #[test]
161 fn what_someone_owns_is_kept_short_and_once() {
162 let owns = clean_owns(&[
163 " storefront ".into(),
164 "Storefront".into(),
165 "".into(),
166 "the release process.".into(),
167 "x".repeat(100),
168 ]);
169 assert_eq!(owns[0], "storefront");
170 assert_eq!(owns[1], "the release process");
171 assert_eq!(owns[2].len(), MAX_OWNS_LENGTH);
172 assert_eq!(owns.len(), 3);
173 let many: Vec<String> = (0..20).map(|i| format!("thing {i}")).collect();
174 assert_eq!(clean_owns(&many).len(), MAX_OWNS);
175 }
176
177 #[test]
Merge branch 'rm-orgchart-getstarted'178 fn people_write_their_own_and_owners_anyones() {
People and teams are front and centre: one directory of people and agents with presence, local time, titles, teams and what each owns; profiles with manager and reports and the agents they work with; an org chart with each team's agents beside the person who leads it; and teams of any mix, with a lead, a channel, a budget agents keep to and the agents on them. Every agent is told its teams each turn (who leads, who owns what, who's around and who to page), and the team page shows exactly what. Member management is Members and invites; the people and teams guide says how.179 assert!(may_edit_profile(ProfileField::Title, true, false));
180 assert!(may_edit_profile(ProfileField::Owns, false, true));
181 assert!(!may_edit_profile(ProfileField::Owns, false, false));
182 }
183}

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