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.
| Mission control shows where you are needed and what agents landed without you; git answers in about 200ms | 1 | import assert from "node:assert/strict"; |
| 2 | import { test } from "node:test"; | |
| 3 | ||
| 4 | import { | |
| 5 | type Merged, | |
| 6 | change, | |
| Fast pages, required checks on the branch, self-hosted runners, honest incidents | 7 | checksFact, |
| Thirteen MCP tools and classic token scopes; agents rate their confidence and can be put on an issue in one step | 8 | confidenceAsk, |
| 9 | confidenceLine, | |
| Mission control shows where you are needed and what agents landed without you; git answers in about 200ms | 10 | dayKey, |
| 11 | isTestFile, | |
| 12 | landedByAgents, | |
| 13 | landedToday, | |
| 14 | parseSort, | |
| 15 | parseTab, | |
| 16 | pullFacts, | |
| 17 | reachesBack, | |
| 18 | reasonFor, | |
| 19 | signedPercent, | |
| 20 | sortRows, | |
| 21 | stallReason, | |
| 22 | summaryLine, | |
| Mission control counts people's direct pushes to the default branch | 23 | pushedCommits, |
| Mission control shows where you are needed and what agents landed without you; git answers in about 200ms | 24 | waitingRows, |
| 25 | weekOf, | |
| 26 | whyFor, | |
| Thirteen MCP tools and classic token scopes; agents rate their confidence and can be put on an issue in one step | 27 | withConfidence, |
| Mission control shows where you are needed and what agents landed without you; git answers in about 200ms | 28 | } from "./mission-control.ts"; |
| 29 | ||
| 30 | const HOUR = 3_600_000; | |
| 31 | const DAY = 24 * HOUR; | |
| 32 | // Monday 2026-10-05, 18:00 UTC. | |
| 33 | const NOW = Date.parse("2026-10-05T18:00:00Z"); | |
| 34 | const repo = { namespace: "acme", name: "web" }; | |
| 35 | ||
| 36 | test("each kind of need gets its reason chip", () => { | |
| 37 | assert.equal(reasonFor({ kind: "deploy", detail: "" }), "blocking"); | |
| 38 | assert.equal(reasonFor({ kind: "limit", detail: "" }), "blocking"); | |
| 39 | assert.equal(reasonFor({ kind: "review", detail: "" }), "asked_for_you"); | |
| 40 | assert.equal(reasonFor({ kind: "invitation", detail: "" }), "asked_for_you"); | |
| 41 | assert.equal(reasonFor({ kind: "ready", detail: "" }), "ready_to_merge"); | |
| 42 | assert.equal(reasonFor({ kind: "checks", detail: "" }), "checks_failing"); | |
| 43 | assert.equal(reasonFor({ kind: "stuck", detail: "" }), "stalled"); | |
| 44 | }); | |
| 45 | ||
| 46 | test("a stopped pull request's reason comes from the sentence g1t stopped with", () => { | |
| Fast pages, required checks on the branch, self-hosted runners, honest incidents | 47 | assert.equal(stallReason("The required check CI still fails after the agent revised 3 times."), "checks_failing"); |
| 48 | assert.equal(stallReason("The required checks CI and Lint still fail after the agent revised twice."), "checks_failing"); | |
| Mission control shows where you are needed and what agents landed without you; git answers in about 200ms | 49 | assert.equal(stallReason("CI / push still fails after the agent revised 2 times."), "checks_failing"); |
| Fast pages, required checks on the branch, self-hosted runners, honest incidents | 50 | assert.equal(stallReason("It failed in the merge queue after the agent revised twice."), "checks_failing"); |
| 51 | assert.equal(stallReason("Its checks could not be run."), "checks_failing"); | |
| Mission control shows where you are needed and what agents landed without you; git answers in about 200ms | 52 | assert.equal( |
| 53 | stallReason("g1t stopped the agent's implement run when it reached its cost cap. Raise the cap under Settings, Guardrails."), | |
| 54 | "outside_guardrails", | |
| 55 | ); | |
| 56 | assert.equal(stallReason("g1t stopped the agent's review run when it reached its time cap."), "outside_guardrails"); | |
| 57 | assert.equal(stallReason("g1t could not merge this: the branch moved"), "blocking"); | |
| 58 | assert.equal(stallReason("Needs 1 approving review from someone with write access."), "needs_review"); | |
| 59 | assert.equal(stallReason("The review still asks for changes after the agent revised 3 times."), "needs_review"); | |
| 60 | assert.equal(stallReason("alex asked for changes, and the agent has already revised 3 times."), "needs_review"); | |
| 61 | assert.equal(stallReason("syntaqx stopped the agent's implement run."), "stalled"); | |
| 62 | assert.equal(stallReason("g1t could not start the next step: no slot"), "stalled"); | |
| 63 | }); | |
| 64 | ||
| 65 | test("why a need needs a person is said for every reason", () => { | |
| 66 | for (const kind of ["deploy", "limit", "invitation", "review", "stuck", "checks", "ready", "conflict"] as const) { | |
| 67 | const need = { kind, detail: "" }; | |
| 68 | assert.ok(whyFor(reasonFor(need), need).length > 20, kind); | |
| 69 | } | |
| 70 | const stalled = { kind: "stalled" as const, detail: "It reached its cost cap." }; | |
| 71 | assert.match(whyFor(reasonFor(stalled), stalled), /Guardrails/); | |
| 72 | }); | |
| 73 | ||
| 74 | test("what the agent knows lists only what is known", () => { | |
| 75 | const facts = pullFacts({ | |
| 76 | checkStatus: "failed", | |
| 77 | files: [ | |
| 78 | { path: "src/retry.ts", additions: 40, deletions: 2 }, | |
| 79 | { path: "src/retry.test.ts", additions: 60, deletions: 0 }, | |
| 80 | ], | |
| 81 | lifecycle: { revisions: 2 }, | |
| 82 | runs: [ | |
| 83 | { kind: "implement", costUsd: 0.5 }, | |
| 84 | { kind: "revise", costUsd: 0.25 }, | |
| 85 | { kind: "checks", costUsd: null }, | |
| 86 | ], | |
| 87 | }); | |
| 88 | const by = Object.fromEntries(facts.map((f) => [f.label, f])); | |
| Fast pages, required checks on the branch, self-hosted runners, honest incidents | 89 | assert.equal(by["Required checks"].value, "Failed in the merge queue"); |
| 90 | assert.equal(by["Required checks"].tone, "bad"); | |
| Mission control shows where you are needed and what agents landed without you; git answers in about 200ms | 91 | assert.equal(by["Files changed"].value, "2"); |
| 92 | assert.equal(by.Lines.value, "+100 −2"); | |
| 93 | assert.equal(by.Tests.value, "1 file"); | |
| 94 | assert.equal(by["Sent back"].value, "2 times"); | |
| 95 | assert.equal(by["Agent runs"].value, "2 · $0.75"); | |
| 96 | ||
| 97 | const bare = pullFacts({ checkStatus: null, files: [] }); | |
| Fast pages, required checks on the branch, self-hosted runners, honest incidents | 98 | assert.deepEqual(bare, [{ label: "Required checks", value: "Not known yet", tone: null }]); |
| 99 | }); | |
| 100 | ||
| 101 | test("the required checks are read from where the agent's pull request stands", () => { | |
| 102 | const at = (stage: Parameters<typeof checksFact>[1] extends infer L ? NonNullable<L>["stage"] : never, detail: string) => | |
| 103 | checksFact(null, { stage, detail }); | |
| 104 | assert.deepEqual(at("checking", "Waiting for CI / pull_request to finish."), { label: "Required checks", value: "Running", tone: null }); | |
| 105 | assert.equal(at("checking", "Waiting for the required check Deploy to report on its latest commit.").value, "Not reported yet"); | |
| 106 | assert.equal(at("revising", "CI / pull_request failed. The agent is being sent back to fix it.").value, "Failing"); | |
| 107 | assert.equal(at("needs_you", "The required check CI still fails after the agent revised twice.").value, "Failing"); | |
| 108 | // Past the checks: a review, an approval or a person's decision holds it, not them. | |
| 109 | assert.equal(at("revising", "The review asked for changes. The agent is being sent back to make them.").value, "Passing"); | |
| 110 | assert.equal(at("needs_you", "This repository requires 1 approving review before a pull request merges; this one has 0.").value, "Passing"); | |
| 111 | assert.equal(at("ready", "Everything this repository asks for is met. Ready to merge.").tone, "good"); | |
| g1t is one name: its agent's work, commits and comments show as @g1t, and nobody can claim g1t or g1t-agent | 112 | assert.equal(at("working", "g1t is making the change.").value, "Not run yet"); |
| Fast pages, required checks on the branch, self-hosted runners, honest incidents | 113 | assert.equal(checksFact("failed", { stage: "revising", detail: "It failed in the merge queue." }).value, "Failed in the merge queue"); |
| Mission control shows where you are needed and what agents landed without you; git answers in about 200ms | 114 | }); |
| 115 | ||
| Thirteen MCP tools and classic token scopes; agents rate their confidence and can be put on an issue in one step | 116 | test("a change held for low confidence is its own reason, ahead of what it would read as", () => { |
| 117 | const held = | |
| 118 | "The agent's confidence in this change is low (checks failing, tests not added). This repository asks a person before merging it: approve it to let it land, or ask for changes."; | |
| 119 | // Its reasons name checks and approval, but it is held for its confidence. | |
| 120 | assert.equal(stallReason(held), "low_confidence"); | |
| 121 | assert.equal(reasonFor({ kind: "stalled", detail: held }), "low_confidence"); | |
| 122 | ||
| 123 | const low = { level: "low" as const, reasons: ["tests not added", "3 revisions"], uncertainAbout: ["the retry limit"] }; | |
| 124 | assert.equal(withConfidence("ready_to_merge", low), "low_confidence"); | |
| 125 | assert.equal(withConfidence("needs_review", low), "low_confidence"); | |
| 126 | // A failure that needs someone anyway keeps its own chip. | |
| 127 | assert.equal(withConfidence("checks_failing", low), "checks_failing"); | |
| 128 | assert.equal(withConfidence("ready_to_merge", { level: "medium" }), "ready_to_merge"); | |
| 129 | assert.equal(withConfidence("ready_to_merge", null), "ready_to_merge"); | |
| 130 | ||
| 131 | assert.equal(confidenceLine(low), "Low — tests not added, 3 revisions"); | |
| 132 | assert.equal(confidenceLine({ level: "high", reasons: [] }), "High"); | |
| 133 | assert.match(confidenceAsk(low), /not sure of the change: tests not added, 3 revisions\. Approve it/); | |
| 134 | ||
| 135 | const why = whyFor("low_confidence", { kind: "stalled", detail: held }, low); | |
| 136 | assert.match(why, /from tests not added, 3 revisions\./); | |
| 137 | assert.match(why, /unsure about the retry limit/); | |
| 138 | assert.match(why, /nothing merges it until you approve it/); | |
| 139 | // Ready, not held: it says to look before merging. | |
| 140 | assert.match(whyFor("low_confidence", { kind: "ready", detail: "" }, low), /Look at it before you merge it/); | |
| 141 | }); | |
| 142 | ||
| 143 | test("what the agent knows gains its confidence, across the row", () => { | |
| 144 | const facts = pullFacts({ | |
| 145 | checkStatus: "passed", | |
| 146 | files: [{ path: "src/retry.ts", additions: 40, deletions: 2 }], | |
| 147 | lifecycle: { revisions: 3 }, | |
| 148 | confidence: { level: "low", reasons: ["tests not added", "3 revisions"] }, | |
| 149 | }); | |
| 150 | const fact = facts.find((f) => f.label === "Confidence"); | |
| 151 | assert.deepEqual(fact, { label: "Confidence", value: "Low — tests not added, 3 revisions", tone: "bad", wide: true }); | |
| 152 | assert.equal(facts.at(-1)?.label, "Confidence"); | |
| 153 | }); | |
| 154 | ||
| Mission control shows where you are needed and what agents landed without you; git answers in about 200ms | 155 | test("test files are recognised by the names test runners use", () => { |
| 156 | assert.ok(isTestFile("src/a.test.ts")); | |
| 157 | assert.ok(isTestFile("src/__tests__/a.ts")); | |
| 158 | assert.ok(isTestFile("tests/api.rs")); | |
| 159 | assert.ok(isTestFile("pkg/store_test.go")); | |
| 160 | assert.ok(!isTestFile("src/testing-utils.ts")); | |
| 161 | assert.ok(!isTestFile("src/contest.ts")); | |
| A file named test.js or spec.rb counts as a test: confidence no longer says tests not added when they were | 162 | assert.ok(isTestFile("test.js")); |
| 163 | assert.ok(isTestFile("lib/spec.rb")); | |
| Mission control shows where you are needed and what agents landed without you; git answers in about 200ms | 164 | }); |
| 165 | ||
| Mission control counts people's own changes as theirs; a shorter sidebar with setup under Settings, and a 56px top bar | 166 | const merged = (daysAgo: number, mergedBy: string | null, number = 1, authoredByAgent = true): Merged => ({ |
| Mission control shows where you are needed and what agents landed without you; git answers in about 200ms | 167 | repo, |
| 168 | number, | |
| 169 | title: `Change ${number}`, | |
| g1t is one name: its agent's work, commits and comments show as @g1t, and nobody can claim g1t or g1t-agent | 170 | agent: "g1t", |
| Mission control counts people's own changes as theirs; a shorter sidebar with setup under Settings, and a 56px top bar | 171 | authoredByAgent, |
| Mission control shows where you are needed and what agents landed without you; git answers in about 200ms | 172 | mergedBy, |
| 173 | mergedAt: new Date(NOW - daysAgo * DAY).toISOString(), | |
| 174 | files: [], | |
| 175 | }); | |
| 176 | ||
| Mission control counts people's direct pushes to the default branch | 177 | test("a push to the default branch counts a person's own commits, not merges or agents'", () => { |
| 178 | const c = (hash: string, author: string, parents = 1) => ({ hash, author: { name: author }, parents: Array(parents).fill("p") }); | |
| 179 | const history = [c("e", "Chase"), c("d", "g1t"), c("m", "g1t", 2), c("b", "Chase"), c("a", "Chase")]; | |
| 180 | assert.deepEqual(pushedCommits(history, "a").map((x) => x.hash), ["e", "b"]); | |
| 181 | // Where it pointed before was not read: everything read counts. | |
| 182 | assert.deepEqual(pushedCommits(history, "zz").map((x) => x.hash), ["e", "b", "a"]); | |
| 183 | }); | |
| 184 | ||
| Mission control shows where you are needed and what agents landed without you; git answers in about 200ms | 185 | test("a change landed without a person when g1t merged it", () => { |
| 186 | assert.ok(landedByAgents({ mergedBy: "g1t" })); | |
| g1t is one name: its agent's work, commits and comments show as @g1t, and nobody can claim g1t or g1t-agent | 187 | assert.ok(landedByAgents({ mergedBy: "g1t" })); |
| Mission control shows where you are needed and what agents landed without you; git answers in about 200ms | 188 | assert.ok(landedByAgents({ mergedBy: null })); |
| 189 | assert.ok(!landedByAgents({ mergedBy: "syntaqx" })); | |
| 190 | }); | |
| 191 | ||
| Mission control counts people's own changes as theirs; a shorter sidebar with setup under Settings, and a 56px top bar | 192 | test("the week is seven days, each split by who did the work, with the week before", () => { |
| Mission control shows where you are needed and what agents landed without you; git answers in about 200ms | 193 | const week = weekOf( |
| Mission control counts people's own changes as theirs; a shorter sidebar with setup under Settings, and a 56px top bar | 194 | [ |
| 195 | merged(0, "g1t"), | |
| 196 | merged(0, "syntaqx"), | |
| 197 | // A person's own change, merged by them and auto-merged by g1t: both theirs. | |
| 198 | merged(0, "syntaqx", 2, false), | |
| 199 | merged(1, "g1t", 3, false), | |
| 200 | merged(1, "g1t"), | |
| 201 | merged(6, "g1t"), | |
| 202 | merged(8, "g1t"), | |
| 203 | merged(10, "alex"), | |
| 204 | merged(20, "g1t"), | |
| 205 | ], | |
| Mission control shows where you are needed and what agents landed without you; git answers in about 200ms | 206 | NOW, |
| 207 | "UTC", | |
| 208 | ); | |
| 209 | assert.equal(week.days.length, 7); | |
| 210 | assert.equal(week.days[6].key, "2026-10-05"); | |
| 211 | assert.equal(week.days[6].label, "Mon"); | |
| 212 | assert.deepEqual( | |
| Mission control counts people's own changes as theirs; a shorter sidebar with setup under Settings, and a 56px top bar | 213 | week.days.map((d) => [d.agents, d.assisted, d.people]), |
| 214 | [[1, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [1, 0, 1], [1, 1, 1]], | |
| Mission control shows where you are needed and what agents landed without you; git answers in about 200ms | 215 | ); |
| Mission control counts people's own changes as theirs; a shorter sidebar with setup under Settings, and a 56px top bar | 216 | assert.equal(week.total, 6); |
| Mission control shows where you are needed and what agents landed without you; git answers in about 200ms | 217 | assert.equal(week.byAgents, 3); |
| Mission control counts people's own changes as theirs; a shorter sidebar with setup under Settings, and a 56px top bar | 218 | assert.equal(week.agentChanges, 4); |
| 219 | assert.equal(week.people, 2); | |
| Mission control shows where you are needed and what agents landed without you; git answers in about 200ms | 220 | assert.equal(week.previous, 2); |
| 221 | // Not knowing the week before is not the same as nothing in it. | |
| 222 | assert.equal(weekOf([merged(0, "g1t")], NOW, "UTC", false).previous, null); | |
| 223 | }); | |
| 224 | ||
| 225 | test("days follow the viewer's time zone", () => { | |
| 226 | // 02:00 UTC on the 5th is still the 4th in Los Angeles. | |
| 227 | const late = Date.parse("2026-10-05T02:00:00Z"); | |
| 228 | assert.equal(dayKey(late, "America/Los_Angeles"), "2026-10-04"); | |
| 229 | assert.equal(dayKey(late, "UTC"), "2026-10-05"); | |
| 230 | assert.equal(dayKey(late, "Not/AZone"), "2026-10-05"); | |
| Mission control counts people's own changes as theirs; a shorter sidebar with setup under Settings, and a 56px top bar | 231 | const week = weekOf([{ mergedAt: new Date(late).toISOString(), mergedBy: "g1t", authoredByAgent: true }], NOW, "America/Los_Angeles"); |
| Mission control shows where you are needed and what agents landed without you; git answers in about 200ms | 232 | assert.equal(week.days.find((d) => d.key === "2026-10-04")?.agents, 1); |
| 233 | }); | |
| 234 | ||
| 235 | test("the change from last week, and how it reads", () => { | |
| 236 | assert.equal(change(12, 10), 0.2); | |
| 237 | assert.equal(change(5, 0), null); | |
| 238 | assert.equal(change(5, null), null); | |
| 239 | assert.equal(signedPercent(0.2), "+20%"); | |
| 240 | assert.equal(signedPercent(-0.054), "−5%"); | |
| 241 | assert.equal(signedPercent(0.001), "0%"); | |
| 242 | }); | |
| 243 | ||
| 244 | test("a repository's merged list reaches back when it is not full or its oldest is old enough", () => { | |
| 245 | const since = NOW - 14 * DAY; | |
| 246 | const at = (days: number) => ({ mergedAt: new Date(NOW - days * DAY).toISOString(), updatedAt: new Date(NOW).toISOString() }); | |
| 247 | assert.ok(reachesBack([at(1), at(2)], since, 100)); | |
| 248 | assert.ok(reachesBack([at(1), at(20)], since, 2)); | |
| 249 | assert.ok(!reachesBack([at(1), at(3)], since, 2)); | |
| 250 | }); | |
| 251 | ||
| 252 | test("landed today is the viewer's calendar day, newest first", () => { | |
| 253 | const rows = landedToday([merged(0.1, "g1t", 3), merged(0.5, "syntaqx", 4), merged(1, "g1t", 2)], NOW, "UTC"); | |
| 254 | assert.deepEqual( | |
| 255 | rows.map((r) => [r.ref, r.byAgents]), | |
| 256 | [ | |
| 257 | ["#3", true], | |
| 258 | ["#4", false], | |
| 259 | ], | |
| 260 | ); | |
| 261 | assert.equal(rows[1].by?.name, "syntaqx"); | |
| 262 | }); | |
| 263 | ||
| 264 | test("waiting on agents holds each pull request once, running ones first, and nothing that needs you", () => { | |
| 265 | const pull = (number: number, minutesAgo: number) => ({ | |
| 266 | number, | |
| 267 | title: `Pull ${number}`, | |
| g1t is one name: its agent's work, commits and comments show as @g1t, and nobody can claim g1t or g1t-agent | 268 | agent: "g1t", |
| Mission control shows where you are needed and what agents landed without you; git answers in about 200ms | 269 | updatedAt: new Date(NOW - minutesAgo * 60_000).toISOString(), |
| 270 | checkStatus: null, | |
| 271 | files: [], | |
| 272 | }); | |
| 273 | const rows = waitingRows({ | |
| 274 | active: [ | |
| Fast pages, required checks on the branch, self-hosted runners, honest incidents | 275 | { pull: pull(1, 30), lifecycle: { stage: "checking", detail: "Waiting for CI / pull_request to finish.", revisions: 0 }, repo }, |
| Mission control shows where you are needed and what agents landed without you; git answers in about 200ms | 276 | { pull: pull(2, 5), lifecycle: { stage: "needs_you", detail: "Stopped.", revisions: 0 }, repo }, |
| 277 | { pull: pull(3, 50), lifecycle: { stage: "queued", detail: "In the merge queue.", revisions: 0 }, repo }, | |
| 278 | { pull: pull(9, 1), lifecycle: { stage: "reviewing", detail: "Reviewing.", revisions: 0 }, repo }, | |
| 279 | ], | |
| 280 | live: [ | |
| 281 | { | |
| 282 | id: "r1", | |
| 283 | repo, | |
| 284 | number: 3, | |
| 285 | title: "Pull 3", | |
| 286 | kind: "update", | |
| g1t is one name: its agent's work, commits and comments show as @g1t, and nobody can claim g1t or g1t-agent | 287 | agent: "g1t", |
| Mission control shows where you are needed and what agents landed without you; git answers in about 200ms | 288 | step: "Merging main in", |
| 289 | costUsd: 0.1, | |
| 290 | startedAt: new Date(NOW - 60_000).toISOString(), | |
| 291 | createdAt: new Date(NOW - 60_000).toISOString(), | |
| 292 | updatedAt: new Date(NOW - 10_000).toISOString(), | |
| 293 | }, | |
| 294 | { | |
| 295 | id: "r2", | |
| 296 | repo, | |
| 297 | number: null, | |
| 298 | title: null, | |
| 299 | kind: "plan", | |
| g1t is one name: its agent's work, commits and comments show as @g1t, and nobody can claim g1t or g1t-agent | 300 | agent: "g1t", |
| Mission control shows where you are needed and what agents landed without you; git answers in about 200ms | 301 | step: null, |
| 302 | costUsd: null, | |
| 303 | startedAt: null, | |
| 304 | createdAt: new Date(NOW - 120_000).toISOString(), | |
| 305 | updatedAt: new Date(NOW - 120_000).toISOString(), | |
| 306 | }, | |
| 307 | ], | |
| 308 | drafts: [ | |
| 309 | { ...pull(1, 30), repo }, | |
| 310 | { ...pull(4, 20), repo }, | |
| 311 | { ...pull(5, 20), agent: "syntaqx", repo }, | |
| 312 | ], | |
| 313 | needKeys: new Set(["acme/web#9"]), | |
| 314 | }); | |
| 315 | assert.deepEqual( | |
| 316 | rows.map((r) => [r.ref, r.chip, r.live]), | |
| 317 | [ | |
| 318 | ["#3", "In queue", true], | |
| 319 | [null, "Planning", true], | |
| 320 | ["#4", "Working", false], | |
| 321 | ["#1", "Checking", false], | |
| 322 | ], | |
| 323 | ); | |
| 324 | assert.equal(rows[0].detail, "Merging main in"); | |
| 325 | assert.equal(rows[0].run, "/acme/web/agents/runs/r1"); | |
| 326 | assert.equal(rows[1].title, "Planning in web"); | |
| 327 | }); | |
| 328 | ||
| 329 | test("tabs and sorting come from the address", () => { | |
| 330 | assert.equal(parseTab("waiting"), "waiting"); | |
| 331 | assert.equal(parseTab("nope"), null); | |
| 332 | assert.equal(parseSort("newest"), "newest"); | |
| 333 | assert.equal(parseSort(null), "impact"); | |
| 334 | const rows = [{ at: 1 }, { at: 3 }, { at: 2 }]; | |
| 335 | assert.deepEqual(sortRows(rows, "impact"), rows); | |
| 336 | assert.deepEqual( | |
| 337 | sortRows(rows, "newest").map((r) => r.at), | |
| 338 | [3, 2, 1], | |
| 339 | ); | |
| 340 | }); | |
| 341 | ||
| 342 | test("the summary says the week honestly", () => { | |
| Mission control counts people's own changes as theirs; a shorter sidebar with setup under Settings, and a 56px top bar | 343 | const none = { total: 0, byAgents: 0, agentChanges: 0, people: 0, live: 0, needs: 0 }; |
| 344 | assert.equal( | |
| 345 | summaryLine({ ...none, total: 47, byAgents: 39, agentChanges: 47 }), | |
| 346 | "Agents landed 39 of their 47 changes this week without you.", | |
| 347 | ); | |
| 348 | assert.equal( | |
| 349 | summaryLine({ ...none, total: 3, byAgents: 3, agentChanges: 3 }), | |
| 350 | "Agents landed all 3 changes of theirs this week without you.", | |
| 351 | ); | |
| 352 | assert.equal( | |
| 353 | summaryLine({ ...none, total: 2, agentChanges: 2 }), | |
| 354 | "Agents made 2 changes this week, each merged by a person.", | |
| 355 | ); | |
| 356 | // People's own work is counted as theirs, never as agents' that needed help. | |
| 357 | assert.equal( | |
| 358 | summaryLine({ ...none, total: 7, byAgents: 4, agentChanges: 5, people: 2 }), | |
| 359 | "Agents landed 4 of their 5 changes this week without you, and people landed 2 changes of their own.", | |
| 360 | ); | |
| 361 | assert.equal(summaryLine({ ...none, total: 3, people: 3 }), "People landed 3 changes this week; none were agents'."); | |
| 362 | assert.equal(summaryLine({ ...none, live: 1 }), "1 agent is at work. Nothing has landed this week yet."); | |
| 363 | assert.match(summaryLine(none), /Assign an issue/); | |
| Mission control shows where you are needed and what agents landed without you; git answers in about 200ms | 364 | }); |