Repository navigation
Conversation
…l page Each of the Load panel's CPU, memory and disk figures gains a bar in a line of its own underneath, filled to that figure's own percent. Memory takes the colour of the pressure it already prints; CPU and disk stay neutral because the worker reports no limit for them that the console could judge them against. Swap gains none: the wire sends swap used and no swap total. The gauge is plain CSS rather than the charts library, because the string render the Load tests already use draws an empty div from Recharts, so a library gauge's fill could only be asserted in the browser lane. Moving the fill's width costs 0.13ms of layout per change, measured, over a panel that changes once every 15 seconds; the slide uses the existing --duration token, which prefers-reduced-motion already zeroes. Closes #569
The spec check on #569 found three gaps this commit closes in code: - LoadFacts grew to cyclomatic 11, so the three figure rows' shared "unavailable, or a figure with a gauge under it" shape moves to FigureRow, which each of CPU, memory and disk hands its own text to. - Nothing proved the bar is under its figure rather than beside it, or that it caps rather than filling the value column; the new lane test measures both on a page wide enough for the two to differ. - The fill's own 120 ms ease could land a measurement between two levels, so the percent test now waits for the reading and the fill to settle together instead of only for the reading. Closes #569
CI runs the console lane on Linux, and only macOS reports a memory pressure, so a live worker can only ever paint the idle tone there. Deleting the ok, warn and error rules from the stylesheet left the suite green. The colour test now asks the stylesheet what each of the four tone classes would paint, in both themes, and still never the accent. It checks the colour rule only. Which pressure reaches which class is the unit test's, and no healthy machine can be made to report warn or critical. Closes #569
The gauge clamped with Math.round while the figure above it rounds with the module's own roundHalfUp. The two differ just under a half: a busy share of 0.49999999999999994 printed 1% and drew a 0 bar. No reading will ever be that number, but the spec says the two round identically and a near-copy of the canonical helper sat beside it. The test's list now includes that float, so its title is true of every case it runs and the pair of roundings is told apart by a named assertion. Found by the code review. Closes #569
One test was titled for a figure that says Unavailable and also carried a Memory row that says "12.0 GiB of 0 B available" — a figure shown with its number and no gauge, which is a different promise and was named by no title. It now has its own, and asserts the number survives as well as the bar's absence. Found by the code review. Closes #569
Only macOS reports a memory pressure, so a worker in CI — Linux — can only ever paint the idle gauge. The colour test closed that with a probe: it built a load-bar in the page and asked the stylesheet what each tone class would paint. That proved the stylesheet's rule, but never passed through the component, so the tone map it leans on was proven only in the unit seam. This answers the page's own read of /v1/workers with memory.pressure set to each of the four states and every other field left as the daemon sent it, so the real component paints each tone, in both themes, and no tone rule — or accent substitute — survives being changed. Verified: a dark-only swap of one tone's colour, and painting another with the accent, each fail a named assertion. Two smaller things from the same review. The fill-width test waited for the reading's own stamp to hold still, which moves between two reads even when the levels do not, so it retried for nothing; it now compares the levels themselves. And the transition test's title promised a slide to a new level, which its body never sees — it reads the stylesheet's transition back, so that is what it says. Closes #569
CONSOLE.md said CPU and disk stay neutral because no CPU or disk "limit" is reported, on a page that prints "(limit)" beside the CPU figure whenever the worker limits itself — a container's user saw both at once. The console invents no thresholds, so the word is threshold; the doc says so, and says what a "(limit)" is instead. It also now says the memory bar is neutral where no pressure is reported, which is every Linux worker. DESIGN.md's new entry opened "A figure of how full something is carries a bar", which is binding for later console work and makes the worker card's Load and Capacity rows wrong as they stand — and a card gauge is this task's stated non-goal. It now describes the panel it was written for and says so. Found by the code review. Closes #569
V3RON
marked this pull request as ready for review
October 15, 2026 06:55
V3RON
added this pull request to stack #590
October 15, 2026 08:04
…e charts rule is The review probed the claim rather than reading it: a page served with `style-src 'self'` drops the whole style attribute, so the custom property arrives empty, while the same level set through the CSSOM resolves. The gauge works because React writes styles through the CSSOM — which is what ADR 0011 says — and not because style attributes sit outside `style-src`, which is what the comment and DESIGN.md both said. The distinction is not academic here: the unit seam renders the gauge with `renderToStaticMarkup`, and that markup is exactly what the policy refuses, so only this component's own render paints a bar. Both now say the reason, and the guide adds the consequence the reviewer named: a gauge is drawn by the console's code, never by server-rendered output. The gauge entry also pointed at the charts rule "below" it. That rule is the bullet ten lines above, so the pointer sent a reader away from the answer.
…e attributes The fix that replaced one unsupported CSP claim wrapped the replacement in another: it cited ADR 0011 for markup style attributes being refused and the bar coming out empty. The ADR says neither. It states one thing — React's `style` prop is allowed because it creates no `<style>` element — and is silent on attributes, so citing it for what the policy does to markup puts a claim in the ADR that its authors never wrote. The attribution now stops where the ADR does, and the comment says outright not to read the policy's behaviour into it. What the probe did establish stays: a level written into a style attribute is refused by `style-src 'self'` and the bar comes out empty; the same level set through the CSSOM resolves, because the policy does not reach there. That is observed here, not sourced from the ADR, and the comment now keeps the two apart. Same correction in the design guide's gauge entry.
V3RON
added a commit
that referenced
this pull request
Oct 15, 2026
) ## 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: ```ts 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 (#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 #575 shipped it. #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 #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.*
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Each of the Load panel's CPU, memory and disk figures gains a bar in a line of its own underneath, filled to that figure's own percent. The panel was text alone.
34% busy, 10 cores9.8 GiB of 32.0 GiB available112.4 GiB of 494.4 GiB freeSwap gains none: the wire sends
swapUsedBytesand no swap total, so there is nothing to divide by.Spec: #569. Part of #527.
Closes #569
How
Plain CSS, not the charts library — the string render the Load tests already use draws an empty
<div>from Recharts, so a library gauge's fill could only have been asserted in the browser lane. This one is asserted at both seams.loadGauges()inui/src/views/load-model.tsanswers each figure's fullness as a whole 0–100 percent, and nothing where the reading has no denominator. Whole, so a CPU bar rounds the way the busy share above it rounds. Clamped, because a memory or disk pair is two independent non-negative numbers on the wire and the console revalidates no view.--load-barcustom property; every colour comes fromstyles.cssby tone class (load-bar-ok/-warn/-error/-idle, deliberately not.status-*— the lane checks that every.statusshows a word, and a bar has none). The CSP refuses an injected<style>element and applies a style attribute, which is how the level is allowed (checked against a live daemon).--status-idle: the search found no CPU or disk threshold anywhere that reaches a view, and the console invents no threshold.aria-hiddenand takes no focus — the figure above it is the text, so nothing is said twice and no number is visible only as a shape.FigureRowholds the shared "unavailable, or a figure with its gauge under it" shape, so CPU, memory and disk each hand it their own text rather than repeating it.Motion
The fill slides to a new level over
--duration, whichprefers-reduced-motion: reducealready zeroes document-wide. Measured before choosing it (Chromium, Chrome's own counters, 600 level changes on three bars at one per frame — 15× the real 15 s cadence):transition: widthcost 0.132 ms layout and 0.827 ms task per frame;transform: scaleX()skipped the layouts (7 vs 614) but cost more total task time (0.900 ms). Neither made a long task, so the compositor-only trick buys nothing here andwidthis the simpler shape.DESIGN.md's Motion section said nothing slides, so this PR amends it with the one exception and its reason, and adds the gauge to the guide's component list.
docs/CONSOLE.md's Load panel paragraph says what the panel now draws.Done when
Unavailablerow, a zero-total figure printed with its number and no bar, the memory tone from each pressure level, CPU/disk idle at any level, the number said once, no bars for no-load/disconnected, and the card unchanged.pnpm test:console --project=chromiumis green. 4 new lane tests: each fill's width is its figure's percent of its track within a pixel, one fill per figure required; the track caps at 20rem rather than filling its value column and sits under the figure, left-aligned with it; each fill's colour is its tone's status token and never the accent, in both themes — with all four tones painted by the real component in both themes, driven by answering the page's own/v1/workersread withmemory.pressureset to each level in turn (a Linux worker never reports one); the transition iswidthover--durationand0sunder emulated reduced motion. The lane's existing 360px, keyboard and axe AA rules still pass.max-widthcap, or turning the bar back into an inline sibling each turns its own test red. On the colour side: deleting the three unreachable tone rules, swapping one tone's colour in the dark theme only, and painting another with the accent each fail a named assertion.normaldrew the ok green at 41%, CPU 26% and disk 97% drew neutral.warnandcriticalare no longer left to that check: the lane drives them through the route the page reads, so the whole chain — pressure → tone class → token → painted pixel — is asserted in both themes without a macOS-only machine.66 48 34 25 20 17 16 16px over eight frames after a level change (transition: width 0.12s), and reads0sunder emulated reduce. A 120 ms ease is not settle-able by eye, so the lane asserts the computed transition.docs/CONSOLE.mdLoad panel paragraph;docs/internal/DESIGN.mdMotion, plus a Load gauges entry under Components.Status
Implement: done Review: round 1, passed Hardware: n/a Gate: waiting for the stack
Assumptions
--fieldfor a track. The spec fixes the shape (a bar under the figure) but not these numbers; taken from the console's own vocabulary — the 8px square a status uses,--fieldas the recessed surface, and a cap so a wide panel does not turn one line of text into a band.20remagainst the value column (minmax(0, 1fr)), so on a wide panel the bar stops short of the column's end. The spec says "under the figure"; which of the column's widths it fills it does not say, and the narrower one is the quieter.browserName), the way the lane's keyboard tests already branch on it. Playwright documentsreducedMotionfor all three engines, but only Chromium's was verified on this machine and its Firefox/WebKit binaries are not installed here; a guard that could hide a real failure in the other two is worse than a narrower one.What the review changed
A fresh-context review of this diff (five axes, spec in hand) found four things worth a commit each; all are in the branch:
Math.roundwhile the figure above it rounds with the module'sroundHalfUp. They differ just under a half — a busy share of0.49999999999999994printed1%and drew a0bar. Now one round, and that float is in the test's list so its title is true of every case.docs/CONSOLE.mdsaid no CPU or disk "limit" is reported, on a page that prints(limit)beside the CPU figure when the worker limits itself — a container's user saw both at once. The word is threshold; the doc says so, and what a(limit)is.Plus two test-honesty fixes: the fill-width test waited on the reading's stamp, which moves between reads even when the levels do not, so it retried for nothing; and the transition test's title promised a slide its body never sees, so it now says what it checks.
Not in this PR
No gauge on the worker's card. No swap gauge. No CPU or disk threshold — that would be a wire change of its own. No history or graph. No wire or protocol change.
Review
Spec review: 0 blocking, 0 fixed, 1 note. Code review: 1 blocking, 1 fixed, 0 notes. Claims review: 1 blocking, 1 fixed, 1 note.
Rounds: 1. Tokens: n/a.
Fixed unreviewed (text only, delivery rule 14):
style-srcdoes not govern style attributes; probed, the policy refuses the attribute and the level survives because React writes through the CSSOMstyleprop creates no<style>elementNothing was rejected: each finding was reproduced against the code at the reviewed commit and confirmed. The CSP one came with a probe — a page served with
style-src 'self'logged a violation and left the custom property empty from markup, and resolved it through the CSSOM.Written by an agent.