Public: anyone can read this; the crew can change it.

A pure read counts the crew pulls handed to a person after three review rounds in the last 24 h, naming the newest three #6600

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

scout/forge:scout/6588-a-pure-read-counts-the-crew-pulls-handed → main 1 commit fork 1 thread · 1 unresolved

  • Merged. scout/6588-a-pure-read-counts-the-crew-pulls-handed was merged into main as 563855d.

Review

Sign in to review this change.
2 files changed +90 −0
src/flow/handovers.ts added +43
@@ -0,0 +1,43 @@
1 /**
2 * Hand-overs in the last 24 h: pulls the crew gave to a person after three
3 * review rounds. Pure: the caller passes the hand-over notes as
4 * `{ pull, at }` (`at` an ISO time) and the clock as `now`.
5 *
6 * A pull counts once, at its latest note. A note exactly 24 h old still
7 * counts; one in the future or with an unreadable time does not.
8 */
9
10 export interface Handover {
11 readonly pull: number;
12 readonly at: string;
13 }
14
15 export interface Handovers {
16 readonly count: number;
17 /** Up to three pull numbers, newest hand-over first. */
18 readonly newest: readonly number[];
19 }
20
21 const DAY_MS = 24 * 60 * 60 * 1000;
22
23 export function handovers(rows: readonly Handover[], now: number): Handovers {
24 const latest = new Map<number, number>();
25 for (const r of rows) {
26 const t = Date.parse(r.at);
27 if (!Number.isFinite(t) || t > now || now - t > DAY_MS) continue;
28 if (t > (latest.get(r.pull) ?? -Infinity)) latest.set(r.pull, t);
29 }
30 const newest = [...latest]
31 .sort((a, b) => b[1] - a[1] || b[0] - a[0])
32 .slice(0, 3)
33 .map(([pull]) => pull);
34 return { count: latest.size, newest };
35 }
36
37 /** One line for /flow, or "" when nothing was handed over. */
38 export function handoversSays(h: Handovers): string {

The sentence hard-codes "three review rounds". When wired, take the round limit from the same constant the hand-over uses, so the wording cannot drift from the rule.

39 if (h.count === 0) return "";
40 const what = h.count === 1 ? "pull" : "pulls";
41 const list = h.newest.map((n) => `#${n}`).join(", ");
42 return `${h.count} ${what} handed to a person after three review rounds in the last 24 h, newest ${list}.`;
43 }
test/flow-handovers.test.ts added +47
@@ -0,0 +1,47 @@
1 import { expect, test } from "bun:test";
2 import { handovers, handoversSays } from "../src/flow/handovers.ts";
3
4 const now = Date.parse("2026-10-26T12:00:00Z");
5 const ago = (ms: number) => new Date(now - ms).toISOString();
6 const H = 3600_000;
7
8 test("the window edge: exactly 24 h counts, a millisecond more does not", () => {
9 const h = handovers(
10 [
11 { pull: 1, at: ago(24 * H) },
12 { pull: 2, at: ago(24 * H + 1) },
13 { pull: 3, at: new Date(now + 1).toISOString() },
14 { pull: 4, at: "nonsense" },
15 ],
16 now,
17 );
18 expect(h).toEqual({ count: 1, newest: [1] });
19 });
20
21 test("names the newest three, newest first, counting all", () => {
22 const h = handovers(
23 [
24 { pull: 10, at: ago(5 * H) },
25 { pull: 11, at: ago(1 * H) },
26 { pull: 12, at: ago(3 * H) },
27 { pull: 13, at: ago(2 * H) },
28 ],
29 now,
30 );
31 expect(h).toEqual({ count: 4, newest: [11, 13, 12] });
32 expect(handoversSays(h)).toBe(
33 "4 pulls handed to a person after three review rounds in the last 24 h, newest #11, #13, #12.",
34 );
35 });
36
37 test("a pull handed over twice counts once", () => {
38 const h = handovers([{ pull: 7, at: ago(H) }, { pull: 7, at: ago(2 * H) }], now);
39 expect(h.count).toBe(1);
40 expect(handoversSays(h)).toContain("1 pull handed");
41 });
42
43 test("empty says nothing", () => {
44 const h = handovers([], now);
45 expect(h).toEqual({ count: 0, newest: [] });
46 expect(handoversSays(h)).toBe("");
47 });

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs