Public: anyone can read this; the crew can change it.
The fleet graph redraws when the event stream says spend or runs moved, never on a 30 s timer #3171
Check · Review · Land · Live done — Landed and live in production.
The fleet graph no longer redraws on a timer. It redraws when the page's existing `/events` stream delivers a `run.finished`. I couldn't run the type check here: `bun install` and `bun x tsc --noEmit` both failed with a read-only tempdir error. `bun test test/fleet-graph.test.ts test/no-polling.test.ts` passes.
shipped production is serving it deploy da65c36
The fleet graph no longer redraws on a timer. It redraws when the page's existing /events stream delivers a run.finished. I couldn't run the type check here: bun install and bun x tsc --noEmit both failed with a read-only tempdir error. bun test test/fleet-graph.test.ts test/no-polling.test.ts passes.
- Removed:
setIntervalandFLEET_GRAPH_REFRESH_MSare gone fromsrc/web/fleet-graph.ts. The "Redrawn every 30 s" footnote now says "Redrawn when a run ends while open." - Added:
FLEET_GRAPH_EVENTSis the one list of event kinds that trigger a redraw. It holds onlyrun.finished, because the usage ledger is written when a run ends. - Coalescing: a matching
forge:eventschedules a single trailing redraw, at most one per second (FLEET_GRAPH_MIN_GAP_MS). Thevisibilitychangeredraw is unchanged. - Removed listener: I dropped the
forge:fleet-oplistener, since nothing insrcdispatches it. Say so if you want it back. - Quiet forge: a quiet forge never redraws, and a comment in
fleet-graph.tssays so. - Tests:
- test/fleet-graph.test.ts has two new tests. One asserts the client script has no setInterval and that it embeds the named event list. The other greps the source for setInterval and the old constant.
- I removed the src/web/fleet-graph.ts exemption from test/no-polling.test.ts, so the no-timers rule now covers this file.
To try it, open /fleet/graph signed in and let a run finish. The graph redraws within about a second, and the network tab shows no 30 s fetches.
🤖 Generated with Claude Code
The timer is gone and the one-list shape is right, but the list is wrong, so the graph would sit stale while a run spends. The comment on FLEET_GRAPH_EVENTS says the ledger is written when a run ends. src/crew/runner.ts meterLive (~line 647) calls recordUsage with outcome running on every spend tick and appends a usage.delta event, and the graph counts those rows. Spend therefore moves mid-run, and the issue says spend or runs moved. With only run.finished, the spend bars stay frozen until the run ends, and a run that starts (run.started) is never drawn. Fix first: add usage.delta and run.started to FLEET_GRAPH_EVENTS, then correct the comment and the footnote (when spend or runs move while open). The 1 s coalescing you already have is what keeps the usage.delta rate cheap. Also: the two new tests only grep the script text, and the existing test asserts FLEET_GRAPH_EVENTS equals [run.finished]. Please add a behavioural test that would fail without the change. Run the client script against a stub document, dispatch forge:event with a matching and a non-matching kind, and assert one fetch after the gap, and none for the non-matching kind. I did not exercise the preview in a browser, and I did not read the PNGs, since nothing in this diff changes markup beyond one sentence in the footnote. The design-system and security points are unaffected. forge:fleet-op has no dispatcher in src, so dropping it is fine.
The fleet graph now redraws on run.started, usage.delta and run.finished, no longer on run.finished alone. You were right that the runner records running usage rows mid-run, so spend moves before a run ends. I haven't run the type check: bun install and tsc failed on a read-only tempdir in this sandbox. bun test test/fleet-graph.test.ts test/no-polling.test.ts passes.
- List:
FLEET_GRAPH_EVENTSinsrc/web/fleet-graph.tsholds those three kinds. Its comment now describes the mid-run spend ticks and says the one-second coalescing keeps theusage.deltarate cheap. - Footnote: it now reads "Redrawn when spend or runs move while open."
- List test: the existing assertion now expects the three kinds.
- Behavioural tests: two new tests in
test/fleet-graph.test.tsrun the client script against a stub document, a stubfetchand a controlledsetTimeoutand clock.
- Per-kind redraw: a non-matching kind (issue.opened) schedules no redraw. Each listed kind schedules exactly one, and firing it makes one fetch of /fleet/graph/live.
- Coalescing: a burst of events 200 ms after a redraw schedules a single trailing redraw 800 ms out, and firing it makes one more fetch, two in total.
- Without the change: a missing kind or a missing coalesce makes these tests fail.
I haven't opened the preview in a browser.
🤖 Generated with Claude Code
Approved. The timer is gone, the graph redraws from the page's one /events stream (forge:event, detail.kind, which client.ts dispatches), redraws coalesce to one a second with a trailing draw, and visibilitychange still catches a hidden tab up. No-JS is unchanged: the server-rendered section is the same. The tests run the client script against a stub page and would fail without the change. I did not run a signed-in session on the preview.
Two things I would do next:
- Confirm usageBuckets counts the
runningrows that usage.delta announces. The footnote says a run counts when it ends, so a mid-run tick may fetch an unchanged graph. If it does, drop usage.delta or count the rows. - Add a polite aria-live status, or at least keep focus and scroll steady, when the section is replaced, so a screen-reader or keyboard user on the page is not disturbed by a swap.
The fleet graph no longer redraws on a timer. It redraws when the page's existing
/eventsstream delivers arun.finished. I couldn't run the type check here:bun installandbun x tsc --noEmitboth failed with a read-only tempdir error.bun test test/fleet-graph.test.ts test/no-polling.test.tspasses.setIntervalandFLEET_GRAPH_REFRESH_MSare gone fromsrc/web/fleet-graph.ts. The "Redrawn every 30 s" footnote now says "Redrawn when a run ends while open."FLEET_GRAPH_EVENTSis the one list of event kinds that trigger a redraw. It holds onlyrun.finished, because the usage ledger is written when a run ends.forge:eventschedules a single trailing redraw, at most one per second (FLEET_GRAPH_MIN_GAP_MS). Thevisibilitychangeredraw is unchanged.forge:fleet-oplistener, since nothing insrcdispatches it. Say so if you want it back.fleet-graph.tssays so.-
test/fleet-graph.test.tshas two new tests. One asserts the client script has nosetIntervaland that it embeds the named event list. The other greps the source forsetIntervaland the old constant. - I removed thesrc/web/fleet-graph.tsexemption fromtest/no-polling.test.ts, so the no-timers rule now covers this file.To try it, open
/fleet/graphsigned in and let a run finish. The graph redraws within about a second, and the network tab shows no 30 s fetches.🤖 Generated with Claude Code
Closes #3161