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

The terminal dashboard's builders line says why builders are idle, with the same words as home #7733

Merged opened this

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

Merged

mason/forge:mason/7665-the-terminal-dashboard-s-builders-line-s → main 1 commit fork 1 thread · 1 unresolved

  • Merged. mason/7665-the-terminal-dashboard-s-builders-line-s was merged into main as 36b2c35.

Review

Sign in to review this change.
2 files changed +27 −2
src/tui/screens/dashboard.ts +11 −2
@@ -112,7 +112,7 @@ import { firedAlarms } from "../../flow/alarms.ts";
112112 import { lastClearedNote } from "../../flow/clog_tick.ts";
113113 import { forgeCeiling } from "../../flow/ceiling.ts";
114114 import { alarmIdleCrew } from "../../flow/alarm_idle_crew.ts";
115 import { idleLine, idleReason } from "../../crew/idle_reason.ts";
115 import { idleLine, idleReason, type IdleReason } from "../../crew/idle_reason.ts";
116116 import { noReleaseAlarm } from "../../flow/alarm_no_release.ts";
117117 import { redMainAlarm } from "../../flow/alarm_red_main.ts";
118118 import { alarmStuck } from "../../flow/alarm_stuck.ts";
@@ -345,7 +345,10 @@ export function dashboardScreen(f: Session, ctx: Ctx): Screen {
345345 ? []
346346 : [
347347 ...idleLines(f.app, now),
348 ...kpiLines(readKpis(f.db, now, kpiForge(f.app)), commit === null ? null : { sha: commit, since: ago(BOOTED) }),
348 ...withIdleWord(
349 kpiLines(readKpis(f.db, now, kpiForge(f.app)), commit === null ? null : { sha: commit, since: ago(BOOTED) }),
350 idleReason(f.app, now),
351 ),
349352 vitalsLine(readVitals(f.db, now)),
350353 ...firedAlarms(f.db, now, { ceilingUsd: forgeCeiling(f.app.config) }).map((a) => a.says),
351354 ...(cleared === undefined ? [] : [cleared]),
@@ -1434,6 +1437,12 @@ export function dashboardScreen(f: Session, ctx: Ctx): Screen {
14341437 return screen;
14351438 }
14361439
1440 /** The builders line ends ` · <word>` while they are idle, the word home prints (`idleReason`). */
1441 function withIdleWord(lines: string[], reason: IdleReason | null): string[] {

The startsWith("Builders running ") match ties this to kpiLines wording in src/flow/kpi_words.ts. If that wording changes, the word silently disappears. It is covered by the test today. A small exported constant for the prefix would be sturdier.

1442 if (reason === null) return lines;
1443 return lines.map((l) => (l.startsWith("Builders running ") ? `${l} · ${reason.word}` : l));
1444 }
1445
14371446 /** Why the builders are idle, as the one line an admin's home reads; none while they are not. */
14381447 function idleLines(app: App, now: Date): string[] {
14391448 const reason = idleReason(app, now);
test/tui-dashboard-idle.test.ts +16
@@ -75,3 +75,19 @@ test("the admin's home prints the reason's line; nobody else's does", async () =
7575 expect(await firstScreen(alice)).toContain(idleLine(reason!));
7676 expect(await firstScreen(bob)).not.toContain("Builders idle");
7777 });
78
79 test("the builders line ends with the reason's word while a slot is free; with every slot busy it has none", async () => {
80 const repo = forge.db.query("SELECT id FROM repo WHERE name = 'svc'").get() as { id: string };
81 forge.db.query("UPDATE repo SET autonomy_ceiling_usd = 1000 WHERE id = ?1").run(repo.id);
82 const reason = idleReason(forge, new Date());
83 expect(reason?.word).toBe("nothing ready");
84 const free = await firstScreen(alice);
85 expect(free).toMatch(new RegExp(`Builders running .* · ${reason!.word}`));
86
87 const bot = getUserByName(forge.db, "idlebot")!;
88 for (let i = 0; i < 2; i++) createRun(forge.db, { repoId: repo.id, kind: "agent", actorId: bot.id });
89 expect(idleReason(forge, new Date())).toBeNull();
90 const busy = await firstScreen(alice);
91 expect(busy).toContain("Builders running");
92 expect(busy).not.toContain("nothing ready");
93 });

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs