docs(web): document the card/executions/template-delete changes
CLAUDE.md's web/app.py summary, WEB-UI.md's dashboard-layout and Executions sections, and a new design-history spec covering the tile removal, the compacted Host card, the dropped level-word strip, and the four cards' shared <dl> grammar.
This commit is contained in:
@@ -0,0 +1,128 @@
|
||||
# Dashboard card consolidation — design (2026-07-25)
|
||||
|
||||
Collapse the dashboard's redundant first two rows: six status-header tiles plus
|
||||
four compact cards that restated them. **The cards win; the tile row is
|
||||
deleted** and each card becomes the single owner of its subject.
|
||||
|
||||
## Problem
|
||||
|
||||
The rows said the same thing twice:
|
||||
|
||||
| Datum | Tile | Card | Also |
|
||||
|---|---|---|---|
|
||||
| Runner up/down | `Runners 2/2` | `partials/runners.html` unit table | Services card, *with* start/stop/restart |
|
||||
| Cache size + entries | `Cache` | `partials/cache.html` | Maintenance card (purge) |
|
||||
| CPU cores | sub-line `/ N cores` | `partials/host.html` "CPU cores" | — |
|
||||
| RAM total | sub-line `/ N GB` | `partials/host.html` "Memory" | — |
|
||||
|
||||
Runner state was in **three** places at once. Only three readouts were unique
|
||||
to the tiles — CPU%, RAM used, and the disk usage whose own card Stage E had
|
||||
already folded away — and only they needed a new home.
|
||||
|
||||
## Decision
|
||||
|
||||
One card per subject, no header:
|
||||
|
||||
| Card | Route | Poll | Owns |
|
||||
|---|---|---|---|
|
||||
| Host | `/host` | 5s | hostname, kernel, full CPU model, cores, **CPU% / RAM / disk + gauges**, uptime, load avg |
|
||||
| VMware modules | `/vmware` | 15s | `vmmon`, `vmnet`, `vmware-host.service` |
|
||||
| Runners | `/runners` | 5s | each `act-runner*` unit + sub-state |
|
||||
| Cargo cache | `/cache` | 5s | root, size, entries, oldest, retention |
|
||||
|
||||
Retired: `partials/status_header.html` and its `HealthSummary` /
|
||||
`_health_summary`. The VM count and orphan count went with them — running VMs
|
||||
are rows in the Executions card and the orphan sweep is a Maintenance action,
|
||||
so neither needed a numeric readout of its own.
|
||||
|
||||
Rejected alternative: keep the tiles as the state layer and merge the four
|
||||
cards into one "Platform" detail card. It splits every subject across two
|
||||
places (is the runner state a tile or a card?), and forces the header to start
|
||||
polling to avoid going stale. One owner per subject is simpler to keep honest.
|
||||
|
||||
## What survives from the tiles
|
||||
|
||||
`_resource_levels(resources, disk) -> ResourceLevels` keeps the threshold logic
|
||||
and the `tile-*` class names, now driving the Host card's three gauges:
|
||||
|
||||
- CPU / RAM: `> 75%` amber, `> 90%` red.
|
||||
- Disk: `> 70%` amber, `> 85%` red.
|
||||
- Any unreadable source: `unknown` (neutral), never a false green.
|
||||
|
||||
**Health is never color-only** — the rule the tiles were built around. Every
|
||||
gauge is followed by `.resource-level`: the level word plus a check/alert icon,
|
||||
both keyed off the same class the gauge fill uses.
|
||||
|
||||
## New data
|
||||
|
||||
`HostResources` gains `cpu_model` / `kernel` / `hostname`, read through the
|
||||
existing `_read_proc` seam but via `_read_proc_optional` — a missing
|
||||
`/proc/cpuinfo` must cost the CPU *name*, never the CPU *percentage* that
|
||||
shares the live-metric `try`. Each degrades to `""`, never `None`, so templates
|
||||
print them unguarded. `cpu_model` is `""` on architectures that don't emit
|
||||
`model name` (arm64), which is a normal outcome.
|
||||
|
||||
## Stage G — height parity across the four cards
|
||||
|
||||
Folding the tiles in left the Host card at eleven `<dl>` rows against its
|
||||
neighbours' two-to-five, and `.dashboard-grid` uses `align-items: stretch`:
|
||||
VMware, Runners and Cargo cache were stretched to Host's height and padded with
|
||||
~200px of empty card. Same data, packed to five rows:
|
||||
|
||||
| Was | Now |
|
||||
|---|---|
|
||||
| `Hostname`, `Kernel` (2 rows) | `System` — `ci-host · 7.0.0-28-generic` |
|
||||
| `CPU` (full model), `Cores` (2 rows) | `CPU` — `Intel Core i9-13900K · 16 cores` |
|
||||
| `Uptime` (1 row) | `.head-meta` at the right end of the card header |
|
||||
| `Load avg` (1 row) | dim sub-line under the CPU gauge |
|
||||
| level strip (word + check icon under each gauge) | the word moved into the gauge's `aria-label` |
|
||||
|
||||
Nothing is dropped and no new grammar is introduced — the card is still a
|
||||
`<dl>`, so it still reads like Cargo cache next to it.
|
||||
|
||||
Three details the shape depends on:
|
||||
|
||||
- **`.head-meta` must reset `text-transform`.** `.card h2` uppercases; an
|
||||
unreset header meta renders `26H 03M`. `.head-select` (the Executions
|
||||
row-count selector) already had to do this — same fix, same header slot.
|
||||
- **Uptime is spelled out** (`uptime 26h 03m`). Outside the `<dl>` it has no
|
||||
`<dt>`, and a bare duration in a header names nothing.
|
||||
- **Dropping the level strip does not make health color-only**, which is why
|
||||
it could go. The rule the tiles were built around was written when a tile
|
||||
showed a coloured bar and little else; the card states the number in text
|
||||
above every bar, so `42%` versus `92%` is already readable without seeing a
|
||||
colour. The word is kept for screen readers via `aria-label`, and the disk
|
||||
row prints `levels.disk_label` as its value when the probe fails and there
|
||||
is no gauge to attach it to.
|
||||
- **The CPU model is trimmed** through `cpu_short` — the raw ~40-char name
|
||||
wraps to three lines in a quarter-width track, which is the height being
|
||||
reclaimed. Full name on the row's `title`. This is the filter's first
|
||||
template caller: it was written for a status tile that was retired before it
|
||||
ever shipped.
|
||||
|
||||
## Stage H — one grammar for all four cards
|
||||
|
||||
Host and Cargo cache were always a `<dl>` at the page's plain 15px body text.
|
||||
VMware and Runners were a bordered `<table>` at `0.9rem` (13.5px) with
|
||||
uppercase `0.75rem` (11.25px) column headers — two font scales and two visual
|
||||
grammars in the same row of four cards. Converted VMware and Runners to the
|
||||
same `<dl>`: component/unit name as `<dt>`, the status badge plus its
|
||||
sub-state sharing one `<dd>` (the same "value + dim sub-text" shape Host's
|
||||
Disk row and Runners' own unit rows already used). No table, no header row,
|
||||
no per-row color tint (`tr.row-alert` dropped along with the `<tr>` it
|
||||
targeted — the badge already carries the color, so the row no longer
|
||||
double-encodes it, matching how Host and Cargo cache never tinted a whole
|
||||
row either).
|
||||
|
||||
## Tests
|
||||
|
||||
`tests/python/test_web_cards.py`: the `_resource_levels` threshold matrix, each
|
||||
card's contents (including the three Stage F additions and the three readouts
|
||||
inherited from the tiles), the "every gauge has a text level" rule, and the
|
||||
absence of any status header. Stage G pins the packed shape: the paired
|
||||
`System` / `CPU` rows (and the absence of the four `<dt>`s they replaced),
|
||||
`count("<dt>") == 5`, uptime in `.head-meta` and not in a `<dt>`, the load
|
||||
average with no `<dt>` of its own, the trimmed-model-plus-`title` pair, and the
|
||||
level word present in each gauge's `aria-label` while `resource-level` is
|
||||
absent from the markup — `id="status-header"` and `stat-tile` must not
|
||||
appear in the dashboard, and `/status-header` / `/platform` must 404.
|
||||
Reference in New Issue
Block a user