| 1 | import assert from "node:assert/strict"; |
| 2 | import { test } from "node:test"; |
| 3 | |
| 4 | import type { Claim, Decision, G1tEvent, WorkSession } from "@g1t/contracts"; |
| 5 | |
| 6 | import { |
| 7 | ENDED_BY_HAND, |
| 8 | askedText, |
| 9 | isAcknowledgement, |
| 10 | checkHref, |
| 11 | endReasonText, |
| 12 | evidenceOf, |
| 13 | expiresIn, |
| 14 | feedLine, |
| 15 | harnessName, |
| 16 | intentTitle, |
| 17 | awakeAndIdle, |
| 18 | liveByItem, |
| 19 | sessionTitle, |
| 20 | duration, |
| 21 | mayEndSession, |
| 22 | mergeSessions, |
| 23 | numberedParts, |
| 24 | parseBlockedBy, |
| 25 | pullHrefs, |
| 26 | pendingLines, |
| 27 | resolutionOf, |
| 28 | sessionTree, |
| 29 | span, |
| 30 | titleHint, |
| 31 | titleIsProvisional, |
| 32 | standingFailure, |
| 33 | touchedByKind, |
| 34 | treeSize, |
| 35 | viaLabel, |
| 36 | } from "./coordination.ts"; |
| 37 | |
| 38 | const ana = { id: "usr_ana", username: "ana" }; |
| 39 | const bo = { id: "usr_bo", username: "bo" }; |
| 40 | |
| 41 | function claim(item: number, over: Partial<Claim> = {}): Claim { |
| 42 | return { |
| 43 | id: `clm_${item}_${over.actor?.username ?? "ana"}`, |
| 44 | repoId: "rep_1", |
| 45 | workspace: "acme", |
| 46 | item, |
| 47 | sessionId: null, |
| 48 | actor: ana, |
| 49 | harness: "claude_code", |
| 50 | note: null, |
| 51 | state: "active", |
| 52 | createdAt: "2026-10-12T10:00:00Z", |
| 53 | expiresAt: "2026-10-12T10:30:00Z", |
| 54 | endedAt: null, |
| 55 | endReason: null, |
| 56 | handoffTo: null, |
| 57 | ...over, |
| 58 | }; |
| 59 | } |
| 60 | |
| 61 | function session(id: string, over: Partial<WorkSession> = {}): WorkSession { |
| 62 | return { |
| 63 | id, |
| 64 | repoId: "rep_1", |
| 65 | item: null, |
| 66 | pull: null, |
| 67 | branch: null, |
| 68 | actor: bo, |
| 69 | harness: "codex", |
| 70 | harnessSessionId: null, |
| 71 | title: null, |
| 72 | state: "active", |
| 73 | headCommit: null, |
| 74 | startedAt: "2026-10-12T10:00:00Z", |
| 75 | lastSeenAt: "2026-10-12T10:05:00Z", |
| 76 | endedAt: null, |
| 77 | endReason: null, |
| 78 | ...over, |
| 79 | }; |
| 80 | } |
| 81 | |
| 82 | function decision(kind: Decision["kind"], at: string, over: Partial<Decision> = {}): Decision { |
| 83 | return { |
| 84 | id: `dec_${kind}_${at}`, |
| 85 | item: 2, |
| 86 | pull: 5, |
| 87 | sessionId: null, |
| 88 | actor: null, |
| 89 | kind, |
| 90 | confidence: "tested", |
| 91 | summary: kind, |
| 92 | evidence: {}, |
| 93 | createdAt: at, |
| 94 | ...over, |
| 95 | }; |
| 96 | } |
| 97 | |
| 98 | const event = (type: string, data: unknown): G1tEvent => |
| 99 | ({ id: `evt_${type}`, type, source: "work", time: "2026-10-12T10:00:00Z", repoId: "rep_1", actor: "usr_ana", data }) as G1tEvent; |
| 100 | |
| 101 | test("harnesses are named as people know them", () => { |
| 102 | assert.equal(harnessName("claude_code"), "Claude Code"); |
| 103 | assert.equal(harnessName("codex"), "Codex"); |
| 104 | assert.equal(harnessName("claude_managed_agents"), "Claude Managed Agents"); |
| 105 | assert.equal(harnessName(null), null); |
| 106 | }); |
| 107 | |
| 108 | test("a claim says when it lapses; one that ended says nothing", () => { |
| 109 | const now = Date.parse("2026-10-12T10:07:00Z"); |
| 110 | assert.equal(expiresIn(claim(1), now), "expires in 23m"); |
| 111 | assert.equal(expiresIn(claim(1, { expiresAt: "2026-10-12T12:00:00Z" }), now), "expires in 1h 53m"); |
| 112 | assert.equal(expiresIn(claim(1, { expiresAt: "2026-10-12T10:00:00Z" }), now), "expiring now"); |
| 113 | assert.equal(expiresIn(claim(1, { state: "released" }), now), null); |
| 114 | assert.equal(span(30_000), "less than a minute"); |
| 115 | assert.equal(span(2 * 3_600_000), "2h"); |
| 116 | }); |
| 117 | |
| 118 | test("Live now lists sessions heard from lately and folds away the idle, the latest first", () => { |
| 119 | const { awake, idle } = awakeAndIdle([ |
| 120 | session("ses_a", { state: "active" }), |
| 121 | session("ses_b", { state: "idle", title: "Tidy the README", lastSeenAt: "2026-10-12T10:00:00Z" }), |
| 122 | session("ses_c", { state: "idle", lastSeenAt: "2026-10-12T10:10:00Z" }), |
| 123 | ]); |
| 124 | assert.deepEqual( |
| 125 | awake.map((one) => one.id), |
| 126 | ["ses_a"], |
| 127 | ); |
| 128 | assert.deepEqual( |
| 129 | idle.map((one) => one.id), |
| 130 | ["ses_c", "ses_b"], |
| 131 | ); |
| 132 | // A tab opened and never typed in is in neither; once it says what it is for, it shows. |
| 133 | const opened = session("ses_d", { state: "active", title: null, startedAt: "2026-10-12T10:00:00Z", lastSeenAt: "2026-10-12T10:00:00Z" }); |
| 134 | assert.deepEqual(awakeAndIdle([opened]), { awake: [], idle: [] }); |
| 135 | assert.equal(awakeAndIdle([{ ...opened, lastSeenAt: "2026-10-12T10:00:05Z" }]).awake.length, 1); |
| 136 | assert.equal(awakeAndIdle([{ ...opened, title: "Fix the build" }]).awake.length, 1); |
| 137 | }); |
| 138 | |
| 139 | test("live work is grouped by work item, most recent first, with sessions on no work item apart", () => { |
| 140 | const { items, loose } = liveByItem( |
| 141 | [claim(1), claim(2, { createdAt: "2026-10-12T10:20:00Z" }), claim(2, { actor: bo })], |
| 142 | [session("ses_a", { item: 1, lastSeenAt: "2026-10-12T10:30:00Z" }), session("ses_b")], |
| 143 | ); |
| 144 | assert.deepEqual( |
| 145 | items.map((group) => [group.item, group.claims.length, group.sessions.length]), |
| 146 | [ |
| 147 | [1, 1, 1], |
| 148 | [2, 2, 0], |
| 149 | ], |
| 150 | ); |
| 151 | assert.deepEqual( |
| 152 | loose.map((one) => one.id), |
| 153 | ["ses_b"], |
| 154 | ); |
| 155 | }); |
| 156 | |
| 157 | test("sessions on no work item are grouped by pull request, then by a branch of their own, then the rest together", () => { |
| 158 | const seen = (minute: number) => `2026-10-12T10:${String(minute).padStart(2, "0")}:00Z`; |
| 159 | const { items, groups, loose } = liveByItem( |
| 160 | [], |
| 161 | [ |
| 162 | session("ses_main", { branch: "main", lastSeenAt: seen(50) }), |
| 163 | session("ses_fix1", { branch: "fix-previews", lastSeenAt: seen(10) }), |
| 164 | session("ses_pr", { pull: 12, branch: "tidy-readme", lastSeenAt: seen(20) }), |
| 165 | session("ses_fix2", { branch: "fix-previews", lastSeenAt: seen(40) }), |
| 166 | session("ses_none", { lastSeenAt: seen(30) }), |
| 167 | session("ses_issue", { item: 3, branch: "3-x", lastSeenAt: seen(5) }), |
| 168 | ], |
| 169 | "main", |
| 170 | ); |
| 171 | assert.deepEqual( |
| 172 | items.map((group) => group.item), |
| 173 | [3], |
| 174 | ); |
| 175 | assert.deepEqual( |
| 176 | groups.map((group) => [group.kind === "pull" ? `#${group.pull}` : group.kind === "branch" ? group.branch : "none", group.sessions.map((one) => one.id)]), |
| 177 | [ |
| 178 | // The most recently heard from first; the rest, on no branch of their own (the default branch is none), last. |
| 179 | ["fix-previews", ["ses_fix2", "ses_fix1"]], |
| 180 | ["#12", ["ses_pr"]], |
| 181 | ["none", ["ses_main", "ses_none"]], |
| 182 | ], |
| 183 | ); |
| 184 | assert.deepEqual( |
| 185 | loose.map((one) => one.id), |
| 186 | ["ses_fix2", "ses_fix1", "ses_pr", "ses_main", "ses_none"], |
| 187 | ); |
| 188 | // Without the default branch known, its sessions are a branch's. |
| 189 | assert.equal(liveByItem([], [session("ses_main", { branch: "main" })]).groups[0].kind, "branch"); |
| 190 | }); |
| 191 | |
| 192 | test("a session is never title-less: its title, its first prompt's intent, its branch, or untitled", () => { |
| 193 | assert.equal(sessionTitle({ title: "Rewrite the auth flow", branch: "auth", firstPrompt: "fix it" }), "Rewrite the auth flow"); |
| 194 | // A title that is only a pasted block's tag reads as none: the first prompt's words title it. |
| 195 | assert.equal( |
| 196 | sessionTitle({ |
| 197 | title: '<pasted_content id="5c50">', |
| 198 | branch: "main", |
| 199 | firstPrompt: '<pasted_content id="5c50">\nlog\n</pasted_content>\nship the runner setup', |
| 200 | }), |
| 201 | "Ship the runner setup", |
| 202 | ); |
| 203 | assert.equal(sessionTitle({ title: '<pasted_content id="5c50">', branch: "main", firstPrompt: null }), "Session on main"); |
| 204 | assert.equal(sessionTitle({ title: null, branch: "auth", firstPrompt: "fix the login redirect. It loops." }), "Fix the login redirect"); |
| 205 | assert.equal(sessionTitle({ title: " ", branch: "auth", firstPrompt: "/clear" }), "Session on auth"); |
| 206 | assert.equal(sessionTitle({ title: null, branch: null }), "Untitled session"); |
| 207 | assert.equal(sessionTitle({ title: null, branch: null }, "look at the CI"), "Look at the CI", "a first prompt given stands in"); |
| 208 | }); |
| 209 | |
| 210 | test("a title from the first prompt is provisional until g1t, the agent or a person names the session", () => { |
| 211 | assert.equal(titleIsProvisional({ title: "This project used to be in syntaqx/g1t", titleSource: "prompt", parentId: null }), true); |
| 212 | assert.equal(titleIsProvisional({ title: null, titleSource: null, parentId: null }), true); |
| 213 | for (const titleSource of ["agent", "person", "item", "pull", "summary"] as const) { |
| 214 | assert.equal(titleIsProvisional({ title: "Move issues to Work items", titleSource, parentId: null }), false, titleSource); |
| 215 | } |
| 216 | // From before titles said where they came from: as it is. |
| 217 | assert.equal(titleIsProvisional({ title: "Rewrite the auth flow", parentId: null }), false); |
| 218 | // A subagent goes by its task. |
| 219 | assert.equal(titleIsProvisional({ title: null, titleSource: null, parentId: "ses_1" }), false); |
| 220 | assert.match(titleHint({ title: "Mine", titleSource: "person", parentId: null }) ?? "", /keeps this title/); |
| 221 | assert.match(titleHint({ title: "x", titleSource: "prompt", parentId: null }) ?? "", /first prompt/); |
| 222 | assert.equal(titleHint({ title: "x", titleSource: "agent", parentId: "ses_1" }), undefined); |
| 223 | }); |
| 224 | |
| 225 | test("a prompt's intent is its first line or sentence, plain, clipped on a word", () => { |
| 226 | assert.equal(intentTitle("Make preview builds faster. They time out on big repositories."), "Make preview builds faster"); |
| 227 | // What an editor puts ahead of the prompt is not the prompt. |
| 228 | assert.equal(intentTitle("<ide_opened_file>The user opened a.ts in the IDE.</ide_opened_file>\nWhy is the page crashing?"), "Why is the page crashing?"); |
| 229 | assert.equal(intentTitle("Why does `src/app.ts` fail? It worked yesterday."), "Why does src/app.ts fail?"); |
| 230 | assert.equal(intentTitle("```js\nconst a = 1;\n```\n## Clean **this** up\nmore"), "Clean this up"); |
| 231 | assert.equal(intentTitle("\n\n- look at [the docs](https://docs.g1t.sh) for g1t.mjs"), "Look at the docs for g1t.mjs"); |
| 232 | const long = intentTitle("Tidy up the README and then look at the CI pipeline, which keeps failing on the Windows runners every night")!; |
| 233 | assert.ok(long.length <= 80 && long.endsWith("…") && !long.endsWith(" …"), long); |
| 234 | for (const prompt of ["/clear", "/g1t:status 4", "<command-name>/clear</command-name>", "```\ncode only\n```", "?", "", null]) { |
| 235 | assert.equal(intentTitle(prompt), null, String(prompt)); |
| 236 | } |
| 237 | }); |
| 238 | |
| 239 | test("evidence is read as far as it goes", () => { |
| 240 | const failed = decision("integration_failure", "2026-10-12T10:00:00Z", { |
| 241 | evidence: { |
| 242 | commit: "abc1234", |
| 243 | check: { name: "CI / merge_group", conclusion: "failure", url: "https://g1t.sh/acme/web/actions/runs/run_1" }, |
| 244 | combination: [4, 5], |
| 245 | suspects: [4], |
| 246 | bisected: false, |
| 247 | }, |
| 248 | }); |
| 249 | assert.deepEqual(evidenceOf(failed), { |
| 250 | commit: "abc1234", |
| 251 | check: { name: "CI / merge_group", conclusion: "failure", url: "https://g1t.sh/acme/web/actions/runs/run_1" }, |
| 252 | combination: [4, 5], |
| 253 | suspects: [4], |
| 254 | bisected: false, |
| 255 | }); |
| 256 | assert.deepEqual(evidenceOf(decision("note", "2026-10-12T10:00:00Z", { evidence: { combination: "nope" } })).combination, []); |
| 257 | assert.equal(checkHref("https://g1t.sh/acme/web/actions/runs/run_1"), "/acme/web/actions/runs/run_1"); |
| 258 | assert.equal(checkHref("https://ci.example.com/build/9"), "https://ci.example.com/build/9"); |
| 259 | assert.equal(checkHref(null), null); |
| 260 | }); |
| 261 | |
| 262 | test("a failure stands until the pull request passes in the queue or merges, and a fix after it answers it", () => { |
| 263 | const failed = decision("integration_failure", "2026-10-12T10:00:00Z"); |
| 264 | const fix = decision("fix", "2026-10-12T10:10:00Z", { confidence: "claimed" }); |
| 265 | const passed = decision("integration_passed", "2026-10-12T10:20:00Z"); |
| 266 | assert.equal(standingFailure([failed], 5), failed); |
| 267 | assert.equal(standingFailure([failed, fix], 5), failed); |
| 268 | assert.equal(standingFailure([passed, failed], 5), null); |
| 269 | assert.equal(standingFailure([failed], 6), null); |
| 270 | assert.deepEqual(resolutionOf([failed, fix, passed], failed), { fix, passed }); |
| 271 | assert.deepEqual(resolutionOf([fix, failed], { ...failed, createdAt: "2026-10-12T10:15:00Z" }), { fix: null, passed: null }); |
| 272 | }); |
| 273 | |
| 274 | test("coordination events read as lines of the feed", () => { |
| 275 | const claimed = feedLine( |
| 276 | event("work.claimed", { repoId: "rep_1", number: 2, item: 2, claim: claim(2, { actor: bo, harness: "codex" }), others: [claim(2)] }), |
| 277 | )!; |
| 278 | assert.equal(claimed.who, "bo"); |
| 279 | assert.equal(claimed.harness, "codex"); |
| 280 | assert.equal(claimed.text, "claimed #2 (ana is on it too)"); |
| 281 | assert.equal(claimed.tone, "warn"); |
| 282 | |
| 283 | const failed = feedLine( |
| 284 | event("integration.failed", { |
| 285 | repoId: "rep_1", |
| 286 | pullId: "pul_5", |
| 287 | number: 5, |
| 288 | pull: 5, |
| 289 | item: 2, |
| 290 | combination: [4, 5], |
| 291 | check: { name: "CI / merge_group", conclusion: "failure", url: null }, |
| 292 | bisected: false, |
| 293 | suspects: [4], |
| 294 | otherPulls: [4], |
| 295 | commit: null, |
| 296 | }), |
| 297 | )!; |
| 298 | assert.equal(failed.who, null); |
| 299 | assert.equal(failed.text, "#5 failed in the merge queue combined as #4, #5: CI / merge_group; fails with #4"); |
| 300 | assert.equal(failed.tone, "danger"); |
| 301 | |
| 302 | const unblocked = feedLine( |
| 303 | event("dependency.changed", { repoId: "rep_1", number: 2, item: 2, blockedBy: [1], added: [], removed: [], cleared: [1], remaining: [], mergedPull: 4 }), |
| 304 | )!; |
| 305 | assert.equal(unblocked.text, "#2 #1 landed (#4 merged); unblocked"); |
| 306 | |
| 307 | const overlap = feedLine(event("overlap.detected", { repoId: "rep_1", number: 5, pull: 5, otherPull: 4, files: ["src/auth.rs"] }))!; |
| 308 | assert.equal(overlap.text, "#5 and #4 change the same file: src/auth.rs"); |
| 309 | assert.equal(overlap.session, undefined); |
| 310 | |
| 311 | const sessions = feedLine( |
| 312 | event("overlap.detected", { |
| 313 | repoId: "rep_1", |
| 314 | sessionId: "ses_a", |
| 315 | otherSessionId: "ses_b", |
| 316 | otherActor: "bo", |
| 317 | files: ["a.rs", "b.rs", "c.rs", "d.rs"], |
| 318 | item: 2, |
| 319 | }), |
| 320 | )!; |
| 321 | assert.equal(sessions.who, "bo"); |
| 322 | assert.equal(sessions.text, "touched the same files in a session as another session: a.rs, b.rs, c.rs and 1 more"); |
| 323 | assert.equal(sessions.session, "ses_a"); |
| 324 | assert.equal(sessions.number, 2); |
| 325 | assert.equal(feedLine(event("session.progressed", { repoId: "rep_1", number: 2, sessionId: "ses_a" })), null); |
| 326 | |
| 327 | const started = feedLine(event("session.started", { repoId: "rep_1", session: session("ses_a", { item: 2 }) }))!; |
| 328 | assert.equal(started.text, "started a session on #2"); |
| 329 | assert.equal(feedLine(event("session.appended", { repoId: "rep_1", number: 5, pullId: "p", count: 3 })), null); |
| 330 | }); |
| 331 | |
| 332 | test("a session is ended by hand by whoever runs it, or by anyone who can write", () => { |
| 333 | const live = session("ses_a"); |
| 334 | assert.equal(mayEndSession(live, bo.id, false), true); |
| 335 | assert.equal(mayEndSession(live, ana.id, false), false); |
| 336 | assert.equal(mayEndSession(live, ana.id, true), true); |
| 337 | assert.equal(mayEndSession(session("ses_b", { state: "idle" }), ana.id, true), true); |
| 338 | // Ended already, or nobody signed in: nothing to offer. |
| 339 | assert.equal(mayEndSession(session("ses_c", { state: "ended" }), bo.id, true), false); |
| 340 | assert.equal(mayEndSession(live, null, true), false); |
| 341 | // One g1t runs is stopped from its page, which stops the run too. |
| 342 | assert.equal(mayEndSession(session("ses_d", { harness: "claude_managed_agents" }), bo.id, true), false); |
| 343 | assert.equal(endReasonText("timed_out"), "timed out"); |
| 344 | assert.equal(endReasonText(`${ENDED_BY_HAND}ana`), "ended by hand by @ana"); |
| 345 | assert.equal(endReasonText(null), null); |
| 346 | }); |
| 347 | |
| 348 | test("a session ended by hand says who ended it in the feed", () => { |
| 349 | const own = feedLine(event("session.ended", { repoId: "rep_1", session: session("ses_a", { item: 2, state: "ended", endReason: `${ENDED_BY_HAND}bo` }) }))!; |
| 350 | assert.equal(own.who, "bo"); |
| 351 | assert.equal(own.text, "ended a session on #2 by hand"); |
| 352 | const theirs = feedLine(event("session.ended", { repoId: "rep_1", session: session("ses_a", { state: "ended", endReason: `${ENDED_BY_HAND}ana` }) }))!; |
| 353 | assert.equal(theirs.who, "ana"); |
| 354 | assert.equal(theirs.harness, null); |
| 355 | assert.equal(theirs.text, "ended bo's session by hand"); |
| 356 | const timedOut = feedLine(event("session.ended", { repoId: "rep_1", session: session("ses_a", { state: "ended", endReason: "timed_out" }) }))!; |
| 357 | assert.equal(timedOut.text, "ended a session (timed out)"); |
| 358 | }); |
| 359 | |
| 360 | test("sessions become trees by their parents; one whose parent is missing is a root", () => { |
| 361 | const roots = sessionTree([ |
| 362 | session("ses_root"), |
| 363 | session("ses_a", { parentId: "ses_root" }), |
| 364 | session("ses_a1", { parentId: "ses_a" }), |
| 365 | session("ses_b", { parentId: "ses_root" }), |
| 366 | session("ses_orphan", { parentId: "ses_gone" }), |
| 367 | session("ses_other"), |
| 368 | ]); |
| 369 | const shape = (nodes: ReturnType<typeof sessionTree>): unknown[] => nodes.map((node) => [node.session.id, shape(node.children)]); |
| 370 | assert.deepEqual(shape(roots), [ |
| 371 | [ |
| 372 | "ses_root", |
| 373 | [ |
| 374 | ["ses_a", [["ses_a1", []]]], |
| 375 | ["ses_b", []], |
| 376 | ], |
| 377 | ], |
| 378 | ["ses_orphan", []], |
| 379 | ["ses_other", []], |
| 380 | ]); |
| 381 | assert.equal(treeSize(roots[0]!), 3); |
| 382 | // A child listed before its parent still lands under it. |
| 383 | assert.deepEqual(shape(sessionTree([session("ses_c", { parentId: "ses_p" }), session("ses_p")])), [["ses_p", [["ses_c", []]]]]); |
| 384 | // A cycle does not lose sessions. |
| 385 | const cycle = sessionTree([session("ses_x", { parentId: "ses_y" }), session("ses_y", { parentId: "ses_x" })]); |
| 386 | assert.equal(cycle.length + cycle.reduce((n, node) => n + treeSize(node), 0), 2); |
| 387 | assert.deepEqual(sessionTree([]), []); |
| 388 | assert.deepEqual( |
| 389 | mergeSessions([session("ses_a"), session("ses_b")], null, [session("ses_b", { title: "later" }), session("ses_c")]).map((one) => [one.id, one.title]), |
| 390 | [ |
| 391 | ["ses_a", null], |
| 392 | ["ses_b", null], |
| 393 | ["ses_c", null], |
| 394 | ], |
| 395 | ); |
| 396 | }); |
| 397 | |
| 398 | test("how a session was tied to its work, what it touched, and how long it ran", () => { |
| 399 | assert.equal(viaLabel("attached"), "attached"); |
| 400 | assert.equal(viaLabel("claim"), "claimed"); |
| 401 | assert.equal(viaLabel("branch"), "its branch"); |
| 402 | assert.equal(viaLabel("commit"), "its commit"); |
| 403 | assert.equal(viaLabel("trailer"), "commit trailer"); |
| 404 | assert.equal(viaLabel("opened"), "opened it"); |
| 405 | assert.equal(viaLabel("ref"), "named it"); |
| 406 | assert.deepEqual( |
| 407 | touchedByKind([ |
| 408 | { kind: "commit", value: "abc" }, |
| 409 | { kind: "file", value: "b.rs" }, |
| 410 | { kind: "file", value: "a.rs" }, |
| 411 | { kind: "file", value: "b.rs" }, |
| 412 | ]), |
| 413 | [ |
| 414 | { kind: "file", values: ["b.rs", "a.rs"] }, |
| 415 | { kind: "commit", values: ["abc"] }, |
| 416 | ], |
| 417 | ); |
| 418 | const now = Date.parse("2026-10-12T10:30:00Z"); |
| 419 | assert.equal(duration(session("ses_a"), now), "30m"); |
| 420 | assert.equal(duration(session("ses_a", { state: "ended", endedAt: "2026-10-12T10:00:42Z" }), now), "42s"); |
| 421 | assert.equal(duration(session("ses_a", { state: "ended", endedAt: null }), now), "5m"); |
| 422 | }); |
| 423 | |
| 424 | test("numbers in a sentence are found for linking", () => { |
| 425 | assert.deepEqual(numberedParts("#5 fails with #4."), [5, " fails with ", 4, "."]); |
| 426 | assert.deepEqual(numberedParts("no numbers"), ["no numbers"]); |
| 427 | }); |
| 428 | |
| 429 | test("blocked_by is typed as numbers", () => { |
| 430 | assert.deepEqual(parseBlockedBy("#12, 14 #12"), [12, 14]); |
| 431 | assert.deepEqual(parseBlockedBy(""), []); |
| 432 | assert.equal(parseBlockedBy("#12, soon"), null); |
| 433 | assert.equal(parseBlockedBy("0"), null); |
| 434 | }); |
| 435 | |
| 436 | test("a session's pending updates read as the feed does, leaving out what it does not show", () => { |
| 437 | const lines = pendingLines([ |
| 438 | { |
| 439 | id: "upd_1", |
| 440 | kind: "work.claimed", |
| 441 | at: "2026-10-14T10:00:00Z", |
| 442 | data: { repoId: "rep_1", number: 4, item: 4, claim: claim(4, { actor: bo, harness: "codex" }) }, |
| 443 | }, |
| 444 | { id: "upd_2", kind: "session.progressed", at: "2026-10-14T10:00:01Z", data: {} }, |
| 445 | ]); |
| 446 | assert.equal(lines.length, 1); |
| 447 | assert.equal(lines[0].id, "upd_1"); |
| 448 | assert.equal(lines[0].who, "bo"); |
| 449 | assert.match(lines[0].text, /claimed #4/); |
| 450 | }); |
| 451 | |
| 452 | test("a work item's pull requests are linked in the repository that has them, and not at all when none or two do", () => { |
| 453 | const href = pullHrefs([ |
| 454 | { path: "acme/web", pulls: [{ number: 41 }] }, |
| 455 | { path: "acme/api", pulls: [{ number: 44 }, { number: 7 }] }, |
| 456 | { path: "acme/docs", pulls: [{ number: 7 }] }, |
| 457 | // A repository deleted before work items kept no path. |
| 458 | { path: "", pulls: [{ number: 50 }] }, |
| 459 | ]); |
| 460 | assert.equal(href(41), "/acme/web/pull/41"); |
| 461 | // A target's pull request is the target's, not the origin's. |
| 462 | assert.equal(href(44), "/acme/api/pull/44"); |
| 463 | assert.equal(href(7), null, "two repositories have a #7: neither is guessed"); |
| 464 | assert.equal(href(99), null, "unknown"); |
| 465 | assert.equal(href(50), null, "a deleted repository"); |
| 466 | }); |
| 467 | |
| 468 | test("what someone asked leaves out what the harness or the editor put ahead of it", () => { |
| 469 | const injected = "<browser_instruction># Claude in Chrome browser automation\n\nYou have access to browser tools.</browser_instruction>"; |
| 470 | assert.equal(askedText(`${injected}Alright, what's next on our work items?`), "Alright, what's next on our work items?"); |
| 471 | assert.equal( |
| 472 | askedText("<system-reminder>Codebase instructions</system-reminder>\n<ide_selection>a.ts</ide_selection>\nWhy is the page crashing?"), |
| 473 | "Why is the page crashing?", |
| 474 | ); |
| 475 | // Only the blocks: a prompt that was nothing else asks nothing. |
| 476 | assert.equal(askedText(injected), ""); |
| 477 | // A tag someone types is theirs. |
| 478 | assert.equal(askedText("<div> renders twice, why?"), "<div> renders twice, why?"); |
| 479 | assert.equal(intentTitle(`${injected}Fix the sidebar under the app bar`), "Fix the sidebar under the app bar"); |
| 480 | }); |
| 481 | |
| 482 | test("acknowledging coordination updates is told apart from a note", () => { |
| 483 | assert.ok(isAcknowledgement("Coordination updates upd_c62316db756f071acf8fa1bbff: Unrelated session ended")); |
| 484 | assert.ok(!isAcknowledgement("The approvals were declined because nobody watched.")); |
| 485 | }); |