Skip to content

feat(ui): the load gauges colour over the console's own thresholds - #586

Merged
V3RON merged 4 commits into
task/569from
task/580
Oct 15, 2026
Merged

V3RON merged 4 commits into
task/569from
task/580

Conversation

@V3RON

@V3RON V3RON commented Oct 15, 2026 •

Copy link
Copy Markdown
Contributor

What

The CPU and disk gauges #575 added were always grey — the worker sends no threshold for either figure, so the panel had nothing to judge them against. A machine at 97% busy across all twelve cores looked exactly like one idling at 5%.

The console now holds those numbers itself: amber at 85% full, red at 95%, neutral below, read off the one figure the bar sits under.

before after
CPU 34% grey bar grey bar (under 85)
CPU 90% grey bar amber bar
memory 1.5 GiB of 36 GiB, no pressure word grey bar red bar (96%)
disk 12 GiB of 926 GiB free grey bar red bar (99%)

Spec: #580. Stacked on #575 — it changes the colour of gauges that PR introduces.

Closes #580

How

One file, ui/src/load-thresholds.ts, holds both numbers and the comparison:

export const LOAD_THRESHOLDS = { errorPercent: 95, warnPercent: 85 } as const;
export function levelOf(percent: number | undefined): Tone
  • Current value only. No earlier readings, no hold, no state carried between renders — that was the instruction, and it has a consequence worth naming: a build spike colours the CPU bar for one 15-second cycle, and a page reload re-derives the verdict from whatever the next reading says. A sustained-only rule needs history, history lives with the sampler in the daemon, and that is a wire change (The console colours each load gauge over its own thresholds #580's own out-of-scope section).
  • Neutral below the warn threshold, not green. So a colour always means "a threshold was crossed" rather than "healthy, as far as we guessed" — grey carries no claim.
  • Memory's pressure word wins. Where the worker reports normal/warn/critical, that's the worker's own judgement of the same fact and it outranks ours, at any fullness. Where it reports none — every Linux worker — memory falls back to the threshold like the other two.
  • The colour is the bar's alone. The figures stay numbers, the card's one-line summary doesn't colour, and simlock status reports the same figures with no verdict. That asymmetry is now said out loud in CONSOLE.md, so an operator who lives in the CLI isn't told a different story.

DESIGN.md's Load gauges entry used to claim a gauge shades "a level the console has a word for and nothing else". For CPU and disk that is now false, so the entry says so and bounds it: this is the one place in the console where a colour says what no word does, kept on a gauge beside the figure it judges — not in a status, not in a table, not in the card's text.

Done when

  • Unit, 186 green (5 new in load-thresholds.test.ts, +2 rewritten in worker-detail.test.tsx): each threshold at and just below its value, the two numbers' order and bounds, undefined → idle, CPU and disk coloured across both edges, memory falling back at 97% and staying neutral at 6%, and the pressure word winning at 50%.
  • Lane, 26 green (pnpm test:console --project=chromium): the colour test now answers the page's own /v1/workers read with the memory figures set per case, not just the word — so all six cases expect the same colour on a CI machine of any free-memory level, and one case pins word-over-threshold. Both themes.
  • They fail for the right reason. Mutation-checked: downgrading the error tone fails the lane's 97% with no word to paint the error gauge; dropping the error threshold fails four named unit tests; ignoring the pressure word fails two. Extracting the route into answerMemory was Fallow's cyclomatic rule on the test's arrow, not a style call.
  • The docs say what is now true — CONSOLE.md names the numbers and where the word wins; DESIGN.md bounds the exception.

Status

Implement: done Review: fix check, passed Hardware: n/a Gate: waiting for the stack

Assumptions

  • Assumption: 85/95 are the console's pick. The middle of the usual range (CloudWatch recommends 80% average; Checkmk ships 80 warn / 90 crit; Auvik 90/95), rounded up because a CI or dev box that also compiles trips 80 as a matter of course.
  • Assumption: Memory falls back to the threshold when the worker sends no word, rather than staying grey as feat(ui): the console draws each worker's load as gauges on its detail page #575 shipped it. The console colours each load gauge over its own thresholds #580 asks for one rule across the three figures; a memory bar that never colours on Linux would keep the gap this task exists to close.
  • Assumption: The disk threshold is a percentage of a total, not diskPressure.freeBytesThreshold in GiB. That config is real and drives the reaper, but it never reaches the worker view — reaching it is the wire change The console colours each load gauge over its own thresholds #580 puts out of scope. Consequence, stated plainly: on a 1 TB disk, red here means ~46 GiB free while Simlock's own reaper waits for 10 GiB. The two numbers answer different questions ("this volume is nearly gone" vs "I can no longer create a device"), and the bar will go red first.
  • Assumption: Thresholds live in the UI, so simlock status, the MCP server and the agent surfaces keep reporting the figures without a verdict. The same numbers can't reach them from ui/src — that's the daemon-side version of this, if this one earns its keep.

Not in this PR

No wire or protocol change. No level from the daemon. No queueDepth, no load history, no graph. No thresholds in config.

Review

Spec review: 3 blocking, 3 fixed, 0 notes. Code review: 4 blocking, 4 fixed, 0 notes. Claims review: 0 blocking, 0 fixed, 0 notes.
Rounds: 2. Tokens: n/a.

Fixed unreviewed (text and tests only, delivery rule 14):

  • code: ui/src/load-thresholds.test.ts:6 the error threshold's one-step-under edge was unproven at either seam — moving it 95 to 92 left 41 unit tests green
  • code: ui/src/views/worker-detail.test.tsx:610 the title still said memory with no pressure word is idle, which this branch's own change made false
  • code: docs/internal/DESIGN.md:188 gave LOAD_THRESHOLDS to CPU and disk only; memory takes it too wherever the worker sends no pressure word
  • code: e2e/console/workers.spec.ts:691 the comment promised a mis-coloured gauge would fail there, which stopped being true when the redundant token comparison came out
  • fix check, code: ui/src/views/worker-detail.test.tsx:623 a why-comment cited "a full disk" in a test that has no disk case
  • fix check, claims: ui/src/views/worker-detail.tsx:427 cited ADR 0011 for markup attributes; the ADR says only that the style prop creates no <style> element

Nothing was rejected: each finding was reproduced against the code at the reviewed commit, and the threshold-edge one was reproduced twice with the threshold moved by hand. The fix check ran because the stack's rebase needed a conflict resolved in docs/internal/DESIGN.md, not because round 1 confirmed a src finding — it confirmed none.

Written by an agent.

@V3RON
V3RON added this pull request to stack #590 October 15, 2026 08:04
V3RON added 4 commits October 15, 2026 10:26
The CPU and disk gauges were always grey: the worker sends no threshold for
them, so the panel had nothing to judge them against. A machine at 97% busy
across all twelve cores looked no different from one idling at 5%.

The console holds the numbers itself now, in one file. LOAD_THRESHOLDS is amber
at 85% full and red at 95%, and levelOf compares the figure the gauge is drawn
from and nothing else — no earlier readings, no hold, no state carried between
renders. A figure below 85% stays neutral, so a colour always says a threshold
was crossed rather than "healthy, as far as we guessed".

Memory keeps its colour from the worker's own pressure word where it sends one:
that is the worker's judgement of the same fact and outranks ours. Where it sends
none — every Linux worker — memory falls back to the threshold like the other
two.

The lane's colour test rewrites the memory figures it serves as well as the
pressure word, so all six cases expect the same colour on a CI machine of any
free-memory level, and one case pins that the word outranks the threshold. The
unit seam owns the table itself.

How the test says it changed too. Reading the memory fields needed a route
helper of its own and the shared per-gauge claim an assertion of its own — the
test's arrow had grown past Fallow's cyclomatic limit — and separating them
showed the per-gauge loop never needed to re-check which token a tone class
resolves to: the memory poll already proves that for all four classes in both
themes. So the loop now asserts only what it alone can see, that every gauge
carries a tone class and none of them the accent, and a mutation confirmed it
can still fail.

Closes #580
CONSOLE.md said the CPU and disk bars stay neutral because the worker sends no
threshold to judge them against. They no longer do: the console supplies its
own. The paragraph now states the numbers, says they read the one figure beside
the bar, and says where memory's pressure word wins instead — plus that the
figures stay numbers and `simlock status` reports them with no verdict, so a
reader who never opens the console is not told a different story.

DESIGN.md's Load gauges entry claimed a gauge shades "a level the console has a
word for and nothing else". For CPU and disk that is now false, so the entry
says so plainly and bounds the exception: the one place in the console where a
colour says something no word does, kept on a gauge and nowhere else — not in a
status, not in a table, not in the card's line of text.

Closes #580
…the lane no longer checks

Four review findings, all of them about a claim outrunning its proof.

The error threshold's lower edge was never tested, at either seam: moving it
from 95 to 92 left 41 unit tests green. Both seams now assert one step under
each edge, and the module's own test names the two numbers as literals, since
the console's manual prints them to a person and a relative assertion cannot
hold a constant to a promise about its value.

The memory row's title still said a view with no pressure is idle. It has been
false since this branch's own change: no word means the threshold decides, so
such a memory goes amber and red like the other two. The word cases and the
fallback cases now carry titles that say which one each proves, and the
fallback tests the spec's own 97% figure rather than the 95% edge alone.

The lane's per-gauge comment promised a gauge coloured by something other than
its own tone class would fail there. That stopped being true when the redundant
token comparison came out — the memory poll already proves all four classes in
both themes — and the comment survived the edit that made it false. It now
states what the predicate actually watches.
…one does

The comment above the fallback cases ended "and a full disk on the one step
under it". No disk appears in that test — the memory fixture carries the disk
from the shared reading at 77% used — and the one-step-under cases live in the
CPU and disk test, at 94% on both figures. A why-comment that points at another
test's cases is how a reader ends up trusting a proof that is not there.
@V3RON
V3RON marked this pull request as ready for review October 15, 2026 08:30
@V3RON
V3RON merged commit 1fb5555 into main Oct 15, 2026
33 checks passed
@V3RON
V3RON deleted the task/580 branch October 15, 2026 18:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

The console colours each load gauge over its own thresholds

1 participant