flagon-io/g1t

public

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/apps/web/app/lib/mission-control.test.ts

263 lines10,341 bytesCodeBlame
1import assert from "node:assert/strict";
2import { test } from "node:test";
3
4import {
5 type Merged,
6 change,
7 dayKey,
8 isTestFile,
9 landedByAgents,
10 landedToday,
11 parseSort,
12 parseTab,
13 pullFacts,
14 reachesBack,
15 reasonFor,
16 signedPercent,
17 sortRows,
18 stallReason,
19 summaryLine,
20 waitingRows,
21 weekOf,
22 whyFor,
23} from "./mission-control.ts";
24
25const HOUR = 3_600_000;
26const DAY = 24 * HOUR;
27// Monday 2026-10-05, 18:00 UTC.
28const NOW = Date.parse("2026-10-05T18:00:00Z");
29const repo = { namespace: "acme", name: "web" };
30
31test("each kind of need gets its reason chip", () => {
32 assert.equal(reasonFor({ kind: "deploy", detail: "" }), "blocking");
33 assert.equal(reasonFor({ kind: "limit", detail: "" }), "blocking");
34 assert.equal(reasonFor({ kind: "review", detail: "" }), "asked_for_you");
35 assert.equal(reasonFor({ kind: "invitation", detail: "" }), "asked_for_you");
36 assert.equal(reasonFor({ kind: "ready", detail: "" }), "ready_to_merge");
37 assert.equal(reasonFor({ kind: "checks", detail: "" }), "checks_failing");
38 assert.equal(reasonFor({ kind: "stuck", detail: "" }), "stalled");
39});
40
41test("a stopped pull request's reason comes from the sentence g1t stopped with", () => {
42 assert.equal(stallReason("The acceptance checks still fail after the agent revised 3 times."), "checks_failing");
43 assert.equal(stallReason("CI / push still fails after the agent revised 2 times."), "checks_failing");
44 assert.equal(stallReason("The acceptance checks could not be run."), "checks_failing");
45 assert.equal(
46 stallReason("g1t stopped the agent's implement run when it reached its cost cap. Raise the cap under Settings, Guardrails."),
47 "outside_guardrails",
48 );
49 assert.equal(stallReason("g1t stopped the agent's review run when it reached its time cap."), "outside_guardrails");
50 assert.equal(stallReason("g1t could not merge this: the branch moved"), "blocking");
51 assert.equal(stallReason("Needs 1 approving review from someone with write access."), "needs_review");
52 assert.equal(stallReason("The review still asks for changes after the agent revised 3 times."), "needs_review");
53 assert.equal(stallReason("alex asked for changes, and the agent has already revised 3 times."), "needs_review");
54 assert.equal(stallReason("syntaqx stopped the agent's implement run."), "stalled");
55 assert.equal(stallReason("g1t could not start the next step: no slot"), "stalled");
56});
57
58test("why a need needs a person is said for every reason", () => {
59 for (const kind of ["deploy", "limit", "invitation", "review", "stuck", "checks", "ready", "conflict"] as const) {
60 const need = { kind, detail: "" };
61 assert.ok(whyFor(reasonFor(need), need).length > 20, kind);
62 }
63 const stalled = { kind: "stalled" as const, detail: "It reached its cost cap." };
64 assert.match(whyFor(reasonFor(stalled), stalled), /Guardrails/);
65});
66
67test("what the agent knows lists only what is known", () => {
68 const facts = pullFacts({
69 checkStatus: "failed",
70 files: [
71 { path: "src/retry.ts", additions: 40, deletions: 2 },
72 { path: "src/retry.test.ts", additions: 60, deletions: 0 },
73 ],
74 lifecycle: { revisions: 2 },
75 runs: [
76 { kind: "implement", costUsd: 0.5 },
77 { kind: "revise", costUsd: 0.25 },
78 { kind: "checks", costUsd: null },
79 ],
80 });
81 const by = Object.fromEntries(facts.map((f) => [f.label, f]));
82 assert.equal(by.Checks.value, "Failing");
83 assert.equal(by.Checks.tone, "bad");
84 assert.equal(by["Files changed"].value, "2");
85 assert.equal(by.Lines.value, "+100 −2");
86 assert.equal(by.Tests.value, "1 file");
87 assert.equal(by["Sent back"].value, "2 times");
88 assert.equal(by["Agent runs"].value, "2 · $0.75");
89
90 const bare = pullFacts({ checkStatus: null, files: [] });
91 assert.deepEqual(bare, [{ label: "Checks", value: "Not run", tone: null }]);
92});
93
94test("test files are recognised by the names test runners use", () => {
95 assert.ok(isTestFile("src/a.test.ts"));
96 assert.ok(isTestFile("src/__tests__/a.ts"));
97 assert.ok(isTestFile("tests/api.rs"));
98 assert.ok(isTestFile("pkg/store_test.go"));
99 assert.ok(!isTestFile("src/testing-utils.ts"));
100 assert.ok(!isTestFile("src/contest.ts"));
101});
102
103const merged = (daysAgo: number, mergedBy: string | null, number = 1): Merged => ({
104 repo,
105 number,
106 title: `Change ${number}`,
107 agent: "g1t-agent",
108 mergedBy,
109 mergedAt: new Date(NOW - daysAgo * DAY).toISOString(),
110 files: [],
111});
112
113test("a change landed without a person when g1t merged it", () => {
114 assert.ok(landedByAgents({ mergedBy: "g1t" }));
115 assert.ok(landedByAgents({ mergedBy: "g1t-agent" }));
116 assert.ok(landedByAgents({ mergedBy: null }));
117 assert.ok(!landedByAgents({ mergedBy: "syntaqx" }));
118});
119
120test("the week is seven days, each split by who landed it, with the week before", () => {
121 const week = weekOf(
122 [merged(0, "g1t"), merged(0, "syntaqx"), merged(1, "g1t"), merged(6, "g1t"), merged(8, "g1t"), merged(10, "alex"), merged(20, "g1t")],
123 NOW,
124 "UTC",
125 );
126 assert.equal(week.days.length, 7);
127 assert.equal(week.days[6].key, "2026-10-05");
128 assert.equal(week.days[6].label, "Mon");
129 assert.deepEqual(
130 week.days.map((d) => [d.agents, d.people]),
131 [[1, 0], [0, 0], [0, 0], [0, 0], [0, 0], [1, 0], [1, 1]],
132 );
133 assert.equal(week.total, 4);
134 assert.equal(week.byAgents, 3);
135 assert.equal(week.previous, 2);
136 // Not knowing the week before is not the same as nothing in it.
137 assert.equal(weekOf([merged(0, "g1t")], NOW, "UTC", false).previous, null);
138});
139
140test("days follow the viewer's time zone", () => {
141 // 02:00 UTC on the 5th is still the 4th in Los Angeles.
142 const late = Date.parse("2026-10-05T02:00:00Z");
143 assert.equal(dayKey(late, "America/Los_Angeles"), "2026-10-04");
144 assert.equal(dayKey(late, "UTC"), "2026-10-05");
145 assert.equal(dayKey(late, "Not/AZone"), "2026-10-05");
146 const week = weekOf([{ mergedAt: new Date(late).toISOString(), mergedBy: "g1t" }], NOW, "America/Los_Angeles");
147 assert.equal(week.days.find((d) => d.key === "2026-10-04")?.agents, 1);
148});
149
150test("the change from last week, and how it reads", () => {
151 assert.equal(change(12, 10), 0.2);
152 assert.equal(change(5, 0), null);
153 assert.equal(change(5, null), null);
154 assert.equal(signedPercent(0.2), "+20%");
155 assert.equal(signedPercent(-0.054), "−5%");
156 assert.equal(signedPercent(0.001), "0%");
157});
158
159test("a repository's merged list reaches back when it is not full or its oldest is old enough", () => {
160 const since = NOW - 14 * DAY;
161 const at = (days: number) => ({ mergedAt: new Date(NOW - days * DAY).toISOString(), updatedAt: new Date(NOW).toISOString() });
162 assert.ok(reachesBack([at(1), at(2)], since, 100));
163 assert.ok(reachesBack([at(1), at(20)], since, 2));
164 assert.ok(!reachesBack([at(1), at(3)], since, 2));
165});
166
167test("landed today is the viewer's calendar day, newest first", () => {
168 const rows = landedToday([merged(0.1, "g1t", 3), merged(0.5, "syntaqx", 4), merged(1, "g1t", 2)], NOW, "UTC");
169 assert.deepEqual(
170 rows.map((r) => [r.ref, r.byAgents]),
171 [
172 ["#3", true],
173 ["#4", false],
174 ],
175 );
176 assert.equal(rows[1].by?.name, "syntaqx");
177});
178
179test("waiting on agents holds each pull request once, running ones first, and nothing that needs you", () => {
180 const pull = (number: number, minutesAgo: number) => ({
181 number,
182 title: `Pull ${number}`,
183 agent: "g1t-agent",
184 updatedAt: new Date(NOW - minutesAgo * 60_000).toISOString(),
185 checkStatus: null,
186 files: [],
187 });
188 const rows = waitingRows({
189 active: [
190 { pull: pull(1, 30), lifecycle: { stage: "checking", detail: "The acceptance checks are running.", revisions: 0 }, repo },
191 { pull: pull(2, 5), lifecycle: { stage: "needs_you", detail: "Stopped.", revisions: 0 }, repo },
192 { pull: pull(3, 50), lifecycle: { stage: "queued", detail: "In the merge queue.", revisions: 0 }, repo },
193 { pull: pull(9, 1), lifecycle: { stage: "reviewing", detail: "Reviewing.", revisions: 0 }, repo },
194 ],
195 live: [
196 {
197 id: "r1",
198 repo,
199 number: 3,
200 title: "Pull 3",
201 kind: "update",
202 agent: "g1t-agent",
203 step: "Merging main in",
204 costUsd: 0.1,
205 startedAt: new Date(NOW - 60_000).toISOString(),
206 createdAt: new Date(NOW - 60_000).toISOString(),
207 updatedAt: new Date(NOW - 10_000).toISOString(),
208 },
209 {
210 id: "r2",
211 repo,
212 number: null,
213 title: null,
214 kind: "plan",
215 agent: "g1t-agent",
216 step: null,
217 costUsd: null,
218 startedAt: null,
219 createdAt: new Date(NOW - 120_000).toISOString(),
220 updatedAt: new Date(NOW - 120_000).toISOString(),
221 },
222 ],
223 drafts: [
224 { ...pull(1, 30), repo },
225 { ...pull(4, 20), repo },
226 { ...pull(5, 20), agent: "syntaqx", repo },
227 ],
228 needKeys: new Set(["acme/web#9"]),
229 });
230 assert.deepEqual(
231 rows.map((r) => [r.ref, r.chip, r.live]),
232 [
233 ["#3", "In queue", true],
234 [null, "Planning", true],
235 ["#4", "Working", false],
236 ["#1", "Checking", false],
237 ],
238 );
239 assert.equal(rows[0].detail, "Merging main in");
240 assert.equal(rows[0].run, "/acme/web/agents/runs/r1");
241 assert.equal(rows[1].title, "Planning in web");
242});
243
244test("tabs and sorting come from the address", () => {
245 assert.equal(parseTab("waiting"), "waiting");
246 assert.equal(parseTab("nope"), null);
247 assert.equal(parseSort("newest"), "newest");
248 assert.equal(parseSort(null), "impact");
249 const rows = [{ at: 1 }, { at: 3 }, { at: 2 }];
250 assert.deepEqual(sortRows(rows, "impact"), rows);
251 assert.deepEqual(
252 sortRows(rows, "newest").map((r) => r.at),
253 [3, 2, 1],
254 );
255});
256
257test("the summary says the week honestly", () => {
258 assert.equal(summaryLine({ total: 47, byAgents: 39, live: 2, needs: 8 }), "Agents landed 39 of 47 changes this week without you.");
259 assert.equal(summaryLine({ total: 3, byAgents: 3, live: 0, needs: 0 }), "Agents landed all 3 changes this week without you.");
260 assert.equal(summaryLine({ total: 2, byAgents: 0, live: 0, needs: 0 }), "2 changes landed this week, each merged by a person.");
261 assert.equal(summaryLine({ total: 0, byAgents: 0, live: 1, needs: 0 }), "1 agent is at work. Nothing has landed this week yet.");
262 assert.match(summaryLine({ total: 0, byAgents: 0, live: 0, needs: 0 }), /Assign an issue/);
263});