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.

Commit

A run on an issue names its work item, not a pull request

Runs g1t's agent started on an issue (implement, answer; no pull request) kept the issue's number in its repository, which the move to work items never rewrote, and the workspace's sessions showed it as a pull request of that number: another change entirely. Such a run now reports no pull request number, and the sessions list names the work item the issue became, found through work_item_repos.legacy_number when read.

G1T-Session:
ses_01m56w5m6cegebbtcjh7ax0a5g
syntaqxcommitted Parent79f53c0Browse files
2 files+79−100/2 viewed
+7−6
127127 }
128128 }
129129
130−/// A run of g1t's agent, as one of the workspace's sessions.
131−pub(crate) fn from_run(run: AgentRun, repo_id: &str) -> WorkspaceSession {
130+/// A run of g1t's agent, as one of the workspace's sessions: `item`, the
131+/// work item one started on an issue worked on.
132+pub(crate) fn from_run(run: AgentRun, repo_id: &str, item: Option<u32>) -> WorkspaceSession {
132133 let state = if run.status.is_active() { SessionState::Active } else { SessionState::Ended };
133134 WorkspaceSession {
134135 kind: SessionKind::Agent,
140141 harness: None,
141142 agent: Some(run.agent),
142143 branch: None,
143− item: None,
144+ item,
144145 item_title: None,
145146 pull: run.number,
146147 pull_title: None,
333334 self.agent_runs_in(&workspace, &ids, a.shown == SessionsShown::Live, member, limit)
334335 .await?
335336 .into_iter()
336− .map(|(repo_id, run)| from_run(run, &repo_id))
337+ .map(|(repo_id, run, item)| from_run(run, &repo_id, item))
337338 .collect()
338339 } else {
339340 Vec::new()
667668 })
668669 .collect();
669670 let wanted = Wanted { shown: SessionsShown::Recent, kind: None, harness: None, person: None, item: None };
670− let all = merged(Vec::new(), vec![from_run(run(8, "running"), "rep_api")], recorded, &wanted, 10);
671+ let all = merged(Vec::new(), vec![from_run(run(8, "running"), "rep_api", None)], recorded, &wanted, 10);
671672 let kinds: Vec<(SessionKind, &str)> = all.iter().map(|session| (session.kind, session.id.as_str())).collect();
672673 assert_eq!(kinds, [(SessionKind::Agent, "run_1"), (SessionKind::Recorded, "pr_api_7")]);
673674 assert_eq!(all[0].actor.as_deref(), Some("g1t"));
679680 assert!(merged(Vec::new(), Vec::new(), vec![(all[1].clone(), false)], &by_harness, 10).is_empty());
680681 // A finished run is not live.
681682 let live = Wanted { shown: SessionsShown::Live, kind: None, harness: None, person: None, item: None };
682− assert!(merged(Vec::new(), vec![from_run(run(8, "succeeded"), "rep_api")], Vec::new(), &live, 10).is_empty());
683+ assert!(merged(Vec::new(), vec![from_run(run(8, "succeeded"), "rep_api", None)], Vec::new(), &live, 10).is_empty());
683684 }
684685
685686 #[test]
+72−4
6666 const SESSION_ENTRIES: u32 = 2000;
6767
6868 /// Every column but the steps, which only a run's own page needs.
69+///
70+/// A run g1t's agent started on an issue (no pull request; only these
71+/// kinds) kept the issue's number in its repository, which the move to
72+/// work items (g1t #57) never rewrote: `on_item` says the number is not a
73+/// pull request's, and `item` is the work item's number in its workspace,
74+/// found through the issue's old number.
6975 const COLUMNS: &str = "id, workspace, repo_id, repo, number, pull_id, kind, agent, model, status,
7076 step, step_count, cost_usd, turns, sandbox, token_hash, started_by, error, created_at,
7177 started_at, finished_at, updated_at, budget_usd, time_cap_minutes, halted,
72− COALESCE((SELECT title FROM pulls WHERE pulls.id = agent_runs.pull_id), agent_runs.title) AS title";
78+ COALESCE((SELECT title FROM pulls WHERE pulls.id = agent_runs.pull_id), agent_runs.title) AS title,
79+ (agent_runs.pull_id IS NULL AND agent_runs.kind IN ('implement', 'answer')) AS on_item,
80+ (SELECT i.number FROM work_item_repos r JOIN work_items i ON i.id = r.item_id
81+ WHERE agent_runs.pull_id IS NULL AND agent_runs.kind IN ('implement', 'answer')
82+ AND r.repo_id = agent_runs.repo_id AND r.legacy_number = agent_runs.number) AS item";
7383
7484 /// The pull requests of the repositories in `?1` (a JSON array of ids)
7585 /// with entries recorded on them directly (not by a session moved onto
133143 time_cap_minutes: Option<u32>,
134144 #[serde(default)]
135145 halted: Option<String>,
146+ /// 1 when `number` is an issue's, not a pull request's (see [`COLUMNS`]).
147+ #[serde(default)]
148+ on_item: Option<u32>,
149+ /// The work item that issue became, by number in its workspace.
150+ #[serde(default)]
151+ item: Option<u32>,
136152 }
137153
138154 impl RunRow {
155+ fn on_item(&self) -> bool {
156+ self.on_item.is_some_and(|flag| flag != 0)
157+ }
158+
159+ /// The work item it worked on, for a run started on an issue.
160+ pub(crate) fn item(&self) -> Option<u32> {
161+ self.item.filter(|_| self.on_item())
162+ }
163+
139164 fn status(&self) -> RunStatus {
140165 RunStatus::parse(&self.status).unwrap_or(RunStatus::Failed)
141166 }
143168 /// The run as `member` (or not) may see it: model and cost are the
144169 /// workspace's business.
145170 fn into_run(self, member: bool) -> AgentRun {
171+ let on_item = self.on_item();
146172 let (namespace, name) = self.repo.split_once('/').unwrap_or((&self.repo, ""));
147173 AgentRun {
148174 repo: RepoPath {
157183 .and_then(|steps| serde_json::from_str(steps).ok())
158184 .unwrap_or_default(),
159185 id: self.id,
160− number: self.number.filter(|number| *number > 0),
186+ // An issue's old number is no pull request's.
187+ number: self.number.filter(|number| *number > 0 && !on_item),
161188 title: self.title,
162189 agent: self.agent,
163190 model: self.model.filter(|_| member),
484511 /// (sandboxes for checks, the queue and merge checks are not agents),
485512 /// newest first, with each one's repository id. Only `live` ones: those
486513 /// queued or running.
487− pub(crate) async fn agent_runs_in(&self, workspace: &str, repo_ids: &[String], live: bool, member: bool, limit: u32) -> Result<Vec<(String, AgentRun)>> {
514+ /// Each with its repository's id, and the work item it worked on, by
515+ /// number, for one started on an issue.
516+ pub(crate) async fn agent_runs_in(
517+ &self,
518+ workspace: &str,
519+ repo_ids: &[String],
520+ live: bool,
521+ member: bool,
522+ limit: u32,
523+ ) -> Result<Vec<(String, AgentRun, Option<u32>)>> {
488524 if repo_ids.is_empty() {
489525 return Ok(Vec::new());
490526 }
501537 .all()
502538 .await?
503539 .results::<RunRow>()?;
504− Ok(rows.into_iter().map(|row| (row.repo_id.clone(), row.into_run(member))).collect())
540+ Ok(rows
541+ .into_iter()
542+ .map(|row| {
543+ let item = row.item();
544+ (row.repo_id.clone(), row.into_run(member), item)
545+ })
546+ .collect())
505547 }
506548
507549 /// The pull requests of some repositories with a recorded session,
707749 }
708750
709751 #[test]
752+ fn a_run_on_an_issue_names_its_work_item_not_a_pull_request() {
753+ let db = crate::migrated::database();
754+ db.execute_batch(
755+ "INSERT INTO work_items (id, workspace, number, title, body, author_id, author_name, created_at, updated_at)
756+ VALUES ('iss_7', 'acme', 57, 'Passkeys', '', 'usr_1', 'ana', '', '');
757+ INSERT INTO work_item_repos (item_id, repo_id, role, legacy_number, added_at) VALUES ('iss_7', 'rep_web', 'origin', 7, '');
758+ INSERT INTO agent_runs (id, workspace, repo_id, repo, number, pull_id, kind, agent, status, sandbox, token_hash, created_at, updated_at)
759+ VALUES ('arn_issue', 'acme', 'rep_web', 'acme/web', 7, NULL, 'implement', 'g1t', 'succeeded', 's', 't', '', ''),
760+ ('arn_pull', 'acme', 'rep_web', 'acme/web', 7, 'pul_7', 'review', 'g1t', 'succeeded', 's', 't', '', ''),
761+ ('arn_queue', 'acme', 'rep_web', 'acme/web', 7, NULL, 'queue', 'g1t', 'succeeded', 's', 't', '', '');",
762+ )
763+ .unwrap();
764+ let run = |id: &str| {
765+ db.query_row(&format!("SELECT on_item, item FROM (SELECT {COLUMNS} FROM agent_runs WHERE id = ?1)"), [id], |row| {
766+ Ok((row.get::<_, i64>(0)?, row.get::<_, Option<i64>>(1)?))
767+ })
768+ .unwrap()
769+ };
770+ // Issue #7 of web is work item #57 of acme; pull request #7 is not it.
771+ assert_eq!(run("arn_issue"), (1, Some(57)));
772+ assert_eq!(run("arn_pull"), (0, None));
773+ // The merge queue names a pull request by number alone.
774+ assert_eq!(run("arn_queue"), (0, None));
775+ }
776+
777+ #[test]
710778 fn steps_are_one_short_line() {
711779 assert_eq!(one_line(" Read\n src/lib.rs ", 40), "Read src/lib.rs");
712780 let long = one_line(&"x".repeat(300), 10);

Comments

Sign in to comment.