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 plan says what g1t is for without measuring it against other products | 1 | /** |
| The site no longer has g1t's own agents. g1t is where the coding agents you already use do their work, so the Agents mode is gone from the rail, with every page under /<workspace>/-/agents: personas, faces, skills, abilities, routines, computers, sessions and agent spend. Customize (skills, connectors, plugins, marketplaces), the context hub, model providers, the AI Gateway page, the Spend page and pill, Ask g1t, and the repository's Agents, Plans and agent settings pages go too, and their addresses now answer 404. | 2 | * The shaping behind Code's Overview: why each thing needs the viewer, |
| 3 | * what is known about it, the tabs, the week of landed changes, and the | |
| 4 | * one sentence under the title. Pure, so it is tested on its own; it | |
| The plan says what g1t is for without measuring it against other products | 5 | * imports only types. |
| 6 | */ | |
| The site no longer has g1t's own agents. g1t is where the coding agents you already use do their work, so the Agents mode is gone from the rail, with every page under /<workspace>/-/agents: personas, faces, skills, abilities, routines, computers, sessions and agent spend. Customize (skills, connectors, plugins, marketplaces), the context hub, model providers, the AI Gateway page, the Spend page and pill, Ask g1t, and the repository's Agents, Plans and agent settings pages go too, and their addresses now answer 404. | 7 | import type { CheckStatus, ChangedFile, RepoPath } from "@g1t/contracts"; |
| The plan says what g1t is for without measuring it against other products | 8 | |
| 9 | import type { Need } from "./mission"; | |
| 10 | ||
| 11 | const DAY = 24 * 60 * 60 * 1000; | |
| 12 | ||
| The site no longer has g1t's own agents. g1t is where the coding agents you already use do their work, so the Agents mode is gone from the rail, with every page under /<workspace>/-/agents: personas, faces, skills, abilities, routines, computers, sessions and agent spend. Customize (skills, connectors, plugins, marketplaces), the context hub, model providers, the AI Gateway page, the Spend page and pill, Ask g1t, and the repository's Agents, Plans and agent settings pages go too, and their addresses now answer 404. | 13 | /** Accounts that are g1t's own machinery: `isAgent` in ./mission, kept here so this module imports only types. Older rows still name them. */ |
| The plan says what g1t is for without measuring it against other products | 14 | export function isAgent(name: string | null | undefined): boolean { |
| 15 | return name === "g1t" || (name ?? "").endsWith("-agent"); | |
| 16 | } | |
| 17 | ||
| 18 | // --- Reasons ---------------------------------------------------------------- | |
| 19 | ||
| 20 | /** Why something is waiting on a person, as the chip beside it says. */ | |
| The site no longer has g1t's own agents. g1t is where the coding agents you already use do their work, so the Agents mode is gone from the rail, with every page under /<workspace>/-/agents: personas, faces, skills, abilities, routines, computers, sessions and agent spend. Customize (skills, connectors, plugins, marketplaces), the context hub, model providers, the AI Gateway page, the Spend page and pill, Ask g1t, and the repository's Agents, Plans and agent settings pages go too, and their addresses now answer 404. | 21 | export type Reason = "blocking" | "invited" | "needs_review" | "checks_failing" | "stalled"; |
| The plan says what g1t is for without measuring it against other products | 22 | |
| 23 | export const REASON_LABEL: Record<Reason, string> = { | |
| 24 | blocking: "Blocking", | |
| The site no longer has g1t's own agents. g1t is where the coding agents you already use do their work, so the Agents mode is gone from the rail, with every page under /<workspace>/-/agents: personas, faces, skills, abilities, routines, computers, sessions and agent spend. Customize (skills, connectors, plugins, marketplaces), the context hub, model providers, the AI Gateway page, the Spend page and pill, Ask g1t, and the repository's Agents, Plans and agent settings pages go too, and their addresses now answer 404. | 25 | invited: "Invitation", |
| 26 | needs_review: "Review requested", | |
| The plan says what g1t is for without measuring it against other products | 27 | checks_failing: "Checks failing", |
| 28 | stalled: "Stalled", | |
| 29 | }; | |
| 30 | ||
| The site no longer has g1t's own agents. g1t is where the coding agents you already use do their work, so the Agents mode is gone from the rail, with every page under /<workspace>/-/agents: personas, faces, skills, abilities, routines, computers, sessions and agent spend. Customize (skills, connectors, plugins, marketplaces), the context hub, model providers, the AI Gateway page, the Spend page and pill, Ask g1t, and the repository's Agents, Plans and agent settings pages go too, and their addresses now answer 404. | 31 | /** The reason a need is shown, from its kind. */ |
| 32 | export function reasonFor(need: Pick<Need, "kind">): Reason { | |
| The plan says what g1t is for without measuring it against other products | 33 | switch (need.kind) { |
| 34 | case "deploy": | |
| 35 | case "conflict": | |
| 36 | return "blocking"; | |
| 37 | case "invitation": | |
| The site no longer has g1t's own agents. g1t is where the coding agents you already use do their work, so the Agents mode is gone from the rail, with every page under /<workspace>/-/agents: personas, faces, skills, abilities, routines, computers, sessions and agent spend. Customize (skills, connectors, plugins, marketplaces), the context hub, model providers, the AI Gateway page, the Spend page and pill, Ask g1t, and the repository's Agents, Plans and agent settings pages go too, and their addresses now answer 404. | 38 | return "invited"; |
| The plan says what g1t is for without measuring it against other products | 39 | case "review": |
| The site no longer has g1t's own agents. g1t is where the coding agents you already use do their work, so the Agents mode is gone from the rail, with every page under /<workspace>/-/agents: personas, faces, skills, abilities, routines, computers, sessions and agent spend. Customize (skills, connectors, plugins, marketplaces), the context hub, model providers, the AI Gateway page, the Spend page and pill, Ask g1t, and the repository's Agents, Plans and agent settings pages go too, and their addresses now answer 404. | 40 | case "ready": |
| 41 | return "needs_review"; | |
| The plan says what g1t is for without measuring it against other products | 42 | case "checks": |
| 43 | return "checks_failing"; | |
| The site no longer has g1t's own agents. g1t is where the coding agents you already use do their work, so the Agents mode is gone from the rail, with every page under /<workspace>/-/agents: personas, faces, skills, abilities, routines, computers, sessions and agent spend. Customize (skills, connectors, plugins, marketplaces), the context hub, model providers, the AI Gateway page, the Spend page and pill, Ask g1t, and the repository's Agents, Plans and agent settings pages go too, and their addresses now answer 404. | 44 | case "stalled": |
| The plan says what g1t is for without measuring it against other products | 45 | case "stuck": |
| Usage, Billing settings and prepaid AI credit; fixes from the UX audit | 46 | case "runner": |
| The plan says what g1t is for without measuring it against other products | 47 | return "stalled"; |
| 48 | } | |
| 49 | } | |
| 50 | ||
| The site no longer has g1t's own agents. g1t is where the coding agents you already use do their work, so the Agents mode is gone from the rail, with every page under /<workspace>/-/agents: personas, faces, skills, abilities, routines, computers, sessions and agent spend. Customize (skills, connectors, plugins, marketplaces), the context hub, model providers, the AI Gateway page, the Spend page and pill, Ask g1t, and the repository's Agents, Plans and agent settings pages go too, and their addresses now answer 404. | 51 | /** Why only a person can move it: the callout beside what is known. */ |
| 52 | export function whyFor(reason: Reason, need: Pick<Need, "kind">): string { | |
| The plan says what g1t is for without measuring it against other products | 53 | if (need.kind === "deploy") |
| 54 | return "Production still serves the build before this one. Every push to the default branch builds again, so this fails until what broke is fixed."; | |
| 55 | if (need.kind === "invitation") return "An invitation is to you. No one else can accept it."; | |
| 56 | if (need.kind === "review") return "You were asked to review it by name, so it waits for your verdict."; | |
| Usage, Billing settings and prepaid AI credit; fixes from the UX audit | 57 | if (need.kind === "runner") |
| 58 | return "The job runs only on a self-hosted runner with those labels, and none is connected. It starts as soon as one is; nothing else picks it up."; | |
| The plan says what g1t is for without measuring it against other products | 59 | switch (reason) { |
| 60 | case "blocking": | |
| The site no longer has g1t's own agents. g1t is where the coding agents you already use do their work, so the Agents mode is gone from the rail, with every page under /<workspace>/-/agents: personas, faces, skills, abilities, routines, computers, sessions and agent spend. Customize (skills, connectors, plugins, marketplaces), the context hub, model providers, the AI Gateway page, the Spend page and pill, Ask g1t, and the repository's Agents, Plans and agent settings pages go too, and their addresses now answer 404. | 61 | return "Nothing moves until someone decides how it lands, or whether it should."; |
| The plan says what g1t is for without measuring it against other products | 62 | case "checks_failing": |
| The site no longer has g1t's own agents. g1t is where the coding agents you already use do their work, so the Agents mode is gone from the rail, with every page under /<workspace>/-/agents: personas, faces, skills, abilities, routines, computers, sessions and agent spend. Customize (skills, connectors, plugins, marketplaces), the context hub, model providers, the AI Gateway page, the Spend page and pill, Ask g1t, and the repository's Agents, Plans and agent settings pages go too, and their addresses now answer 404. | 63 | return "It was taken out of the merge queue because its checks failed with what was ahead of it. Push a fix, then merge it again."; |
| The plan says what g1t is for without measuring it against other products | 64 | case "needs_review": |
| The site no longer has g1t's own agents. g1t is where the coding agents you already use do their work, so the Agents mode is gone from the rail, with every page under /<workspace>/-/agents: personas, faces, skills, abilities, routines, computers, sessions and agent spend. Customize (skills, connectors, plugins, marketplaces), the context hub, model providers, the AI Gateway page, the Spend page and pill, Ask g1t, and the repository's Agents, Plans and agent settings pages go too, and their addresses now answer 404. | 65 | return "It waits for a verdict before it can land."; |
| 66 | case "invited": | |
| 67 | return "It is addressed to you."; | |
| The plan says what g1t is for without measuring it against other products | 68 | case "stalled": |
| The site no longer has g1t's own agents. g1t is where the coding agents you already use do their work, so the Agents mode is gone from the rail, with every page under /<workspace>/-/agents: personas, faces, skills, abilities, routines, computers, sessions and agent spend. Customize (skills, connectors, plugins, marketplaces), the context hub, model providers, the AI Gateway page, the Spend page and pill, Ask g1t, and the repository's Agents, Plans and agent settings pages go too, and their addresses now answer 404. | 69 | return "It has stopped moving and needs a look."; |
| The plan says what g1t is for without measuring it against other products | 70 | } |
| 71 | } | |
| 72 | ||
| 73 | /** Reasons where nothing moves until a person acts on it. */ | |
| 74 | export const BLOCKING: ReadonlySet<Reason> = new Set<Reason>(["blocking"]); | |
| 75 | ||
| The site no longer has g1t's own agents. g1t is where the coding agents you already use do their work, so the Agents mode is gone from the rail, with every page under /<workspace>/-/agents: personas, faces, skills, abilities, routines, computers, sessions and agent spend. Customize (skills, connectors, plugins, marketplaces), the context hub, model providers, the AI Gateway page, the Spend page and pill, Ask g1t, and the repository's Agents, Plans and agent settings pages go too, and their addresses now answer 404. | 76 | // --- What is known ---------------------------------------------------------- |
| The plan says what g1t is for without measuring it against other products | 77 | |
| 78 | export type FactTone = "good" | "warn" | "bad" | null; | |
| 79 | /** One thing known; a `wide` one takes the whole row and wraps. */ | |
| 80 | export type Fact = { label: string; value: string; tone: FactTone; wide?: boolean }; | |
| 81 | ||
| The site no longer has g1t's own agents. g1t is where the coding agents you already use do their work, so the Agents mode is gone from the rail, with every page under /<workspace>/-/agents: personas, faces, skills, abilities, routines, computers, sessions and agent spend. Customize (skills, connectors, plugins, marketplaces), the context hub, model providers, the AI Gateway page, the Spend page and pill, Ask g1t, and the repository's Agents, Plans and agent settings pages go too, and their addresses now answer 404. | 82 | /** How the checks the default branch requires stand on a pull request, from what a list knows without opening it. */ |
| 83 | export function checksFact(checkStatus: CheckStatus | null): Fact { | |
| The plan says what g1t is for without measuring it against other products | 84 | const fact = (value: string, tone: FactTone): Fact => ({ label: "Required checks", value, tone }); |
| 85 | if (checkStatus === "failed") return fact("Failed in the merge queue", "bad"); | |
| The site no longer has g1t's own agents. g1t is where the coding agents you already use do their work, so the Agents mode is gone from the rail, with every page under /<workspace>/-/agents: personas, faces, skills, abilities, routines, computers, sessions and agent spend. Customize (skills, connectors, plugins, marketplaces), the context hub, model providers, the AI Gateway page, the Spend page and pill, Ask g1t, and the repository's Agents, Plans and agent settings pages go too, and their addresses now answer 404. | 86 | if (checkStatus === "passed") return fact("Passing", "good"); |
| 87 | if (checkStatus === "queued" || checkStatus === "running") return fact("Running", null); | |
| The plan says what g1t is for without measuring it against other products | 88 | return fact("Not known yet", null); |
| 89 | } | |
| 90 | ||
| 91 | /** Test files, by the names test runners look for. */ | |
| 92 | export function isTestFile(path: string): boolean { | |
| A file named test.js or spec.rb counts as a test: confidence no longer says tests not added when they were | 93 | return ( |
| 94 | /(^|\/)(__tests__|tests?|spec)\//i.test(path) || | |
| 95 | /[._-](test|spec)\.[a-z0-9]+$/i.test(path) || | |
| 96 | /_test\.(go|rs|py)$/i.test(path) || | |
| 97 | // A file named for what it is: test.js, tests.py, spec.rb. | |
| 98 | /(^|\/)(tests?|specs?)\.[a-z0-9]+$/i.test(path) | |
| 99 | ); | |
| The plan says what g1t is for without measuring it against other products | 100 | } |
| 101 | ||
| 102 | const plural = (n: number, one: string, many = `${one}s`) => `${n.toLocaleString("en-US")} ${n === 1 ? one : many}`; | |
| 103 | ||
| 104 | /** | |
| 105 | * What is known about a pull request without opening it: its checks, how | |
| The site no longer has g1t's own agents. g1t is where the coding agents you already use do their work, so the Agents mode is gone from the rail, with every page under /<workspace>/-/agents: personas, faces, skills, abilities, routines, computers, sessions and agent spend. Customize (skills, connectors, plugins, marketplaces), the context hub, model providers, the AI Gateway page, the Spend page and pill, Ask g1t, and the repository's Agents, Plans and agent settings pages go too, and their addresses now answer 404. | 106 | * much it changes and the tests it touches. Only what is known is listed. |
| The plan says what g1t is for without measuring it against other products | 107 | */ |
| The site no longer has g1t's own agents. g1t is where the coding agents you already use do their work, so the Agents mode is gone from the rail, with every page under /<workspace>/-/agents: personas, faces, skills, abilities, routines, computers, sessions and agent spend. Customize (skills, connectors, plugins, marketplaces), the context hub, model providers, the AI Gateway page, the Spend page and pill, Ask g1t, and the repository's Agents, Plans and agent settings pages go too, and their addresses now answer 404. | 108 | export function pullFacts(input: { checkStatus: CheckStatus | null; files: ChangedFile[] }): Fact[] { |
| 109 | const facts: Fact[] = [checksFact(input.checkStatus)]; | |
| The plan says what g1t is for without measuring it against other products | 110 | if (input.files.length > 0) { |
| 111 | const added = input.files.reduce((sum, f) => sum + f.additions, 0); | |
| 112 | const removed = input.files.reduce((sum, f) => sum + f.deletions, 0); | |
| 113 | facts.push({ label: "Files changed", value: input.files.length.toLocaleString("en-US"), tone: null }); | |
| 114 | facts.push({ label: "Lines", value: `+${added.toLocaleString("en-US")} −${removed.toLocaleString("en-US")}`, tone: null }); | |
| 115 | const tests = input.files.filter((f) => isTestFile(f.path)).length; | |
| 116 | facts.push({ label: "Tests", value: tests > 0 ? plural(tests, "file") : "None touched", tone: tests > 0 ? "good" : "warn" }); | |
| 117 | } | |
| 118 | return facts; | |
| 119 | } | |
| 120 | ||
| 121 | // --- Rows ------------------------------------------------------------------- | |
| 122 | ||
| The site no longer has g1t's own agents. g1t is where the coding agents you already use do their work, so the Agents mode is gone from the rail, with every page under /<workspace>/-/agents: personas, faces, skills, abilities, routines, computers, sessions and agent spend. Customize (skills, connectors, plugins, marketplaces), the context hub, model providers, the AI Gateway page, the Spend page and pill, Ask g1t, and the repository's Agents, Plans and agent settings pages go too, and their addresses now answer 404. | 123 | /** Someone on a row: a person, or g1t itself on older rows. */ |
| The plan says what g1t is for without measuring it against other products | 124 | export type Who = { name: string; agent: boolean }; |
| 125 | ||
| 126 | export const who = (name: string | null | undefined): Who | null => (name ? { name, agent: isAgent(name) } : null); | |
| 127 | ||
| The site no longer has g1t's own agents. g1t is where the coding agents you already use do their work, so the Agents mode is gone from the rail, with every page under /<workspace>/-/agents: personas, faces, skills, abilities, routines, computers, sessions and agent spend. Customize (skills, connectors, plugins, marketplaces), the context hub, model providers, the AI Gateway page, the Spend page and pill, Ask g1t, and the repository's Agents, Plans and agent settings pages go too, and their addresses now answer 404. | 128 | /** A form on the page that acts without leaving it. */ |
| The plan says what g1t is for without measuring it against other products | 129 | export type QuickAction = { label: string; to: string; fields: Record<string, string>; done: string }; |
| 130 | ||
| 131 | /** One thing that needs the viewer, with what is known about it. */ | |
| 132 | export type NeedRow = { | |
| 133 | key: string; | |
| 134 | reason: Reason; | |
| 135 | repo: RepoPath | null; | |
| 136 | /** "#41", when it is an issue or pull request. */ | |
| 137 | ref: string | null; | |
| 138 | title: string; | |
| 139 | /** The ask, in one sentence. */ | |
| 140 | ask: string; | |
| The site no longer has g1t's own agents. g1t is where the coding agents you already use do their work, so the Agents mode is gone from the rail, with every page under /<workspace>/-/agents: personas, faces, skills, abilities, routines, computers, sessions and agent spend. Customize (skills, connectors, plugins, marketplaces), the context hub, model providers, the AI Gateway page, the Spend page and pill, Ask g1t, and the repository's Agents, Plans and agent settings pages go too, and their addresses now answer 404. | 141 | /** Who is waiting: the author, or the person who asked. */ |
| The plan says what g1t is for without measuring it against other products | 142 | by: Who | null; |
| 143 | at: number; | |
| 144 | to: string; | |
| 145 | open: string; | |
| 146 | facts: Fact[]; | |
| 147 | why: string; | |
| 148 | quick: QuickAction | null; | |
| 149 | /** Somewhere else to act, when that is the next step. */ | |
| 150 | link: { label: string; to: string } | null; | |
| 151 | }; | |
| 152 | ||
| 153 | const pathKey = (repo: RepoPath, number: number | null) => `${repo.namespace}/${repo.name}#${number ?? "-"}`.toLowerCase(); | |
| 154 | ||
| 155 | // --- Landed ----------------------------------------------------------------- | |
| 156 | ||
| 157 | /** | |
| The site no longer has g1t's own agents. g1t is where the coding agents you already use do their work, so the Agents mode is gone from the rail, with every page under /<workspace>/-/agents: personas, faces, skills, abilities, routines, computers, sessions and agent spend. Customize (skills, connectors, plugins, marketplaces), the context hub, model providers, the AI Gateway page, the Spend page and pill, Ask g1t, and the repository's Agents, Plans and agent settings pages go too, and their addresses now answer 404. | 158 | * The accounts g1t itself acts as on the event log: the account older rows |
| 159 | * name, and the policy that merges and pushes for it. An event's `actor` | |
| 160 | * is an account id, so this is what tells g1t apart, whatever name a | |
| 161 | * commit carries. | |
| Merge leftovers: plan activity filtered in the query, pushes counted by account, ghost during the deletion window, profile time zones (identity 0039) | 162 | */ |
| 163 | export const G1T_ACCOUNT_IDS: ReadonlySet<string> = new Set(["usr_g1t_agent", "g1t_policy"]); | |
| 164 | ||
| 165 | /** A `git.push` from the log, as far as placing its commits needs it. */ | |
| 166 | export type PushRecord = { | |
| 167 | time: string; | |
| 168 | /** The account that pushed; null when the log does not say. */ | |
| 169 | actor?: string | null; | |
| 170 | data: { after: string; before?: string; causedByJob?: string }; | |
| 171 | }; | |
| 172 | ||
| 173 | /** | |
| The site no longer has g1t's own agents. g1t is where the coding agents you already use do their work, so the Agents mode is gone from the rail, with every page under /<workspace>/-/agents: personas, faces, skills, abilities, routines, computers, sessions and agent spend. Customize (skills, connectors, plugins, marketplaces), the context hub, model providers, the AI Gateway page, the Spend page and pill, Ask g1t, and the repository's Agents, Plans and agent settings pages go too, and their addresses now answer 404. | 174 | * Whether a person pushed: not g1t itself (by the account that signed in |
| 175 | * to push), and not a workflow job's own token. | |
| The plan says what g1t is for without measuring it against other products | 176 | */ |
| Merge leftovers: plan activity filtered in the query, pushes counted by account, ghost during the deletion window, profile time zones (identity 0039) | 177 | export function pushedByPerson(push: Pick<PushRecord, "actor" | "data">): boolean { |
| 178 | if (push.data.causedByJob) return false; | |
| 179 | return !(push.actor && G1T_ACCOUNT_IDS.has(push.actor)); | |
| 180 | } | |
| 181 | ||
| 182 | /** | |
| 183 | * The commits a person's push to the default branch brought, from the | |
| 184 | * history at its `after` (newest first) back to its `before`: their own, | |
| 185 | * not merges (a pull request landing) and not g1t's (`byG1t`: a commit | |
| 186 | * g1t wrote, told by its author address, which a person may fast-forward | |
| 187 | * onto the branch). A push whose `before` is not in what was read gives | |
| 188 | * what was read. Who pushed is the caller's to decide (`pushedByPerson`); | |
| 189 | * a commit's author name says nothing about it. | |
| 190 | */ | |
| 191 | export function pushedCommits<C extends { hash: string; parents: string[] }>( | |
| The plan says what g1t is for without measuring it against other products | 192 | history: C[], |
| 193 | before: string | undefined, | |
| Merge leftovers: plan activity filtered in the query, pushes counted by account, ghost during the deletion window, profile time zones (identity 0039) | 194 | byG1t: (commit: C) => boolean = () => false, |
| The plan says what g1t is for without measuring it against other products | 195 | ): C[] { |
| 196 | const end = before ? history.findIndex((commit) => commit.hash === before) : -1; | |
| 197 | const brought = end === -1 ? history : history.slice(0, end); | |
| Merge leftovers: plan activity filtered in the query, pushes counted by account, ghost during the deletion window, profile time zones (identity 0039) | 198 | return brought.filter((commit) => commit.parents.length <= 1 && !byG1t(commit)); |
| The plan says what g1t is for without measuring it against other products | 199 | } |
| 200 | ||
| Merge branch 'main' into actions-toolkit-oidc-artifacts | 201 | /** |
| Merge leftovers: plan activity filtered in the query, pushes counted by account, ghost during the deletion window, profile time zones (identity 0039) | 202 | * Each commit of `history` (newest first) a person's push in `pushes` |
| 203 | * (newest first) brought, at that push's time. A commit pushed twice | |
| 204 | * (after a force push, say) counts once, at its first landing; a push | |
| 205 | * whose `after` is no longer in the history (rewritten) brings nothing, | |
| 206 | * and g1t's own pushes bring nothing here: pull requests count those. | |
| Merge branch 'main' into actions-toolkit-oidc-artifacts | 207 | */ |
| Merge leftovers: plan activity filtered in the query, pushes counted by account, ghost during the deletion window, profile time zones (identity 0039) | 208 | export function placePushes<C extends { hash: string; parents: string[] }>( |
| Merge branch 'main' into actions-toolkit-oidc-artifacts | 209 | history: C[], |
| Merge leftovers: plan activity filtered in the query, pushes counted by account, ghost during the deletion window, profile time zones (identity 0039) | 210 | pushes: PushRecord[], |
| 211 | byG1t: (commit: C) => boolean = () => false, | |
| Merge branch 'main' into actions-toolkit-oidc-artifacts | 212 | ): { hash: string; at: string }[] { |
| 213 | const index = new Map(history.map((commit, i) => [commit.hash, i])); | |
| Merge leftovers: plan activity filtered in the query, pushes counted by account, ghost during the deletion window, profile time zones (identity 0039) | 214 | // Each commit's first landing: the time, or null when g1t landed it. |
| 215 | const seen = new Map<string, string | null>(); | |
| Merge branch 'main' into actions-toolkit-oidc-artifacts | 216 | for (const push of [...pushes].reverse()) { |
| 217 | const start = index.get(push.data.after); | |
| 218 | if (start === undefined) continue; | |
| 219 | const end = push.data.before ? index.get(push.data.before) : undefined; | |
| Merge leftovers: plan activity filtered in the query, pushes counted by account, ghost during the deletion window, profile time zones (identity 0039) | 220 | const range = history.slice(start, end ?? history.length); |
| 221 | const at = pushedByPerson(push) ? push.time : null; | |
| 222 | for (const commit of pushedCommits(range, undefined, byG1t)) { | |
| 223 | if (!seen.has(commit.hash)) seen.set(commit.hash, at); | |
| Merge branch 'main' into actions-toolkit-oidc-artifacts | 224 | } |
| 225 | } | |
| Merge leftovers: plan activity filtered in the query, pushes counted by account, ghost during the deletion window, profile time zones (identity 0039) | 226 | return [...seen].flatMap(([hash, at]) => (at ? [{ hash, at }] : [])); |
| Merge branch 'main' into actions-toolkit-oidc-artifacts | 227 | } |
| 228 | ||
| Merge branch 'worktree-agent-ab9543c492a7ed481' into spend-guardrails | 229 | /** |
| 230 | * Whether a read of the default branch reaches back far enough to place | |
| 231 | * every push, newest first: it holds the oldest push's `before`, or it is | |
| 232 | * the whole branch. A shorter read than `asked` is the whole branch. | |
| 233 | */ | |
| 234 | export function historyCovers(history: { hash: string }[], pushes: PushRecord[], asked: number): boolean { | |
| 235 | if (history.length < asked) return true; | |
| 236 | const oldest = pushes.at(-1)?.data.before; | |
| 237 | return oldest != null && history.some((commit) => commit.hash === oldest); | |
| 238 | } | |
| 239 | ||
| Merge leftovers: plan activity filtered in the query, pushes counted by account, ghost during the deletion window, profile time zones (identity 0039) | 240 | /** A merged pull request, as the week counts it. */ |
| The plan says what g1t is for without measuring it against other products | 241 | export type Merged = { |
| 242 | repo: RepoPath; | |
| 243 | number: number; | |
| 244 | title: string; | |
| 245 | mergedBy: string | null; | |
| 246 | mergedAt: string; | |
| 247 | files: ChangedFile[]; | |
| 248 | }; | |
| 249 | ||
| 250 | /** | |
| The site no longer has g1t's own agents. g1t is where the coding agents you already use do their work, so the Agents mode is gone from the rail, with every page under /<workspace>/-/agents: personas, faces, skills, abilities, routines, computers, sessions and agent spend. Customize (skills, connectors, plugins, marketplaces), the context hub, model providers, the AI Gateway page, the Spend page and pill, Ask g1t, and the repository's Agents, Plans and agent settings pages go too, and their addresses now answer 404. | 251 | * Whether a change landed without anyone pressing merge: g1t merged it, by |
| 252 | * auto-merge or from the merge queue. | |
| The plan says what g1t is for without measuring it against other products | 253 | */ |
| The site no longer has g1t's own agents. g1t is where the coding agents you already use do their work, so the Agents mode is gone from the rail, with every page under /<workspace>/-/agents: personas, faces, skills, abilities, routines, computers, sessions and agent spend. Customize (skills, connectors, plugins, marketplaces), the context hub, model providers, the AI Gateway page, the Spend page and pill, Ask g1t, and the repository's Agents, Plans and agent settings pages go too, and their addresses now answer 404. | 254 | export function mergedAutomatically(change: Pick<Merged, "mergedBy">): boolean { |
| The plan says what g1t is for without measuring it against other products | 255 | return change.mergedBy == null || isAgent(change.mergedBy); |
| 256 | } | |
| 257 | ||
| 258 | /** A day's date in a time zone, as `YYYY-MM-DD`; UTC when the zone is unknown. */ | |
| 259 | export function dayKey(at: number, timeZone: string | null): string { | |
| 260 | try { | |
| 261 | return new Intl.DateTimeFormat("en-CA", { year: "numeric", month: "2-digit", day: "2-digit", timeZone: timeZone || "UTC" }).format(at); | |
| 262 | } catch { | |
| 263 | return new Date(at).toISOString().slice(0, 10); | |
| 264 | } | |
| 265 | } | |
| 266 | ||
| The site no longer has g1t's own agents. g1t is where the coding agents you already use do their work, so the Agents mode is gone from the rail, with every page under /<workspace>/-/agents: personas, faces, skills, abilities, routines, computers, sessions and agent spend. Customize (skills, connectors, plugins, marketplaces), the context hub, model providers, the AI Gateway page, the Spend page and pill, Ask g1t, and the repository's Agents, Plans and agent settings pages go too, and their addresses now answer 404. | 267 | /** One day's landed changes. */ |
| 268 | export type WeekDay = { key: string; label: string; count: number }; | |
| The plan says what g1t is for without measuring it against other products | 269 | |
| 270 | export type Week = { | |
| 271 | days: WeekDay[]; | |
| 272 | /** Changes landed in the last seven days. */ | |
| 273 | total: number; | |
| 274 | /** The seven days before, or null when the lists read do not reach back that far. */ | |
| 275 | previous: number | null; | |
| 276 | }; | |
| 277 | ||
| 278 | /** | |
| The site no longer has g1t's own agents. g1t is where the coding agents you already use do their work, so the Agents mode is gone from the rail, with every page under /<workspace>/-/agents: personas, faces, skills, abilities, routines, computers, sessions and agent spend. Customize (skills, connectors, plugins, marketplaces), the context hub, model providers, the AI Gateway page, the Spend page and pill, Ask g1t, and the repository's Agents, Plans and agent settings pages go too, and their addresses now answer 404. | 279 | * The last seven days in the viewer's zone, oldest first, each with how |
| 280 | * many changes landed, and the week before as one number. `complete` says | |
| 281 | * whether what was read reaches back two weeks; when it does not, the week | |
| 282 | * before is not guessed. | |
| The plan says what g1t is for without measuring it against other products | 283 | */ |
| The site no longer has g1t's own agents. g1t is where the coding agents you already use do their work, so the Agents mode is gone from the rail, with every page under /<workspace>/-/agents: personas, faces, skills, abilities, routines, computers, sessions and agent spend. Customize (skills, connectors, plugins, marketplaces), the context hub, model providers, the AI Gateway page, the Spend page and pill, Ask g1t, and the repository's Agents, Plans and agent settings pages go too, and their addresses now answer 404. | 284 | export function weekOf(changes: Pick<Merged, "mergedAt">[], now: number, timeZone: string | null, complete = true): Week { |
| The plan says what g1t is for without measuring it against other products | 285 | const days: WeekDay[] = []; |
| 286 | const index = new Map<string, number>(); | |
| 287 | for (let back = 6; back >= 0; back -= 1) { | |
| 288 | const at = now - back * DAY; | |
| 289 | const key = dayKey(at, timeZone); | |
| 290 | if (index.has(key)) continue; | |
| 291 | index.set(key, days.length); | |
| 292 | let label: string; | |
| 293 | try { | |
| 294 | label = new Intl.DateTimeFormat("en-US", { weekday: "short", timeZone: timeZone || "UTC" }).format(at); | |
| 295 | } catch { | |
| 296 | label = new Intl.DateTimeFormat("en-US", { weekday: "short", timeZone: "UTC" }).format(at); | |
| 297 | } | |
| The site no longer has g1t's own agents. g1t is where the coding agents you already use do their work, so the Agents mode is gone from the rail, with every page under /<workspace>/-/agents: personas, faces, skills, abilities, routines, computers, sessions and agent spend. Customize (skills, connectors, plugins, marketplaces), the context hub, model providers, the AI Gateway page, the Spend page and pill, Ask g1t, and the repository's Agents, Plans and agent settings pages go too, and their addresses now answer 404. | 298 | days.push({ key, label, count: 0 }); |
| The plan says what g1t is for without measuring it against other products | 299 | } |
| 300 | const oldest = days[0]?.key ?? ""; | |
| 301 | const twoWeeks = dayKey(now - 13 * DAY, timeZone); | |
| 302 | let previous = 0; | |
| 303 | for (const change of changes) { | |
| 304 | const at = Date.parse(change.mergedAt); | |
| 305 | if (!Number.isFinite(at) || at > now) continue; | |
| 306 | const key = dayKey(at, timeZone); | |
| 307 | const slot = index.get(key); | |
| The site no longer has g1t's own agents. g1t is where the coding agents you already use do their work, so the Agents mode is gone from the rail, with every page under /<workspace>/-/agents: personas, faces, skills, abilities, routines, computers, sessions and agent spend. Customize (skills, connectors, plugins, marketplaces), the context hub, model providers, the AI Gateway page, the Spend page and pill, Ask g1t, and the repository's Agents, Plans and agent settings pages go too, and their addresses now answer 404. | 308 | if (slot != null) days[slot].count += 1; |
| 309 | else if (key < oldest && key >= twoWeeks) previous += 1; | |
| The plan says what g1t is for without measuring it against other products | 310 | } |
| The site no longer has g1t's own agents. g1t is where the coding agents you already use do their work, so the Agents mode is gone from the rail, with every page under /<workspace>/-/agents: personas, faces, skills, abilities, routines, computers, sessions and agent spend. Customize (skills, connectors, plugins, marketplaces), the context hub, model providers, the AI Gateway page, the Spend page and pill, Ask g1t, and the repository's Agents, Plans and agent settings pages go too, and their addresses now answer 404. | 311 | return { days, total: days.reduce((sum, day) => sum + day.count, 0), previous: complete ? previous : null }; |
| The plan says what g1t is for without measuring it against other products | 312 | } |
| 313 | ||
| 314 | /** The change from one number to another, as a share; null from nothing. */ | |
| 315 | export function change(current: number, previous: number | null): number | null { | |
| 316 | if (previous == null || previous === 0) return null; | |
| 317 | return (current - previous) / previous; | |
| 318 | } | |
| 319 | ||
| 320 | /** "+18%", "−5%", "0%". */ | |
| 321 | export function signedPercent(share: number): string { | |
| 322 | const n = Math.round(share * 100); | |
| 323 | return n > 0 ? `+${n}%` : n < 0 ? `−${Math.abs(n)}%` : "0%"; | |
| 324 | } | |
| 325 | ||
| 326 | /** | |
| 327 | * Whether the merged pull requests read for one repository reach back to | |
| 328 | * `since`: the list is not full, or its oldest goes back that far. | |
| 329 | */ | |
| 330 | export function reachesBack(list: { mergedAt: string | null; updatedAt: string }[], since: number, page: number): boolean { | |
| 331 | if (list.length < page) return true; | |
| 332 | const oldest = Math.min(...list.map((pull) => Date.parse(pull.mergedAt ?? pull.updatedAt)).filter(Number.isFinite)); | |
| 333 | return oldest <= since; | |
| 334 | } | |
| 335 | ||
| The site no longer has g1t's own agents. g1t is where the coding agents you already use do their work, so the Agents mode is gone from the rail, with every page under /<workspace>/-/agents: personas, faces, skills, abilities, routines, computers, sessions and agent spend. Customize (skills, connectors, plugins, marketplaces), the context hub, model providers, the AI Gateway page, the Spend page and pill, Ask g1t, and the repository's Agents, Plans and agent settings pages go too, and their addresses now answer 404. | 336 | /** One change that landed, as the Landed tab lists it. */ |
| The plan says what g1t is for without measuring it against other products | 337 | export type LandedRow = { |
| 338 | key: string; | |
| 339 | repo: RepoPath; | |
| 340 | ref: string; | |
| 341 | title: string; | |
| The site no longer has g1t's own agents. g1t is where the coding agents you already use do their work, so the Agents mode is gone from the rail, with every page under /<workspace>/-/agents: personas, faces, skills, abilities, routines, computers, sessions and agent spend. Customize (skills, connectors, plugins, marketplaces), the context hub, model providers, the AI Gateway page, the Spend page and pill, Ask g1t, and the repository's Agents, Plans and agent settings pages go too, and their addresses now answer 404. | 342 | /** Who merged it; g1t for a change that merged automatically. */ |
| The plan says what g1t is for without measuring it against other products | 343 | by: Who | null; |
| The site no longer has g1t's own agents. g1t is where the coding agents you already use do their work, so the Agents mode is gone from the rail, with every page under /<workspace>/-/agents: personas, faces, skills, abilities, routines, computers, sessions and agent spend. Customize (skills, connectors, plugins, marketplaces), the context hub, model providers, the AI Gateway page, the Spend page and pill, Ask g1t, and the repository's Agents, Plans and agent settings pages go too, and their addresses now answer 404. | 344 | automatic: boolean; |
| The plan says what g1t is for without measuring it against other products | 345 | at: number; |
| 346 | to: string; | |
| 347 | facts: Fact[]; | |
| 348 | }; | |
| 349 | ||
| 350 | /** What landed on the viewer's calendar day, newest first. */ | |
| 351 | export function landedToday(changes: Merged[], now: number, timeZone: string | null): LandedRow[] { | |
| 352 | const today = dayKey(now, timeZone); | |
| 353 | return changes | |
| 354 | .filter((change) => dayKey(Date.parse(change.mergedAt), timeZone) === today) | |
| 355 | .sort((a, b) => Date.parse(b.mergedAt) - Date.parse(a.mergedAt)) | |
| 356 | .map((change) => ({ | |
| 357 | key: pathKey(change.repo, change.number), | |
| 358 | repo: change.repo, | |
| 359 | ref: `#${change.number}`, | |
| 360 | title: change.title, | |
| 361 | by: who(change.mergedBy ?? "g1t"), | |
| The site no longer has g1t's own agents. g1t is where the coding agents you already use do their work, so the Agents mode is gone from the rail, with every page under /<workspace>/-/agents: personas, faces, skills, abilities, routines, computers, sessions and agent spend. Customize (skills, connectors, plugins, marketplaces), the context hub, model providers, the AI Gateway page, the Spend page and pill, Ask g1t, and the repository's Agents, Plans and agent settings pages go too, and their addresses now answer 404. | 362 | automatic: mergedAutomatically(change), |
| The plan says what g1t is for without measuring it against other products | 363 | at: Date.parse(change.mergedAt), |
| 364 | to: `/${change.repo.namespace}/${change.repo.name}/pull/${change.number}`, | |
| 365 | facts: pullFacts({ checkStatus: "passed", files: change.files }).filter((fact) => fact.label !== "Required checks"), | |
| 366 | })); | |
| 367 | } | |
| 368 | ||
| 369 | // --- Tabs and sorting ------------------------------------------------------- | |
| 370 | ||
| The site no longer has g1t's own agents. g1t is where the coding agents you already use do their work, so the Agents mode is gone from the rail, with every page under /<workspace>/-/agents: personas, faces, skills, abilities, routines, computers, sessions and agent spend. Customize (skills, connectors, plugins, marketplaces), the context hub, model providers, the AI Gateway page, the Spend page and pill, Ask g1t, and the repository's Agents, Plans and agent settings pages go too, and their addresses now answer 404. | 371 | export type Tab = "needs" | "landed"; |
| The plan says what g1t is for without measuring it against other products | 372 | export type Sort = "impact" | "newest"; |
| 373 | ||
| The site no longer has g1t's own agents. g1t is where the coding agents you already use do their work, so the Agents mode is gone from the rail, with every page under /<workspace>/-/agents: personas, faces, skills, abilities, routines, computers, sessions and agent spend. Customize (skills, connectors, plugins, marketplaces), the context hub, model providers, the AI Gateway page, the Spend page and pill, Ask g1t, and the repository's Agents, Plans and agent settings pages go too, and their addresses now answer 404. | 374 | export const TABS: Tab[] = ["needs", "landed"]; |
| The plan says what g1t is for without measuring it against other products | 375 | |
| 376 | export function parseTab(value: string | null): Tab | null { | |
| The site no longer has g1t's own agents. g1t is where the coding agents you already use do their work, so the Agents mode is gone from the rail, with every page under /<workspace>/-/agents: personas, faces, skills, abilities, routines, computers, sessions and agent spend. Customize (skills, connectors, plugins, marketplaces), the context hub, model providers, the AI Gateway page, the Spend page and pill, Ask g1t, and the repository's Agents, Plans and agent settings pages go too, and their addresses now answer 404. | 377 | return value === "needs" || value === "landed" ? value : null; |
| The plan says what g1t is for without measuring it against other products | 378 | } |
| 379 | ||
| 380 | export function parseSort(value: string | null): Sort { | |
| 381 | return value === "newest" ? "newest" : "impact"; | |
| 382 | } | |
| 383 | ||
| 384 | /** Rows in the order chosen: as ranked (most urgent first), or newest first. */ | |
| 385 | export function sortRows<T extends { at: number }>(rows: T[], sort: Sort): T[] { | |
| 386 | return sort === "newest" ? [...rows].sort((a, b) => b.at - a.at) : rows; | |
| 387 | } | |
| 388 | ||
| 389 | // --- The summary ------------------------------------------------------------ | |
| 390 | ||
| The site no longer has g1t's own agents. g1t is where the coding agents you already use do their work, so the Agents mode is gone from the rail, with every page under /<workspace>/-/agents: personas, faces, skills, abilities, routines, computers, sessions and agent spend. Customize (skills, connectors, plugins, marketplaces), the context hub, model providers, the AI Gateway page, the Spend page and pill, Ask g1t, and the repository's Agents, Plans and agent settings pages go too, and their addresses now answer 404. | 391 | /** The sentence under the title: the week, in one line. */ |
| 392 | export function summaryLine(input: { total: number; needs: number }): string { | |
| 393 | const { total, needs } = input; | |
| 394 | if (total > 0) return `${plural(total, "change")} landed in the last 7 days.`; | |
| The plan says what g1t is for without measuring it against other products | 395 | if (needs > 0) return "Nothing has landed this week. What is waiting on you is below."; |
| The site no longer has g1t's own agents. g1t is where the coding agents you already use do their work, so the Agents mode is gone from the rail, with every page under /<workspace>/-/agents: personas, faces, skills, abilities, routines, computers, sessions and agent spend. Customize (skills, connectors, plugins, marketplaces), the context hub, model providers, the AI Gateway page, the Spend page and pill, Ask g1t, and the repository's Agents, Plans and agent settings pages go too, and their addresses now answer 404. | 396 | return "Nothing has landed this week yet."; |
| The plan says what g1t is for without measuring it against other products | 397 | } |
| 398 | ||
| 399 | /** "Monday, Oct 5", in the viewer's zone. */ | |
| 400 | export function dateLine(now: number, timeZone: string | null): string { | |
| 401 | const options: Intl.DateTimeFormatOptions = { weekday: "long", month: "short", day: "numeric" }; | |
| 402 | try { | |
| 403 | return new Intl.DateTimeFormat("en-US", { ...options, timeZone: timeZone || "UTC" }).format(now); | |
| 404 | } catch { | |
| 405 | return new Intl.DateTimeFormat("en-US", { ...options, timeZone: "UTC" }).format(now); | |
| 406 | } | |
| 407 | } |
This file's history is long; its oldest lines are credited to the oldest commit read.