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

A pure function writes the note posted when a clog clears: cause, time to detect, time to clear, stranded dollars #6539

Merged opened this

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

Merged

scout/forge:scout/6519-a-pure-function-writes-the-note-posted-w → main 1 commit fork

  • Merged. scout/6519-a-pure-function-writes-the-note-posted-w was merged into main as 21e32d9.

Review

Sign in to review this change.
2 files changed +74 −0
src/flow/clog_note.ts added +41
@@ -0,0 +1,41 @@
1 /**
2 * The note posted when a clog clears (#5930): the cause on one line, how long
3 * the clog took to be noticed and to clear, and the dollars stranded at the
4 * peak. Pure: plain data in, markdown out. Posting it is another module's job.
5 */
6
7 export interface ClogNoteInput {
8 /** Why the clog was declared, one phrase each. */
9 readonly reasons: readonly string[];
10 /** Epoch ms: when the clog began, when it was declared, when it cleared. */
11 readonly startedAt: number;
12 readonly detectedAt: number;
13 readonly clearedAt: number;
14 /** Dollars stranded at the peak; absent or not above zero leaves the line out. */
15 readonly strandedUsd?: number | null;
16 }
17
18 const MINUTE = 60_000;
19 const HOUR_AFTER_MIN = 90;
20
21 /** "12 min" up to 90 minutes, then "2.5 h"; a negative or non-finite span reads as 0 min. */
22 export function duration(ms: number): string {
23 const min = Number.isFinite(ms) && ms > 0 ? ms / MINUTE : 0;
24 if (min <= HOUR_AFTER_MIN) return `${Math.round(min)} min`;
25 return `${(Math.round(min / 6) / 10).toString()} h`;
26 }
27
28 export function clogNote(input: ClogNoteInput): string {
29 const why = input.reasons.map((r) => r.trim()).filter(Boolean);
30 const lines = [
31 `Clog cleared. Cause: ${why.length ? why.join("; ") : "not recorded"}.`,
32 "",
33 `- Time to detect: ${duration(input.detectedAt - input.startedAt)}`,
34 `- Time to clear: ${duration(input.clearedAt - input.detectedAt)}`,
35 ];
36 const usd = input.strandedUsd;
37 if (typeof usd === "number" && Number.isFinite(usd) && usd > 0) {
38 lines.push(`- Stranded at peak: $${usd.toLocaleString("en-US", { maximumFractionDigits: 2 })}`);
39 }
40 return lines.join("\n") + "\n";
41 }
test/flow-clog-note.test.ts added +33
@@ -0,0 +1,33 @@
1 /** The clog-cleared note: full, without dollars, minutes and hours, never negative. */
2 import { expect, test } from "bun:test";
3 import { clogNote, duration } from "../src/flow/clog_note.ts";
4
5 const M = 60_000;
6 const base = { reasons: ["red main", "no reviewer"], startedAt: 0, detectedAt: 10 * M, clearedAt: 70 * M };
7
8 test("a full note names cause, both times and the dollars", () => {
9 const note = clogNote({ ...base, strandedUsd: 1234.5 });
10 expect(note).toBe(
11 "Clog cleared. Cause: red main; no reviewer.\n\n- Time to detect: 10 min\n- Time to clear: 60 min\n- Stranded at peak: $1,234.5\n",
12 );
13 });
14
15 test("no dollars leaves the line out", () => {
16 expect(clogNote(base)).not.toContain("Stranded");
17 expect(clogNote({ ...base, strandedUsd: 0 })).not.toContain("Stranded");
18 });
19
20 test("minutes up to 90, hours past it", () => {
21 expect(duration(90 * M)).toBe("90 min");
22 expect(duration(91 * M)).toBe("1.5 h");
23 expect(duration(150 * M)).toBe("2.5 h");
24 });
25
26 test("a negative span reads as zero", () => {
27 expect(duration(-5 * M)).toBe("0 min");
28 expect(clogNote({ ...base, detectedAt: -M })).toContain("Time to detect: 0 min");
29 });
30
31 test("no reasons still reads", () => {
32 expect(clogNote({ ...base, reasons: [] })).toContain("Cause: not recorded.");
33 });

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs