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 front page is Home: where you're needed comes first, however long ago it started, with what's at stake and how long it has waited; then what happened since you were last here, in words, with the agent work that settled, what landed, what deployed, what was decided and what was spent, switchable to the last 24 hours or 7 days; then what's running now. Your last visit is kept with your account, so it's the same on every device; Today's old address leads here. The home guide says how. | 1 | import assert from "node:assert/strict"; |
| 2 | import { test } from "node:test"; | |
| 3 | ||
| 4 | import type { AgentSession, Deployment, InboxItem, InstallRequest, Memory } from "@g1t/contracts"; | |
| 5 | ||
| 6 | import { | |
| 7 | type AttentionRow, | |
| 8 | type CodeNeed, | |
| 9 | type CodePull, | |
| 10 | acceptance, | |
| 11 | agentRow, | |
| 12 | attention, | |
| 13 | chatRow, | |
| 14 | codeRow, | |
| 15 | dayIn, | |
| 16 | decisionsIn, | |
| 17 | deploysIn, | |
| 18 | duration, | |
| 19 | installRequestRow, | |
| 20 | landedIn, | |
| 21 | limitRow, | |
| 22 | notificationRow, | |
| 23 | parseWindow, | |
| 24 | pullOutcome, | |
| 25 | rankAttention, | |
| 26 | running, | |
| 27 | runningDeploys, | |
| 28 | runningSessions, | |
| 29 | sessionOutcome, | |
| 30 | sinceWords, | |
| 31 | spanFor, | |
| 32 | spanMonths, | |
| 33 | spanSentence, | |
| 34 | spanWork, | |
| 35 | spendIn, | |
| 36 | startHere, | |
| 37 | trendLabel, | |
| 38 | waitingSentence, | |
| 39 | } from "./home.ts"; | |
| 40 | ||
| 41 | const HOUR = 3_600_000; | |
| 42 | const DAY = 24 * HOUR; | |
| 43 | // Friday 2026-10-09, 18:00 UTC; 11:00 in Los Angeles. | |
| 44 | const NOW = Date.parse("2026-10-09T18:00:00Z"); | |
| 45 | const LA = "America/Los_Angeles"; | |
| 46 | const repo = { namespace: "acme", name: "web" }; | |
| 47 | const iso = (ms: number) => new Date(ms).toISOString(); | |
| 48 | ||
| 49 | function pull(over: Partial<CodePull> = {}): CodePull { | |
| 50 | return { | |
| 51 | repo, | |
| 52 | number: 1, | |
| 53 | title: "Fix the flaky test", | |
| 54 | status: "merged", | |
| 55 | mergedAt: iso(NOW - HOUR), | |
| 56 | createdAt: iso(NOW - 3 * HOUR), | |
| 57 | updatedAt: iso(NOW - HOUR), | |
| 58 | author: { username: "g1t", kind: "agent" }, | |
| 59 | ...over, | |
| 60 | }; | |
| 61 | } | |
| 62 | ||
| 63 | function session(over: Partial<AgentSession> = {}): AgentSession { | |
| 64 | return { | |
| 65 | id: "s1", | |
| 66 | workspace_id: "w", | |
| 67 | agent_id: "a", | |
| 68 | agent_handle: "margo", | |
| 69 | agent_name: "Margo", | |
| 70 | agent_avatar_seed: "margo", | |
| 71 | subagent: null, | |
| 72 | kind: "chat", | |
| 73 | parent_id: null, | |
| 74 | root_id: "s1", | |
| 75 | payer_agent_id: "a", | |
| 76 | title: "Draft the launch notes", | |
| 77 | goal: "", | |
| 78 | status: "done", | |
| 79 | status_note: null, | |
| 80 | summary: null, | |
| 81 | channel_id: "c", | |
| 82 | channel_kind: "channel", | |
| 83 | channel_name: "launch", | |
| 84 | card_message_id: null, | |
| 85 | asked_by: null, | |
| 86 | asked_by_username: "bo", | |
| 87 | routine_id: null, | |
| 88 | steps: 3, | |
| 89 | tool_calls: 2, | |
| 90 | input_tokens: 0, | |
| 91 | output_tokens: 0, | |
| 92 | charged_micros: 400_000, | |
| 93 | cap_micros: 2_000_000, | |
| 94 | model: null, | |
| 95 | outputs: [], | |
| 96 | created_at: iso(NOW - 2 * HOUR), | |
| 97 | updated_at: iso(NOW - HOUR), | |
| 98 | finished_at: iso(NOW - HOUR), | |
| 99 | visible: true, | |
| 100 | ...over, | |
| 101 | }; | |
| 102 | } | |
| 103 | const need = (over: Partial<CodeNeed>): CodeNeed => ({ | |
| 104 | key: "pull:p1", | |
| 105 | reason: "ready_to_merge", | |
| 106 | repo, | |
| 107 | ref: "#12", | |
| 108 | title: "Add retries", | |
| 109 | ask: "Waiting for you to merge it.", | |
| 110 | by: { name: "g1t", agent: true }, | |
| 111 | for: "bo", | |
| 112 | at: NOW - 2 * HOUR, | |
| 113 | to: "/acme/web/pull/12", | |
| 114 | facts: [{ label: "Files changed", value: "4", tone: null }], | |
| 115 | ...over, | |
| 116 | }); | |
| 117 | ||
| 118 | test("Code's needs become rows with what is at stake", () => { | |
| 119 | const ready = codeRow(need({})); | |
| 120 | assert.equal(ready.kind, "ready"); | |
| 121 | assert.equal(ready.stake?.text, "4 files to land"); | |
| 122 | assert.equal(ready.action.label, "Merge"); | |
| 123 | const deploy = codeRow(need({ key: "deploy:site", reason: "blocking", facts: [{ label: "Production", value: "Serving the build before", tone: "good" }] })); | |
| 124 | assert.equal(deploy.kind, "deploy"); | |
| 125 | assert.equal(deploy.stake?.text, "Production on an older build"); | |
| 126 | assert.equal(codeRow(need({ reason: "checks_failing" })).kind, "pull_blocked"); | |
| 127 | assert.equal(codeRow(need({ key: "run:r1", reason: "stalled", facts: [{ label: "Cost so far", value: "$1.20", tone: null }] })).stake?.text, "$1.20 so far"); | |
| 128 | }); | |
| 129 | ||
| 130 | const row = (over: Partial<AttentionRow>): AttentionRow => ({ | |
| 131 | key: "k", | |
| 132 | kind: "chat", | |
| 133 | title: "t", | |
| 134 | detail: "d", | |
| 135 | stake: null, | |
| 136 | action: { label: "Open", to: "/x" }, | |
| 137 | owner: null, | |
| 138 | at: NOW, | |
| 139 | from: "chat", | |
| 140 | ...over, | |
| 141 | }); | |
| 142 | ||
| 143 | test("Start here: the most pressing tier first, then what has waited longest", () => { | |
| 144 | const rows = [ | |
| 145 | row({ key: "chat:old", kind: "chat", at: NOW - 5 * DAY, action: { label: "Reply", to: "/acme/-/chat/general" } }), | |
| 146 | row({ key: "ready:new", kind: "ready", at: NOW - HOUR, action: { label: "Merge", to: "/acme/web/pull/2" } }), | |
| 147 | row({ key: "ready:old", kind: "ready", at: NOW - 3 * HOUR, action: { label: "Merge", to: "/acme/web/pull/1" } }), | |
| 148 | row({ key: "session:s", kind: "session_cap", at: NOW - 10 * 60_000, action: { label: "Approve more", to: "/acme/-/agents/margo/sessions/s" } }), | |
| 149 | ]; | |
| 150 | const ranked = rankAttention(rows); | |
| 151 | assert.deepEqual(ranked.map((r) => r.key), ["session:s", "ready:old", "ready:new", "chat:old"]); | |
| 152 | const start = startHere(ranked); | |
| 153 | assert.equal(start?.row.key, "session:s"); | |
| 154 | assert.match(start?.why ?? "", /spend cap/); | |
| 155 | // A failed production deploy beats everything. | |
| 156 | const withDeploy = rankAttention([...rows, row({ key: "deploy:site", kind: "deploy", at: NOW, action: { label: "See the build", to: "/acme/site/deployments/1" } })]); | |
| 157 | assert.equal(startHere(withDeploy)?.row.key, "deploy:site"); | |
| 158 | // Ties in tier say so. | |
| 159 | const two = startHere(rankAttention(rows.filter((r) => r.kind === "ready"))); | |
| 160 | assert.equal(two?.row.key, "ready:old"); | |
| 161 | assert.match(two?.why ?? "", /Of the 2 like it, it has waited longest/); | |
| 162 | assert.equal(startHere([]), null); | |
| 163 | }); | |
| 164 | ||
| 165 | test("the same order whatever order the rows came in", () => { | |
| 166 | const rows = [row({ key: "b", kind: "review", at: NOW }), row({ key: "a", kind: "review", at: NOW, action: { label: "Review", to: "/y" } })]; | |
| 167 | assert.deepEqual(rankAttention(rows).map((r) => r.key), ["a", "b"]); | |
| 168 | assert.deepEqual(rankAttention([...rows].reverse()).map((r) => r.key), ["a", "b"]); | |
| 169 | }); | |
| 170 | ||
| 171 | const item = (over: Partial<InboxItem>): InboxItem => ({ | |
| 172 | id: "n1", | |
| 173 | reason: "ci_activity", | |
| 174 | severity: "error", | |
| 175 | title: "Checks failed on #12", | |
| 176 | body: "Add retries", | |
| 177 | event: "check.failed", | |
| 178 | repo: "acme/web", | |
| 179 | workspace: "acme", | |
| 180 | subject: "pull", | |
| 181 | number: 12, | |
| 182 | url: "/acme/web/pull/12", | |
| 183 | actor: "g1t", | |
| 184 | count: 1, | |
| 185 | createdAt: iso(NOW - HOUR), | |
| 186 | updatedAt: iso(NOW - HOUR), | |
| 187 | readAt: null, | |
| 188 | doneAt: null, | |
| 189 | saved: false, | |
| 190 | snoozedUntil: null, | |
| 191 | ...over, | |
| 192 | }); | |
| 193 | ||
| 194 | test("notifications: warnings and failures in this workspace; none about repositories without Code", () => { | |
| 195 | assert.equal(notificationRow(item({}), "acme", true)?.kind, "notification"); | |
| 196 | assert.equal(notificationRow(item({}), "acme", false), null); | |
| 197 | assert.equal(notificationRow(item({ repo: null, url: "/acme/-/agents/margo" }), "acme", false)?.kind, "notification"); | |
| 198 | assert.equal(notificationRow(item({ severity: "info" }), "acme", true), null); | |
| 199 | assert.equal(notificationRow(item({ workspace: "other", repo: "other/web" }), "acme", true), null); | |
| 200 | assert.equal(notificationRow(item({ readAt: iso(NOW) }), "acme", true), null); | |
| 201 | }); | |
| 202 | ||
| 203 | test("a notification about something already listed is left out", () => { | |
| 204 | const out = attention({ | |
| 205 | slug: "acme", | |
| 206 | code: true, | |
| 207 | codeNeeds: [need({})], | |
| 208 | capped: [], | |
| 209 | agents: [], | |
| 210 | canManage: false, | |
| 211 | notifications: [item({})], | |
| 212 | chat: [], | |
| 213 | }); | |
| 214 | assert.deepEqual(out.rows.map((r) => r.key), ["pull:p1"]); | |
| 215 | assert.deepEqual(out.missing, []); | |
| 216 | }); | |
| 217 | ||
| 218 | test("without Code access no Code row is asked for or shown, and a silent source is named", () => { | |
| 219 | const out = attention({ | |
| 220 | slug: "acme", | |
| 221 | code: false, | |
| 222 | codeNeeds: [need({})], | |
| 223 | capped: null, | |
| 224 | agents: null, | |
| 225 | canManage: false, | |
| 226 | notifications: [item({}), item({ id: "n2", repo: null, reason: "agent", severity: "warning", url: "/acme/-/agents/margo" })], | |
| 227 | chat: null, | |
| 228 | }); | |
| 229 | assert.deepEqual(out.rows.map((r) => r.key), ["notification:n2"]); | |
| 230 | assert.equal(out.rows.some((r) => r.from === "code"), false); | |
| 231 | assert.deepEqual(out.missing, ["Agents", "Chat"]); | |
| 232 | }); | |
| 233 | ||
| 234 | test("an agent out of budget is listed only for those who may raise it", () => { | |
| 235 | const agent = { id: "a1", handle: "margo", display_name: "Margo", status: "out_of_budget", spent_month_micros: 52_000_000, budget: { monthly_micros: 50_000_000 }, updated_at: iso(NOW) }; | |
| 236 | assert.equal(agentRow(agent, "acme")?.stake?.text, "$0.00 left"); | |
| 237 | const base = { slug: "acme", code: true, codeNeeds: [], capped: [], notifications: [], chat: [] }; | |
| 238 | assert.equal(attention({ ...base, agents: [agent], canManage: false }).rows.length, 0); | |
| 239 | assert.equal(attention({ ...base, agents: [agent], canManage: true }).rows[0]?.kind, "agent_budget"); | |
| 240 | }); | |
| 241 | ||
| 242 | test("chat: mentions anywhere and unread direct messages, never muted", () => { | |
| 243 | const channel = { id: "c1", kind: "channel" as const, name: "general", last_message_at: iso(NOW) }; | |
| 244 | assert.equal(chatRow({ channel, title: "general", muted: false, unread: 4, mentions: 0 }, "acme"), null); | |
| 245 | assert.equal(chatRow({ channel, title: "general", muted: false, unread: 4, mentions: 2 }, "acme")?.stake?.text, "2 mentions"); | |
| 246 | assert.equal(chatRow({ channel, title: "general", muted: true, unread: 4, mentions: 2 }, "acme"), null); | |
| 247 | const dm = chatRow({ channel: { ...channel, kind: "dm", name: null }, title: "Bo Reed", muted: false, unread: 1, mentions: 0 }, "acme"); | |
| 248 | assert.equal(dm?.action.to, "/acme/-/chat/dm/c1"); | |
| 249 | }); | |
| 250 | ||
| 251 | // The span is NOW back to 3 days ago unless a test says otherwise. | |
| 252 | const SPAN = { from: NOW - 3 * DAY, now: NOW }; | |
| 253 | ||
| 254 | test("a day is the reader's day", () => { | |
| 255 | assert.equal(dayIn(NOW, LA), "2026-10-09"); | |
| 256 | assert.equal(dayIn(Date.parse("2026-10-10T02:00:00Z"), LA), "2026-10-09"); | |
| 257 | assert.equal(dayIn(Date.parse("2026-10-10T02:00:00Z"), null), "2026-10-10"); | |
| 258 | assert.equal(dayIn(NOW, "Not/AZone"), "2026-10-09"); | |
| 259 | }); | |
| 260 | ||
| 261 | test("since your last visit, in words, in the reader's time zone", () => { | |
| 262 | // NOW is Friday 11:00 in Los Angeles. | |
| 263 | assert.equal(sinceWords(NOW - 20_000, NOW, LA), "a moment ago"); | |
| 264 | assert.equal(sinceWords(NOW - 40 * 60_000, NOW, LA), "40 minutes ago"); | |
| 265 | assert.equal(sinceWords(NOW - HOUR - 60_000, NOW, LA), "an hour ago"); | |
| 266 | assert.equal(sinceWords(NOW - 2 * HOUR, NOW, LA), "2 hours ago"); | |
| 267 | // 07:00 Friday. | |
| 268 | assert.equal(sinceWords(NOW - 4 * HOUR, NOW, LA), "this morning"); | |
| 269 | // 02:00 Friday is Thursday night. | |
| 270 | assert.equal(sinceWords(NOW - 9 * HOUR, NOW, LA), "last night"); | |
| 271 | // 19:00 Thursday. | |
| 272 | assert.equal(sinceWords(NOW - 16 * HOUR, NOW, LA), "yesterday evening"); | |
| 273 | // 19:00 Tuesday. | |
| 274 | assert.equal(sinceWords(NOW - 2 * DAY - 16 * HOUR, NOW, LA), "Tuesday evening"); | |
| 275 | // 02:00 Wednesday is Tuesday night. | |
| 276 | assert.equal(sinceWords(NOW - 2 * DAY - 9 * HOUR, NOW, LA), "Tuesday night"); | |
| 277 | assert.equal(sinceWords(NOW - 9 * DAY, NOW, LA), "Sep 30"); | |
| 278 | }); | |
| 279 | ||
| 280 | test("how long a span is", () => { | |
| 281 | assert.equal(duration(30_000), "a moment"); | |
| 282 | assert.equal(duration(45 * 60_000), "45 minutes"); | |
| 283 | assert.equal(duration(HOUR), "1 hour"); | |
| 284 | assert.equal(duration(3 * DAY + HOUR), "3 days"); | |
| 285 | assert.equal(duration(15 * DAY), "2 weeks"); | |
| 286 | }); | |
| 287 | ||
| 288 | test("the span: since your last visit, capped, or a fixed window", () => { | |
| 289 | assert.equal(parseWindow("24h"), "24h"); | |
| 290 | assert.equal(parseWindow("7d"), "7d"); | |
| 291 | assert.equal(parseWindow("nonsense"), "last"); | |
| 292 | assert.equal(parseWindow(null), "last"); | |
| 293 | const away = spanFor("last", NOW, NOW - 2 * DAY - 16 * HOUR, LA); | |
| 294 | assert.equal(away.from, NOW - 2 * DAY - 16 * HOUR); | |
| 295 | assert.equal(away.words, "Since Tuesday evening"); | |
| 296 | assert.equal(away.length, "2 days"); | |
| 297 | assert.equal(away.note, null); | |
| 298 | // Never visited: the last 24 hours, and it says why. | |
| 299 | const first = spanFor("last", NOW, null, LA); | |
| 300 | assert.equal(first.from, NOW - DAY); | |
| 301 | assert.equal(first.note, "first"); | |
| 302 | assert.equal(spanFor("last", NOW, undefined, LA).note, "unknown"); | |
| 303 | // Away a month: 14 days at most. | |
| 304 | const capped = spanFor("last", NOW, NOW - 30 * DAY, LA); | |
| 305 | assert.equal(capped.from, NOW - 14 * DAY); | |
| 306 | assert.equal(capped.note, "capped"); | |
| 307 | assert.equal(capped.words, "In the last 14 days"); | |
| 308 | // The fixed windows ignore the last visit. | |
| 309 | assert.equal(spanFor("24h", NOW, NOW - 3 * DAY, LA).from, NOW - DAY); | |
| 310 | assert.deepEqual([spanFor("7d", NOW, NOW - HOUR, LA).from, spanFor("7d", NOW, NOW - HOUR, LA).words], [NOW - 7 * DAY, "In the last 7 days"]); | |
| 311 | }); | |
| 312 | ||
| 313 | test("an agent's pull request is accepted first time when it merged in the span with no revision", () => { | |
| 314 | assert.equal(pullOutcome(pull(), 0, SPAN), "accepted"); | |
| 315 | assert.equal(pullOutcome(pull(), 2, SPAN), "fixed"); | |
| 316 | assert.equal(pullOutcome(pull({ mergedAt: iso(NOW - 4 * DAY) }), 0, SPAN), null); | |
| 317 | assert.equal(pullOutcome(pull({ status: "closed", mergedAt: null }), 0, SPAN), "dropped"); | |
| 318 | // Still open is not settled: it is under Running now. | |
| 319 | assert.equal(pullOutcome(pull({ status: "open", mergedAt: null }), 1, SPAN), null); | |
| 320 | }); | |
| 321 | ||
| 322 | test("a session counts once, at the root of its tree, when it settled in the span", () => { | |
| 323 | assert.equal(sessionOutcome(session(), SPAN), "finished"); | |
| 324 | assert.equal(sessionOutcome(session({ parent_id: "s0" }), SPAN), null); | |
| 325 | assert.equal(sessionOutcome(session({ status: "failed" }), SPAN), "dropped"); | |
| 326 | assert.equal(sessionOutcome(session({ status: "stopped" }), SPAN), "dropped"); | |
| 327 | assert.equal(sessionOutcome(session({ status: "working", finished_at: null }), SPAN), null); | |
| 328 | assert.equal(sessionOutcome(session({ finished_at: iso(NOW - 4 * DAY) }), SPAN), null); | |
| 329 | }); | |
| 330 | ||
| 331 | test("accepted first time is accepted over every agent pull request that settled", () => { | |
| 332 | assert.deepEqual(acceptance(["accepted", "accepted", "fixed", "dropped", "finished"]), { value: 0.5, of: 4 }); | |
| 333 | assert.equal(acceptance(["finished"]), null); | |
| 334 | }); | |
| 335 | ||
| 336 | test("the span's work: pull requests and sessions, sources, and the 7 days before", () => { | |
| 337 | const work = spanWork({ | |
| 338 | span: SPAN, | |
| 339 | slug: "acme", | |
| 340 | code: { | |
| 341 | pulls: [ | |
| 342 | pull({ number: 1 }), | |
| 343 | pull({ number: 2 }), | |
| 344 | pull({ number: 3, status: "open", mergedAt: null }), | |
| 345 | // A person's pull request is not agent work. | |
| 346 | pull({ number: 4, author: { username: "bo", kind: "user" } }), | |
| 347 | // The 7 days before the span: one accepted, one fixed. | |
| 348 | pull({ number: 5, mergedAt: iso(NOW - 4 * DAY), updatedAt: iso(NOW - 4 * DAY) }), | |
| 349 | pull({ number: 6, mergedAt: iso(NOW - 5 * DAY), updatedAt: iso(NOW - 5 * DAY) }), | |
| 350 | // Older than that: not counted. | |
| 351 | pull({ number: 7, mergedAt: iso(NOW - 11 * DAY), updatedAt: iso(NOW - 11 * DAY) }), | |
| 352 | ], | |
| 353 | revisions: { "acme/web#2": 1, "acme/web#6": 2 }, | |
| 354 | complete: true, | |
| 355 | }, | |
| 356 | sessions: { sessions: [session(), session({ id: "s2", kind: "routine", status: "working", finished_at: null }), session({ id: "s3", parent_id: "s1" })], complete: true }, | |
| 357 | }); | |
| 358 | assert.equal(work.tasks.length, 3); | |
| 359 | assert.deepEqual(work.counts, { accepted: 1, fixed: 1, finished: 1, dropped: 0 }); | |
| 360 | assert.deepEqual(work.rate, { value: 0.5, of: 2 }); | |
| 361 | assert.deepEqual(work.before, { value: 0.5, of: 2 }); | |
| 362 | assert.deepEqual(work.sources, [ | |
| 363 | { source: "code", count: 2 }, | |
| 364 | { source: "chat", count: 1 }, | |
| 365 | ]); | |
| 366 | assert.equal(work.code, "read"); | |
| 367 | assert.equal(work.partial, false); | |
| 368 | assert.equal( | |
| 369 | spanSentence({ words: "Since Tuesday evening" }, work, 3), | |
| 370 | "Since Tuesday evening, agents finished 3 tasks and 3 changes landed. 1 needed a fix after review.", | |
| 371 | ); | |
| 372 | }); | |
| 373 | ||
| 374 | test("no trend when the 7 days before could not all be read, and none without Code", () => { | |
| 375 | const partial = spanWork({ span: SPAN, slug: "acme", code: { pulls: [pull()], revisions: {}, complete: false }, sessions: null }); | |
| 376 | assert.equal(partial.before, null); | |
| 377 | assert.equal(partial.partial, true); | |
| 378 | assert.equal(partial.sessions, "unavailable"); | |
| 379 | const member = spanWork({ span: SPAN, slug: "acme", code: "no_access", sessions: { sessions: [session()], complete: true } }); | |
| 380 | assert.equal(member.code, "no_access"); | |
| 381 | assert.equal(member.rate, null); | |
| 382 | assert.equal(member.tasks.every((t) => t.source !== "code"), true); | |
| 383 | }); | |
| 384 | ||
| 385 | test("the trend is in whole points", () => { | |
| 386 | assert.equal(trendLabel({ value: 0.89 }, { value: 0.83 }), "+6 pts vs the 7 days before"); | |
| 387 | assert.equal(trendLabel({ value: 0.5 }, { value: 0.75 }), "−25 pts vs the 7 days before"); | |
| 388 | assert.equal(trendLabel({ value: 0.5 }, { value: 0.5 }), "Same as the 7 days before"); | |
| 389 | assert.equal(trendLabel(null, { value: 0.5 }), null); | |
| 390 | }); | |
| 391 | ||
| 392 | test("the sentence for a quiet span, and for what needs you", () => { | |
| 393 | const quiet = spanWork({ span: SPAN, slug: "acme", code: { pulls: [], revisions: {}, complete: true }, sessions: { sessions: [], complete: true } }); | |
| 394 | assert.equal(spanSentence({ words: "Since an hour ago" }, quiet, 0), "Since an hour ago, nothing new has settled."); | |
| 395 | assert.equal(spanSentence({ words: "In the last 24 hours" }, quiet, 2), "In the last 24 hours, 2 changes landed."); | |
| 396 | assert.equal(spanSentence({ words: "Since this morning" }, null, null), "Since this morning, agent work couldn't be read."); | |
| 397 | assert.equal(waitingSentence(0), "Nothing needs you right now."); | |
| 398 | assert.equal(waitingSentence(1), "1 thing needs you."); | |
| 399 | assert.equal(waitingSentence(8), "8 things need you."); | |
| 400 | }); | |
| 401 | ||
| 402 | test("what landed: every merge in the span, a person's or an agent's, newest first", () => { | |
| 403 | const landed = landedIn( | |
| 404 | [ | |
| 405 | pull({ number: 1, mergedAt: iso(NOW - 2 * DAY), mergedBy: "bo" }), | |
| 406 | pull({ number: 2, author: { username: "bo", kind: "user" }, mergedAt: iso(NOW - HOUR) }), | |
| 407 | pull({ number: 3, mergedAt: iso(NOW - 5 * DAY) }), | |
| 408 | pull({ number: 4, status: "open", mergedAt: null }), | |
| 409 | ], | |
| 410 | SPAN, | |
| 411 | ); | |
| 412 | assert.deepEqual(landed.map((change) => [change.number, change.agent]), [ | |
| 413 | [2, false], | |
| 414 | [1, true], | |
| 415 | ]); | |
| 416 | assert.equal(landed[1]!.mergedBy, "bo"); | |
| 417 | }); | |
| 418 | ||
| 419 | const deploy = (over: Partial<Deployment>): Deployment => ({ | |
| 420 | id: "d1", | |
| 421 | kind: "production", | |
| 422 | branch: null, | |
| 423 | number: null, | |
| 424 | commit: "abcdef1234", | |
| 425 | status: "ready", | |
| 426 | url: "https://site.g1t.page", | |
| 427 | error: null, | |
| 428 | warnings: [], | |
| 429 | buildSeconds: 40, | |
| 430 | createdBy: "bo", | |
| 431 | createdAt: iso(NOW - 2 * HOUR), | |
| 432 | finishedAt: iso(NOW - HOUR), | |
| 433 | ...over, | |
| 434 | }); | |
| 435 | ||
| 436 | test("builds that finished in the span: live or failed, never skipped", () => { | |
| 437 | const rows = deploysIn( | |
| 438 | [ | |
| 439 | { | |
| 440 | slug: "site", | |
| 441 | deployments: [ | |
| 442 | deploy({}), | |
| 443 | deploy({ id: "d2", status: "replaced", finishedAt: iso(NOW - DAY) }), | |
| 444 | deploy({ id: "d3", status: "failed", finishedAt: iso(NOW - 2 * DAY) }), | |
| 445 | deploy({ id: "d4", status: "skipped", finishedAt: iso(NOW - HOUR) }), | |
| 446 | deploy({ id: "d5", status: "building", finishedAt: null }), | |
| 447 | deploy({ id: "d6", finishedAt: iso(NOW - 5 * DAY) }), | |
| 448 | ], | |
| 449 | }, | |
| 450 | ], | |
| 451 | "acme", | |
| 452 | SPAN, | |
| 453 | ); | |
| 454 | assert.deepEqual(rows.map((row) => [row.key, row.outcome]), [ | |
| 455 | ["deploy:site:d1", "live"], | |
| 456 | ["deploy:site:d2", "live"], | |
| 457 | ["deploy:site:d3", "failed"], | |
| 458 | ]); | |
| 459 | assert.equal(rows[0]!.to, "/acme/site/deployments/d1"); | |
| 460 | assert.equal(rows[0]!.commit, "abcdef1"); | |
| 461 | }); | |
| 462 | ||
| 463 | const request = (over: Partial<InstallRequest>): InstallRequest => ({ | |
| 464 | id: "r1", | |
| 465 | listing: "integration:sentry", | |
| 466 | kind: "integration", | |
| 467 | name: "Sentry", | |
| 468 | note: "For the error alerts", | |
| 469 | requested_by: "bo", | |
| 470 | requested_at: iso(NOW - 2 * DAY), | |
| 471 | status: "open", | |
| 472 | resolved_by: null, | |
| 473 | resolved_at: null, | |
| 474 | ...over, | |
| 475 | }); | |
| 476 | ||
| 477 | const memory = (over: Partial<Memory>): Memory => ({ | |
| 478 | id: "m1", | |
| 479 | scope: "workspace", | |
| 480 | workspace: "acme", | |
| 481 | repo: null, | |
| 482 | text: "Use Postgres for the ledger", | |
| 483 | kind: "decision", | |
| 484 | source: { kind: "person" } as Memory["source"], | |
| 485 | createdBy: "ada", | |
| 486 | pinned: false, | |
| 487 | createdAt: iso(NOW - DAY), | |
| 488 | updatedAt: iso(NOW - DAY), | |
| 489 | lastUsedAt: null, | |
| 490 | ...over, | |
| 491 | }); | |
| 492 | ||
| 493 | test("decisions: kept decisions in memory and requests answered, in the span", () => { | |
| 494 | const rows = decisionsIn( | |
| 495 | { | |
| 496 | memories: [memory({}), memory({ id: "m2", kind: "fact" }), memory({ id: "m3", status: "candidate" }), memory({ id: "m4", createdAt: iso(NOW - 6 * DAY) })], | |
| 497 | requests: [request({}), request({ id: "r2", status: "declined", resolved_by: "ada", resolved_at: iso(NOW - HOUR) })], | |
| 498 | }, | |
| 499 | "acme", | |
| 500 | SPAN, | |
| 501 | ); | |
| 502 | assert.deepEqual(rows.map((row) => row.key), ["request:r2", "memory:m1"]); | |
| 503 | assert.equal(rows[0]!.text, "Turned down Sentry, which bo asked for"); | |
| 504 | }); | |
| 505 | ||
| 506 | test("an open install request needs an owner; answered ones don't", () => { | |
| 507 | assert.equal(installRequestRow(request({}), "acme")?.action.to, "/acme/-/marketplace/requests"); | |
| 508 | assert.equal(installRequestRow(request({ status: "done" }), "acme"), null); | |
| 509 | const base = { slug: "acme", code: true, codeNeeds: [], capped: [], agents: [], canManage: true, notifications: [], chat: [] }; | |
| 510 | assert.equal(attention({ ...base, requests: [request({})] }).rows[0]?.kind, "install_request"); | |
| 511 | assert.equal(attention({ ...base, requests: null }).rows.length, 0); | |
| 512 | }); | |
| 513 | ||
| 514 | test("the workspace's agent budget used up comes first, for those who may raise it", () => { | |
| 515 | assert.equal(limitRow({ alert: 90, spentMicros: 1, since: NOW }, "acme"), null); | |
| 516 | const limit = { alert: 100, spentMicros: 500_000_000, since: NOW - DAY }; | |
| 517 | assert.equal(limitRow(limit, "acme")?.action.to, "/acme/-/spend"); | |
| 518 | const base = { slug: "acme", code: true, codeNeeds: [need({})], capped: [], agents: [], notifications: [], chat: [], limit }; | |
| 519 | assert.equal(attention({ ...base, canManage: true }).start?.row.kind, "limit"); | |
| 520 | assert.equal(attention({ ...base, canManage: false }).rows.some((r) => r.kind === "limit"), false); | |
| 521 | }); | |
| 522 | ||
| 523 | test("running now: live root sessions, agents' changes and builds going; a capped session is under Needs you", () => { | |
| 524 | const sessions = runningSessions( | |
| 525 | [ | |
| 526 | session({ id: "a", status: "working", finished_at: null, created_at: iso(NOW - HOUR) }), | |
| 527 | session({ id: "b", status: "needs_approval", finished_at: null }), | |
| 528 | session({ id: "c", status: "queued", finished_at: null, parent_id: "a" }), | |
| 529 | session({ id: "d", status: "done" }), | |
| 530 | session({ id: "e", status: "queued", finished_at: null, visible: false, created_at: iso(NOW - 2 * HOUR) }), | |
| 531 | ], | |
| 532 | "acme", | |
| 533 | ); | |
| 534 | assert.deepEqual(sessions.map((row) => [row.key, row.status]), [ | |
| 535 | ["session:a", "Working"], | |
| 536 | ["session:e", "Queued"], | |
| 537 | ]); | |
| 538 | assert.equal(sessions[1]!.title, "A session in a conversation you're not in"); | |
| 539 | const builds = runningDeploys( | |
| 540 | [ | |
| 541 | { slug: "site", enabled: true, production: null, previews: 0, latest: deploy({ status: "building", finishedAt: null }) }, | |
| 542 | { slug: "docs", enabled: true, production: null, previews: 0, latest: deploy({}) }, | |
| 543 | ], | |
| 544 | "acme", | |
| 545 | ); | |
| 546 | assert.deepEqual(builds.map((row) => row.status), ["Building"]); | |
| 547 | // Longest-going first within a kind. | |
| 548 | assert.deepEqual(running([builds, sessions]).map((row) => row.key), ["session:e", "session:a", "deploy:site:d1"]); | |
| 549 | }); | |
| 550 | ||
| 551 | ||
| 552 | test("the span's months: one, or the ones it crosses", () => { | |
| 553 | assert.deepEqual(spanMonths(SPAN), ["2026-10"]); | |
| 554 | assert.deepEqual(spanMonths({ from: Date.parse("2026-09-28T00:00:00Z"), now: NOW }), ["2026-09", "2026-10"]); | |
| 555 | assert.deepEqual(spanMonths({ from: Date.parse("2025-12-30T00:00:00Z"), now: Date.parse("2026-01-02T00:00:00Z") }), ["2025-12", "2026-01"]); | |
| 556 | }); | |
| 557 | ||
| 558 | test("spend in the span: the UTC days it covers across months, at price, without money in", () => { | |
| 559 | const september = { | |
| 560 | groups: [ | |
| 561 | { key: "2026-09-29", label: "", chargedMicros: 0, lines: [{ kind: "Agent runs", count: 2, chargedMicros: 1_000_000, costMicros: 0 }] }, | |
| 562 | { key: "2026-09-20", label: "", chargedMicros: 0, lines: [{ kind: "Agent runs", count: 9, chargedMicros: 9_000_000, costMicros: 0 }] }, | |
| 563 | ], | |
| 564 | totals: { chargedMicros: 10_000_000, paidMicros: 0, costMicros: 0, entries: 2 }, | |
| 565 | }; | |
| 566 | const october = { | |
| 567 | groups: [ | |
| 568 | { | |
| 569 | key: "2026-10-09", | |
| 570 | label: "2026-10-09", | |
| 571 | chargedMicros: 0, | |
| 572 | lines: [ | |
| 573 | { kind: "Agent runs", count: 12, chargedMicros: 3_000_000, costMicros: 2_500_000, priceMicros: 4_000_000 }, | |
| 574 | { kind: "Sandbox time", count: 12, chargedMicros: 600_000, costMicros: 500_000 }, | |
| 575 | { kind: "Payments", count: 1, chargedMicros: -50_000_000, costMicros: 0, priceMicros: 0 }, | |
| 576 | ], | |
| 577 | }, | |
| 578 | ], | |
| 579 | totals: { chargedMicros: 12_600_000, paidMicros: 50_000_000, costMicros: 0, entries: 4, priceMicros: 13_600_000 }, | |
| 580 | }; | |
| 581 | const spend = spendIn([september, october], { from: Date.parse("2026-09-29T15:00:00Z"), now: NOW }); | |
| 582 | assert.deepEqual([spend.from, spend.to], ["2026-09-29", "2026-10-09"]); | |
| 583 | assert.equal(spend.totalMicros, 5_600_000); | |
| 584 | assert.deepEqual(spend.lines.map((l) => [l.kind, l.micros, l.count]), [ | |
| 585 | ["Agent runs", 5_000_000, 14], | |
| 586 | ["Sandbox time", 600_000, 12], | |
| 587 | ]); | |
| 588 | // The month so far is the current month's statement. | |
| 589 | assert.equal(spend.monthMicros, 13_600_000); | |
| 590 | }); |