Contracts: workspace_signals, what a workspace's work says over the last weeks
Home's daily signal and the Workspace overview read it: per repository, weekly merges, agent merges, rework, draft and first-review times and stalled changes; daily activity; unreviewed pull requests and the work items waiting on them; pull requests with their work items, milestones and owners; and who has merged where. Part of #157
- G1t-Session:
- ses_01m57128wre9bvvj1y4z4b8dk0
4 files+232−00/4 viewed
| 19 | 19 | mod insights; | |
| 20 | 20 | pub use insights::*; | |
| 21 | 21 | ||
| 22 | + | mod signals; | |
| 23 | + | pub use signals::*; | |
| 24 | + | ||
| 22 | 25 | mod coordination; | |
| 23 | 26 | pub use coordination::*; | |
| 24 | 27 |
| 1 | + | //! What a workspace's work says over the last weeks: the reads behind | |
| 2 | + | //! Home's daily signal and the Workspace overview. Each row is one | |
| 3 | + | //! repository's, so a caller that asks in batches adds them up. | |
| 4 | + | //! | |
| 5 | + | //! A pull request is an agent's when g1t's agent opened it, it was opened | |
| 6 | + | //! as an agent's, or a harness session is tied to it (by a link, by saying | |
| 7 | + | //! so when it started, or by pushing its branch). Whose it is (`owner`) is | |
| 8 | + | //! the person it was requested by, else its author: the one who | |
| 9 | + | //! understood it. | |
| 10 | + | use crate::Viewer; | |
| 11 | + | use serde::{Deserialize, Serialize}; | |
| 12 | + | ||
| 13 | + | #[derive(Deserialize, Serialize)] | |
| 14 | + | #[serde(rename_all = "camelCase")] | |
| 15 | + | pub struct WorkspaceSignalsArgs { | |
| 16 | + | /// The workspace the repositories belong to: its work items and milestones. | |
| 17 | + | pub workspace: String, | |
| 18 | + | /// Up to 100 repositories; the ones the viewer cannot read are left out. | |
| 19 | + | pub repo_ids: Vec<String>, | |
| 20 | + | pub viewer: Viewer, | |
| 21 | + | /// The last UTC day covered, `YYYY-MM-DD`: the last week ends with it. | |
| 22 | + | pub until: String, | |
| 23 | + | /// How many weeks of seven UTC days end with `until`: 1 through 13. | |
| 24 | + | pub weeks: u32, | |
| 25 | + | /// How many UTC days end with `until` for `days`: 1 through 28. | |
| 26 | + | pub days: u32, | |
| 27 | + | /// The first UTC day `pulls` reaches back to, `YYYY-MM-DD`: at most 400 days before `until`. | |
| 28 | + | pub since: String, | |
| 29 | + | } | |
| 30 | + | ||
| 31 | + | /// One repository's week, starting on UTC day `start`. Weeks with nothing | |
| 32 | + | /// in them are left out. | |
| 33 | + | #[derive(Clone, Default, Deserialize, Serialize)] | |
| 34 | + | #[serde(default, rename_all(serialize = "camelCase"))] | |
| 35 | + | pub struct SignalWeek { | |
| 36 | + | pub repo_id: String, | |
| 37 | + | pub start: String, | |
| 38 | + | pub merged: u32, | |
| 39 | + | /// Of `merged`, an agent's. | |
| 40 | + | pub agent_merged: u32, | |
| 41 | + | /// Of `agent_merged`, the ones a person asked for changes on before they merged. | |
| 42 | + | pub agent_reworked: u32, | |
| 43 | + | /// Agent pull requests opened in the week whose session started first, | |
| 44 | + | /// and the seconds from each session's start to its pull request opening. | |
| 45 | + | pub agent_drafted: u32, | |
| 46 | + | pub agent_draft_seconds: f64, | |
| 47 | + | /// First reviews given in the week (a person's approval or request for | |
| 48 | + | /// changes, as Insights counts them), and the seconds each waited. | |
| 49 | + | pub reviewed: u32, | |
| 50 | + | pub review_seconds: f64, | |
| 51 | + | /// Pull requests open when the week ended (now, for the current week) | |
| 52 | + | /// that had been open over seven days by then. | |
| 53 | + | pub stalled: u32, | |
| 54 | + | } | |
| 55 | + | ||
| 56 | + | /// One repository's UTC day. Days with nothing in them are left out. | |
| 57 | + | #[derive(Clone, Default, Deserialize, Serialize)] | |
| 58 | + | #[serde(default, rename_all(serialize = "camelCase"))] | |
| 59 | + | pub struct SignalDay { | |
| 60 | + | pub repo_id: String, | |
| 61 | + | pub day: String, | |
| 62 | + | pub opened: u32, | |
| 63 | + | pub merged: u32, | |
| 64 | + | /// Of `merged`, an agent's. | |
| 65 | + | pub agent_merged: u32, | |
| 66 | + | /// A person's requests for changes. | |
| 67 | + | pub reworked: u32, | |
| 68 | + | /// Reviews and comments by people other than the pull request's owner. | |
| 69 | + | pub reviews: u32, | |
| 70 | + | /// Harness sessions started. | |
| 71 | + | pub sessions: u32, | |
| 72 | + | } | |
| 73 | + | ||
| 74 | + | /// An open pull request no one has reviewed yet, oldest first. | |
| 75 | + | #[derive(Clone, Default, Deserialize, Serialize)] | |
| 76 | + | #[serde(default, rename_all(serialize = "camelCase"))] | |
| 77 | + | pub struct WaitingPull { | |
| 78 | + | pub repo_id: String, | |
| 79 | + | pub number: u32, | |
| 80 | + | pub title: String, | |
| 81 | + | pub created_at: String, | |
| 82 | + | pub agent: bool, | |
| 83 | + | /// For an agent's: seconds from its first session's start to opening. | |
| 84 | + | pub draft_seconds: Option<f64>, | |
| 85 | + | /// The work item it is for, and the open work items blocked by that one. | |
| 86 | + | pub item: Option<u32>, | |
| 87 | + | pub blocks: Vec<u32>, | |
| 88 | + | } | |
| 89 | + | ||
| 90 | + | /// A pull request opened or merged since `since`. | |
| 91 | + | #[derive(Clone, Default, Deserialize, Serialize)] | |
| 92 | + | #[serde(default, rename_all(serialize = "camelCase"))] | |
| 93 | + | pub struct SignalPull { | |
| 94 | + | pub repo_id: String, | |
| 95 | + | pub number: u32, | |
| 96 | + | pub title: String, | |
| 97 | + | /// draft, open, merged or closed. | |
| 98 | + | pub status: String, | |
| 99 | + | pub agent: bool, | |
| 100 | + | pub owner_id: String, | |
| 101 | + | pub owner_name: String, | |
| 102 | + | pub created_at: String, | |
| 103 | + | pub merged_at: Option<String>, | |
| 104 | + | /// The work item it is for, and that item's milestone. | |
| 105 | + | pub item: Option<u32>, | |
| 106 | + | pub milestone: Option<u32>, | |
| 107 | + | } | |
| 108 | + | ||
| 109 | + | /// One person's merged pull requests in one repository, over its whole history. | |
| 110 | + | #[derive(Clone, Default, Deserialize, Serialize)] | |
| 111 | + | #[serde(default, rename_all(serialize = "camelCase"))] | |
| 112 | + | pub struct SignalAuthor { | |
| 113 | + | pub repo_id: String, | |
| 114 | + | pub owner_id: String, | |
| 115 | + | pub owner_name: String, | |
| 116 | + | pub first_merged_at: String, | |
| 117 | + | pub last_merged_at: String, | |
| 118 | + | pub merged: u32, | |
| 119 | + | /// Of `merged`, the ones in the 90 days ending with `until`. | |
| 120 | + | pub recent: u32, | |
| 121 | + | } | |
| 122 | + | ||
| 123 | + | /// A work item `pulls` or `waiting` names. | |
| 124 | + | #[derive(Clone, Default, Deserialize, Serialize)] | |
| 125 | + | #[serde(default, rename_all(serialize = "camelCase"))] | |
| 126 | + | pub struct SignalItem { | |
| 127 | + | pub number: u32, | |
| 128 | + | pub title: String, | |
| 129 | + | pub state: String, | |
| 130 | + | pub milestone: Option<u32>, | |
| 131 | + | } | |
| 132 | + | ||
| 133 | + | /// One of the workspace's milestones. | |
| 134 | + | #[derive(Clone, Default, Deserialize, Serialize)] | |
| 135 | + | #[serde(default, rename_all(serialize = "camelCase"))] | |
| 136 | + | pub struct SignalMilestone { | |
| 137 | + | pub number: u32, | |
| 138 | + | pub title: String, | |
| 139 | + | pub state: String, | |
| 140 | + | pub due_on: Option<String>, | |
| 141 | + | pub created_at: String, | |
| 142 | + | pub closed_at: Option<String>, | |
| 143 | + | } | |
| 144 | + | ||
| 145 | + | #[derive(Default, Serialize)] | |
| 146 | + | #[serde(rename_all = "camelCase")] | |
| 147 | + | pub struct WorkspaceSignals { | |
| 148 | + | pub weeks: Vec<SignalWeek>, | |
| 149 | + | pub days: Vec<SignalDay>, | |
| 150 | + | /// At most 50. | |
| 151 | + | pub waiting: Vec<WaitingPull>, | |
| 152 | + | /// At most 3,000, newest first; `pulls_capped` when there were more. | |
| 153 | + | pub pulls: Vec<SignalPull>, | |
| 154 | + | pub pulls_capped: bool, | |
| 155 | + | pub authors: Vec<SignalAuthor>, | |
| 156 | + | pub items: Vec<SignalItem>, | |
| 157 | + | pub milestones: Vec<SignalMilestone>, | |
| 158 | + | } |
| 564 | 564 | openPull: (actor, repo, input) => call("open_pull", { actor, repo, ...input }), | |
| 565 | 565 | listPulls: (repo, viewer, state, filter = {}) => call("list_pulls", { repo, viewer, state, ...filter }), | |
| 566 | 566 | deliveryInsights: (repoIds, viewer, from, until) => call("delivery_insights", { repoIds, viewer, from, until }), | |
| 567 | + | workspaceSignals: (args, viewer) => call("workspace_signals", { ...args, viewer }), | |
| 567 | 568 | pullsForRepos: (repoIds, viewer, limit) => call("pulls_for_repos", { repoIds, viewer, limit }), | |
| 568 | 569 | // Its own fields arrive in snake_case: read into camelCase here. | |
| 569 | 570 | getPull: (repo, number, viewer) => |
| 1085 | 1085 | */ | |
| 1086 | 1086 | deliveryInsights(repoIds: string[], viewer: Viewer, from: string, until: string): Promise<DeliveryInsights>; | |
| 1087 | 1087 | /** | |
| 1088 | + | * What up to 100 of a workspace's repositories' work says over the last | |
| 1089 | + | * weeks, for Home's daily signal and the Workspace overview: each row is | |
| 1090 | + | * one repository's, so batches add up. Repositories the viewer cannot | |
| 1091 | + | * read are left out. | |
| 1092 | + | */ | |
| 1093 | + | workspaceSignals(args: WorkspaceSignalsArgs, viewer: Viewer): Promise<WorkspaceSignals>; | |
| 1094 | + | /** | |
| 1088 | 1095 | * The newest `limit` open and closed pull requests of each repository, | |
| 1089 | 1096 | * in one call. Repositories the viewer cannot read, and forks, are left | |
| 1090 | 1097 | * out: ask those with `listPulls`. | |
| ⋯ | |||
| 1833 | 1840 | export type DeliveryMetrics = { repoId: string; opened: number; merged: number; active: number; unreviewed: number; checking: number; failed: number; queued: number; leadSeconds: number; reviewSeconds: number; reviewed: number }; | |
| 1834 | 1841 | /** `checksUnavailable`: g1t Actions could not be read, so `checking` and `failed` are not known. */ | |
| 1835 | 1842 | export type DeliveryInsights = { projects: DeliveryMetrics[]; days: { day: string; merged: number }[]; checksUnavailable: boolean }; | |
| 1843 | + | ||
| 1844 | + | /** | |
| 1845 | + | * What a workspace's work says over the last weeks (crates/contracts | |
| 1846 | + | * work/signals.rs). A pull request is an agent's when g1t's agent opened | |
| 1847 | + | * it, it was opened as an agent's, or a harness session is tied to it; its | |
| 1848 | + | * owner is the person who asked for it, else its author. | |
| 1849 | + | */ | |
| 1850 | + | export type WorkspaceSignalsArgs = { | |
| 1851 | + | workspace: string; | |
| 1852 | + | repoIds: string[]; | |
| 1853 | + | /** The last UTC day covered, `YYYY-MM-DD`. */ | |
| 1854 | + | until: string; | |
| 1855 | + | /** Weeks of seven UTC days ending with `until`: 1 through 13. */ | |
| 1856 | + | weeks: number; | |
| 1857 | + | /** UTC days ending with `until` for `days`: 1 through 28. */ | |
| 1858 | + | days: number; | |
| 1859 | + | /** The first UTC day `pulls` reaches back to: at most 400 days before `until`. */ | |
| 1860 | + | since: string; | |
| 1861 | + | }; | |
| 1862 | + | /** One repository's week from UTC day `start`; empty weeks are left out. `stalled`: open when the week ended (now, for this week), and open over seven days by then. */ | |
| 1863 | + | export type SignalWeek = { | |
| 1864 | + | repoId: string; | |
| 1865 | + | start: string; | |
| 1866 | + | merged: number; | |
| 1867 | + | agentMerged: number; | |
| 1868 | + | /** Of `agentMerged`, the ones a person asked for changes on first. */ | |
| 1869 | + | agentReworked: number; | |
| 1870 | + | agentDrafted: number; | |
| 1871 | + | agentDraftSeconds: number; | |
| 1872 | + | reviewed: number; | |
| 1873 | + | reviewSeconds: number; | |
| 1874 | + | stalled: number; | |
| 1875 | + | }; | |
| 1876 | + | export type SignalDay = { repoId: string; day: string; opened: number; merged: number; agentMerged: number; reworked: number; reviews: number; sessions: number }; | |
| 1877 | + | /** An open pull request no one has reviewed, oldest first; `blocks`: the open work items waiting on its work item. */ | |
| 1878 | + | export type WaitingPull = { repoId: string; number: number; title: string; createdAt: string; agent: boolean; draftSeconds: number | null; item: number | null; blocks: number[] }; | |
| 1879 | + | export type SignalPull = { | |
| 1880 | + | repoId: string; | |
| 1881 | + | number: number; | |
| 1882 | + | title: string; | |
| 1883 | + | status: "draft" | "open" | "merged" | "closed"; | |
| 1884 | + | agent: boolean; | |
| 1885 | + | ownerId: string; | |
| 1886 | + | ownerName: string; | |
| 1887 | + | createdAt: string; | |
| 1888 | + | mergedAt: string | null; | |
| 1889 | + | item: number | null; | |
| 1890 | + | milestone: number | null; | |
| 1891 | + | }; | |
| 1892 | + | /** One person's merged pull requests in one repository over its history; `recent`: in the 90 days ending with `until`. */ | |
| 1893 | + | export type SignalAuthor = { repoId: string; ownerId: string; ownerName: string; firstMergedAt: string; lastMergedAt: string; merged: number; recent: number }; | |
| 1894 | + | export type SignalItem = { number: number; title: string; state: string; milestone: number | null }; | |
| 1895 | + | export type SignalMilestone = { number: number; title: string; state: string; dueOn: string | null; createdAt: string; closedAt: string | null }; | |
| 1896 | + | export type WorkspaceSignals = { | |
| 1897 | + | weeks: SignalWeek[]; | |
| 1898 | + | days: SignalDay[]; | |
| 1899 | + | waiting: WaitingPull[]; | |
| 1900 | + | pulls: SignalPull[]; | |
| 1901 | + | pullsCapped: boolean; | |
| 1902 | + | authors: SignalAuthor[]; | |
| 1903 | + | items: SignalItem[]; | |
| 1904 | + | milestones: SignalMilestone[]; | |
| 1905 | + | }; | |
Comments
Sign in to comment.