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
198 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 rulesets: branch and tag rules, agent-first, enforced on push and merge1//! A repository's settings for how g1t's agents handle its pull requests,
2//! and the branch protection settings that are now its "Default branch
3//! protection" ruleset (rulesets.rs).
4//!
5//! What a merge needs is decided by the rules of the branch it merges into
6//! (`Work::merge_gate`); `approvals_gap` asks them for what people must
7//! still do, as g1t sees it when it merges by itself.
A last-commits walk runs to the end unless asked for a budget; the page bounds its own wait8
9use g1t_contracts::time::rfc3339;
10use g1t_contracts::work::*;
11use g1t_contracts::{FailureCode, Outcome};
12use g1t_kit::now_ms;
13use serde::Deserialize;
14use worker::Result;
15
16use crate::Work;
17
18const MAX_REQUIRED_APPROVALS: u32 = 6;
19
20#[derive(Deserialize)]
21struct SettingsRow {
22 auto_merge: u8,
23 require_up_to_date: u8,
24 required_approvals: u32,
25 count_agent_approvals: u8,
26 allow_ignoring_checks: u8,
27 #[serde(default)]
28 merge_queue: u8,
29 /// JSON array of names.
30 #[serde(default)]
31 required_checks: Option<String>,
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar32 #[serde(default)]
33 require_code_owner_review: u8,
A last-commits walk runs to the end unless asked for a budget; the page bounds its own wait34 updated_by: String,
35 updated_at: String,
36}
37
38impl From<SettingsRow> for RepoSettings {
39 fn from(row: SettingsRow) -> Self {
40 RepoSettings {
41 auto_merge: row.auto_merge != 0,
42 required_checks: row
43 .required_checks
44 .as_deref()
45 .and_then(|names| serde_json::from_str(names).ok())
46 .unwrap_or_default(),
47 require_up_to_date: row.require_up_to_date != 0,
48 required_approvals: row.required_approvals,
49 count_agent_approvals: row.count_agent_approvals != 0,
50 allow_ignoring_checks: row.allow_ignoring_checks != 0,
51 merge_queue: row.merge_queue != 0,
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar52 require_code_owner_review: row.require_code_owner_review != 0,
A last-commits walk runs to the end unless asked for a budget; the page bounds its own wait53 updated_by: Some(row.updated_by),
54 updated_at: Some(row.updated_at),
55 }
56 }
57}
58
59impl Work {
Merge rulesets: branch and tag rules, agent-first, enforced on push and merge60 /// A repository's stored settings, by its id: how g1t's agents handle
61 /// its pull requests. Defaults if none were set. Branch protection is
62 /// its rules': see `settings_on`.
A last-commits walk runs to the end unless asked for a budget; the page bounds its own wait63 pub(crate) async fn settings(&self, repo_id: &str) -> Result<RepoSettings> {
64 if let Some(found) = self.prefetched_repo(repo_id) {
65 let row = found.first::<SettingsRow>(crate::prefetch::Slot::Settings)?;
The work, events, webhooks, actions, repos and packages services no longer run g1t's own agents. Work drops the 35 calls that queued issues for g1t, delegated them, messaged and reviewed through the agent, planned, captured memories automatically, rated confidence and capped agent spend, and its pull request lifecycle no longer revises or reviews: a pull request only advances to a merge by the repository's auto-merge setting. Merging a pull request that is behind now catches it up itself, through the repository's branch update, and lands it when the push arrives; when it can't be caught up cleanly the merge says to merge the base in and try again. Recording a session on a pull request, memory recall and remember, overlaps, the merge queue and required checks stay as they were.66 return Ok(row.map_or_else(RepoSettings::default, RepoSettings::from));
A last-commits walk runs to the end unless asked for a budget; the page bounds its own wait67 }
The work, events, webhooks, actions, repos and packages services no longer run g1t's own agents. Work drops the 35 calls that queued issues for g1t, delegated them, messaged and reviewed through the agent, planned, captured memories automatically, rated confidence and capped agent spend, and its pull request lifecycle no longer revises or reviews: a pull request only advances to a merge by the repository's auto-merge setting. Merging a pull request that is behind now catches it up itself, through the repository's branch update, and lands it when the push arrives; when it can't be caught up cleanly the merge says to merge the base in and try again. Recording a session on a pull request, memory recall and remember, overlaps, the merge queue and required checks stay as they were.68 let row = self
69 .db
70 .prepare("SELECT * FROM repo_settings WHERE repo_id = ?")
71 .bind(&[repo_id.into()])?
72 .first::<SettingsRow>(None)
73 .await?;
74 Ok(row.map_or_else(RepoSettings::default, RepoSettings::from))
A last-commits walk runs to the end unless asked for a budget; the page bounds its own wait75 }
76
Merge rulesets: branch and tag rules, agent-first, enforced on push and merge77 /// The default branch's settings: the stored ones, with branch
78 /// protection as its rules stack.
A last-commits walk runs to the end unless asked for a budget; the page bounds its own wait79 pub(crate) async fn get_settings(&self, a: ViewArgs) -> Result<Outcome<RepoSettings>> {
Merge rulesets: branch and tag rules, agent-first, enforced on push and merge80 let repo = match self.repo(&a.repo, &a.viewer).await? {
81 Outcome::Ok(repo) => repo,
82 Outcome::Fail(failure) => return Ok(Outcome::Fail(failure)),
83 };
84 Ok(Outcome::Ok(self.timing.db(3, self.default_branch_settings(&repo)).await?))
A last-commits walk runs to the end unless asked for a budget; the page bounds its own wait85 }
86
Merge rulesets: branch and tag rules, agent-first, enforced on push and merge87 /// Replaces a repository's settings: how g1t's agents work, kept here,
88 /// and the branch protection ones, written to its "Default branch
89 /// protection" ruleset (made when it has none and they protect
90 /// anything). Rules that only rulesets have stay as they are.
A last-commits walk runs to the end unless asked for a budget; the page bounds its own wait91 pub(crate) async fn update_settings(
92 &self,
93 a: UpdateSettingsArgs,
94 ) -> Result<Outcome<RepoSettings>> {
95 let repo = match self.repo(&a.repo, &Some(a.actor.clone())).await? {
96 Outcome::Ok(repo) => repo,
97 Outcome::Fail(failure) => return Ok(Outcome::Fail(failure)),
98 };
Merge main into rm-agents: mirroring removal, open signups, free-tier limits, the g1t CLI and runner fixes, with the agent removal kept. The CLI's table of operations is regenerated from the API without the agent, customize and AI credit operations; no CLI command used them.99 if let Outcome::Fail(failure) = crate::retired::writable(&repo) {
A last-commits walk runs to the end unless asked for a budget; the page bounds its own wait100 return Ok(Outcome::Fail(failure));
101 }
102 if !a.actor.verified {
103 return Ok(Outcome::fail(FailureCode::Forbidden, crate::UNVERIFIED));
104 }
105 if let Outcome::Fail(failure) =
106 crate::allowed(Some(&a.actor), &repo, g1t_contracts::access::Capability::ManageSettings)
107 {
108 return Ok(Outcome::Fail(failure));
109 }
Merge rulesets: branch and tag rules, agent-first, enforced on push and merge110 let before = self.default_branch_settings(&repo).await?;
A last-commits walk runs to the end unless asked for a budget; the page bounds its own wait111 let settings = RepoSettings {
112 required_approvals: a.settings.required_approvals.min(MAX_REQUIRED_APPROVALS),
113 required_checks: tidy_required(&a.settings.required_checks),
Merge rulesets: branch and tag rules, agent-first, enforced on push and merge114 updated_by: Some(a.actor.username.clone()),
A last-commits walk runs to the end unless asked for a budget; the page bounds its own wait115 updated_at: Some(rfc3339(now_ms())),
116 ..a.settings
117 };
Merge rulesets: branch and tag rules, agent-first, enforced on push and merge118 // Branch protection changes need the role that changes it.
119 if protection_changed(&before, &settings)
120 && let Outcome::Fail(failure) =
121 crate::allowed(Some(&a.actor), &repo, g1t_contracts::access::Capability::ManageProtection)
122 {
123 return Ok(Outcome::Fail(failure));
124 }
A last-commits walk runs to the end unless asked for a budget; the page bounds its own wait125 self.db
126 .prepare(
127 "INSERT INTO repo_settings
128 (repo_id, auto_merge, require_up_to_date, required_approvals,
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.129 count_agent_approvals, allow_ignoring_checks,
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar130 merge_queue, required_checks, require_code_owner_review, updated_by, updated_at)
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.131 VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
A last-commits walk runs to the end unless asked for a budget; the page bounds its own wait132 ON CONFLICT (repo_id) DO UPDATE SET
133 auto_merge = excluded.auto_merge,
134 require_up_to_date = excluded.require_up_to_date,
135 required_approvals = excluded.required_approvals,
136 count_agent_approvals = excluded.count_agent_approvals,
137 allow_ignoring_checks = excluded.allow_ignoring_checks,
138 merge_queue = excluded.merge_queue,
139 required_checks = excluded.required_checks,
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar140 require_code_owner_review = excluded.require_code_owner_review,
A last-commits walk runs to the end unless asked for a budget; the page bounds its own wait141 updated_by = excluded.updated_by,
142 updated_at = excluded.updated_at",
143 )
144 .bind(&[
145 repo.id.as_str().into(),
146 u32::from(settings.auto_merge).into(),
147 u32::from(settings.require_up_to_date).into(),
148 settings.required_approvals.into(),
149 u32::from(settings.count_agent_approvals).into(),
150 u32::from(settings.allow_ignoring_checks).into(),
151 u32::from(settings.merge_queue).into(),
152 serde_json::to_string(&settings.required_checks)?.into(),
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar153 u32::from(settings.require_code_owner_review).into(),
A last-commits walk runs to the end unless asked for a budget; the page bounds its own wait154 settings.updated_by.as_deref().unwrap_or_default().into(),
155 settings.updated_at.as_deref().unwrap_or_default().into(),
156 ])?
157 .run()
158 .await?;
Merge rulesets: branch and tag rules, agent-first, enforced on push and merge159 if protection_changed(&before, &settings) {
160 self.write_branch_protection(&repo, &settings, &a.actor).await?;
161 }
162 Ok(Outcome::Ok(self.default_branch_settings(&repo).await?))
A last-commits walk runs to the end unless asked for a budget; the page bounds its own wait163 }
164}
165
Merge rulesets: branch and tag rules, agent-first, enforced on push and merge166/// Whether the branch protection part of the settings differs.
167fn protection_changed(before: &RepoSettings, after: &RepoSettings) -> bool {
168 let names = |settings: &RepoSettings| -> Vec<String> {
169 let mut names: Vec<String> = settings.required_checks.iter().map(|name| name.to_lowercase()).collect();
170 names.sort();
171 names
172 };
173 names(before) != names(after)
174 || before.require_up_to_date != after.require_up_to_date
175 || before.required_approvals != after.required_approvals
176 || before.count_agent_approvals != after.count_agent_approvals
177 || before.allow_ignoring_checks != after.allow_ignoring_checks
178 || before.merge_queue != after.merge_queue
179 || before.require_code_owner_review != after.require_code_owner_review
180}
181
A last-commits walk runs to the end unless asked for a budget; the page bounds its own wait182#[cfg(test)]
183mod tests {
184 use super::*;
185
186 #[test]
Merge rulesets: branch and tag rules, agent-first, enforced on push and merge187 fn only_protection_changes_count_as_protection_changes() {
188 let before = RepoSettings::default();
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.189 let auto = RepoSettings { auto_merge: true, ..RepoSettings::default() };
190 assert!(!protection_changed(&before, &auto));
Merge rulesets: branch and tag rules, agent-first, enforced on push and merge191 let checks = RepoSettings { required_checks: vec!["CI".into()], ..RepoSettings::default() };
192 assert!(protection_changed(&before, &checks));
193 let same = RepoSettings { required_checks: vec!["ci".into()], ..RepoSettings::default() };
194 assert!(!protection_changed(&checks, &same), "names compare without case");
195 let approvals = RepoSettings { required_approvals: 1, ..RepoSettings::default() };
196 assert!(protection_changed(&before, &approvals));
A last-commits walk runs to the end unless asked for a budget; the page bounds its own wait197 }
198}

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