phase: 19_ups_events_expandable

**Phase 19 final verification pass — all tasks already complete; everything green, no code changes needed.**

- **Verified (all 4 task files present in `complete/`)**: `details.events-box` disclosure on `/ups` with no `open` attribute and `#events` inside it; lazy fetch (`if (state.events.open) await refreshEvents()`), toggle stale rule on `state.pollMs`, success-path-only `painted`/`fetchedAt`; `charts-box`-shaped CSS with the `> .body` padding restatement; 4 new served-byte pins in `tests/test_api.py`.
- **Criterion 1 (collapsed on load, no first-poll fetch)**: PASS — re-ran `data/logs/19-task04-verify.cjs` against the live dev server (real upsd, 2 UPSes): **28/28 PASS**, `0` `/api/events` requests across 2 closed polls vs 4 snapshot polls; expand → exactly 1 request, 50 rows with `Oct 20, 07:11` timestamps + severity chips + `1–50 of 200` pager.
- **Criterion 2 (cache/stale/poll-while-open)**: PASS — fresh re-open 2→2 requests, stale re-open 2→3, loop refreshes while open, close asks for nothing, failed fetch keeps rows and doesn't stamp the cache.
- **Criterion 3**: `uv run pytest` → **1458 passed, 15 skipped**; `uv run pytest --cov` → **98.94%** (floor 94, unchanged); `uv run ruff check src tests agent` → clean; `uv run pyright` → **0 errors**.
- **Criterion 4**: `bash .agents/validate.sh` → **exit 0** (incl. `node --check` of all page scripts + repo-hygiene gates).
- **Criterion 5**: `data/logs/ups-events-closed.png` (collapsed `▸ POWER EVENTS`, 0 events requests noted in `ups-events-verify.out`) and `ups-events-open.png` (`▾`, bounded 320px list, pager strip) regenerated and inspected this pass.
- **Criterion 6 (no regressions)**: full suite green incl. UPS contract/pager pins; `uv run pytest -m live --live` (with `.env.dev`) → **11 passed**; phase 18's `nav-final-pass.cjs` sweep re-run → **45/45** (this phase's markup/CSS didn't touch the masthead).
- **Documentation**: full README.md (980 L) + AGENTS.md (1992 L) review against the code — verified env templates vs `config.py`, API route table vs `api.py`, installer flags vs `deploy/install-agent.sh`, agent routes vs `host_agent.py`, test/coverage/live tallies, `data/logs` artifacts, and live fleet facts (`HOST_TARGETS` all `http://`, all three agents `version 1.0.0`, nine sources `ok`). AGENTS.md UI conventions and README's events paragraph already carry the disclosure/lazy/stale sentences from tasks 01–04. **Verified consistent — no changes needed.**
- **Notable**: made zero repo edits (working tree holds only this phase's task work). `uv pip list --outdated` couldn't complete (PyPI 503 for the project name) — no dependency moved this pass, so rule 8 is not triggered; validate.sh's earlier advisory still lists `fastapi 0.153.0 → 0.156.3`, deliberately not upgraded in a UI/verification phase.
- **Next pending phase**: `.agents/phases/todo/20_masthead_nav_polish/` (tasks 01–04).
This commit is contained in:
2026-10-20 19:17:22 -04:00
parent f9b1565abb
commit 95df8b695b
31 changed files with 694 additions and 10 deletions
@@ -0,0 +1,12 @@
**Phase 19 final verification pass — all tasks already complete; everything green, no code changes needed.**
- **Verified (all 4 task files present in `complete/`)**: `details.events-box` disclosure on `/ups` with no `open` attribute and `#events` inside it; lazy fetch (`if (state.events.open) await refreshEvents()`), toggle stale rule on `state.pollMs`, success-path-only `painted`/`fetchedAt`; `charts-box`-shaped CSS with the `> .body` padding restatement; 4 new served-byte pins in `tests/test_api.py`.
- **Criterion 1 (collapsed on load, no first-poll fetch)**: PASS — re-ran `data/logs/19-task04-verify.cjs` against the live dev server (real upsd, 2 UPSes): **28/28 PASS**, `0` `/api/events` requests across 2 closed polls vs 4 snapshot polls; expand → exactly 1 request, 50 rows with `Oct 20, 07:11` timestamps + severity chips + `1–50 of 200` pager.
- **Criterion 2 (cache/stale/poll-while-open)**: PASS — fresh re-open 2→2 requests, stale re-open 2→3, loop refreshes while open, close asks for nothing, failed fetch keeps rows and doesn't stamp the cache.
- **Criterion 3**: `uv run pytest` → **1458 passed, 15 skipped**; `uv run pytest --cov` → **98.94%** (floor 94, unchanged); `uv run ruff check src tests agent` → clean; `uv run pyright` → **0 errors**.
- **Criterion 4**: `bash .agents/validate.sh` → **exit 0** (incl. `node --check` of all page scripts + repo-hygiene gates).
- **Criterion 5**: `data/logs/ups-events-closed.png` (collapsed `▸ POWER EVENTS`, 0 events requests noted in `ups-events-verify.out`) and `ups-events-open.png` (`▾`, bounded 320px list, pager strip) regenerated and inspected this pass.
- **Criterion 6 (no regressions)**: full suite green incl. UPS contract/pager pins; `uv run pytest -m live --live` (with `.env.dev`) → **11 passed**; phase 18's `nav-final-pass.cjs` sweep re-run → **45/45** (this phase's markup/CSS didn't touch the masthead).
- **Documentation**: full README.md (980 L) + AGENTS.md (1992 L) review against the code — verified env templates vs `config.py`, API route table vs `api.py`, installer flags vs `deploy/install-agent.sh`, agent routes vs `host_agent.py`, test/coverage/live tallies, `data/logs` artifacts, and live fleet facts (`HOST_TARGETS` all `http://`, all three agents `version 1.0.0`, nine sources `ok`). AGENTS.md UI conventions and README's events paragraph already carry the disclosure/lazy/stale sentences from tasks 01–04. **Verified consistent — no changes needed.**
- **Notable**: made zero repo edits (working tree holds only this phase's task work). `uv pip list --outdated` couldn't complete (PyPI 503 for the project name) — no dependency moved this pass, so rule 8 is not triggered; validate.sh's earlier advisory still lists `fastapi 0.153.0 → 0.156.3`, deliberately not upgraded in a UI/verification phase.
- **Next pending phase**: `.agents/phases/todo/20_masthead_nav_polish/` (tasks 01–04).
@@ -0,0 +1,65 @@
== uv run pytest --cov (hermetic: no network, live tests skip) ==
........................................................................ [ 4%]
........................................................................ [ 9%]
........................................................................ [ 14%]
........................................................................ [ 19%]
........................................................................ [ 24%]
..................................s..................................... [ 29%]
........................................................................ [ 34%]
........................................................................ [ 39%]
........................................................................ [ 43%]
........................................................................ [ 48%]
........................................................................ [ 53%]
........................................................................ [ 58%]
........................................................................ [ 63%]
.......................................................................s [ 68%]
ssssssssss.............................................................. [ 73%]
........................................................................ [ 78%]
........................................................................ [ 83%]
........................................................................ [ 87%]
........................................................................ [ 92%]
...............................................sss...................... [ 97%]
................................. [100%]
=============================== warnings summary ===============================
.venv/lib64/python3.14/site-packages/fastapi/testclient.py:1
/var/home/ducoterra/Projects/Personal/dashboards/.venv/lib64/python3.14/site-packages/fastapi/testclient.py:1: StarletteDeprecationWarning: Using `httpx` with `starlette.testclient` is deprecated; install `httpx2` instead.
from starlette.testclient import TestClient as TestClient # noqa
-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
================================ tests coverage ================================
_______________ coverage: platform linux, python 3.14.4-final-0 ________________
Name Stmts Miss Cover Missing
------------------------------------------------------------------
src/dashboards/__init__.py 3 0 100%
src/dashboards/__main__.py 14 1 93% 32
src/dashboards/alerts.py 176 0 100%
src/dashboards/api.py 436 0 100%
src/dashboards/app.py 185 0 100%
src/dashboards/auth.py 265 0 100%
src/dashboards/certs.py 292 0 100%
src/dashboards/collectors.py 205 5 98% 284-285, 289-290, 307
src/dashboards/config.py 120 0 100%
src/dashboards/debug.py 14 0 100%
src/dashboards/poller.py 165 3 98% 192-194, 279
src/dashboards/prom.py 112 5 96% 126, 163, 177, 180, 183
src/dashboards/rates.py 73 0 100%
src/dashboards/severity.py 49 0 100%
src/dashboards/sources/__init__.py 0 0 100%
src/dashboards/sources/disks.py 134 0 100%
src/dashboards/sources/host.py 209 0 100%
src/dashboards/sources/llm.py 178 1 99% 285
src/dashboards/sources/nut.py 413 20 95% 153, 172, 186, 198, 204, 217, 225, 241, 319-320, 322, 579, 591, 626, 657, 661, 718-719, 724, 736
src/dashboards/sources/nut_sim.py 134 0 100%
src/dashboards/store.py 313 2 99% 553-554
------------------------------------------------------------------
TOTAL 3490 37 99%
Required test coverage of 94% reached. Total coverage: 98.94%
1458 passed, 15 skipped, 1 warning in 153.66s (0:02:33)
== uv run ruff check src tests agent ==
All checks passed!
== uv run pyright ==
0 errors, 0 warnings, 0 informations
== node --check src/dashboards/web/static/*.js ==
advisory: dependencies are out of date (AGENTS.md rule 8 — update on purpose):
fastapi 0.153.0 0.156.4 wheel
@@ -0,0 +1,13 @@
Done — task 01 complete.
- **`src/dashboards/web/pages/ups.html`**: the `Power events` card body moved inside a `<details class="events-box">` with `<summary>Power events</summary>` — no `open` attribute, `#events`/`ul.events`/`loading…` placeholder unchanged, rest of the page byte-identical.
- **`tests/test_api.py::test_ups_power_events_feed_sits_behind_a_collapsed_disclosure`**: served-byte pin (`details.events-box` present, no `open` attr, summary label, `#events` ul + placeholder *inside*, no duplicate `<h3>`, page's other ids intact). Verified it bites: mutations (added `open`, ul moved out, summary renamed) each fail it.
- `bash .agents/validate.sh` → **exit 0**: `uv run pytest --cov --cov-fail-under=94` = **1455 passed, 15 skipped**, coverage **98.94 %** (floor 94, unchanged); `ruff check src tests agent` "All checks passed"; `pyright` "0 errors"; `node --check` of `web/static/*.js` clean.
- **Browser proof (live dev server, `data/logs/19-task01-disclosure.cjs`)**: 10/10 checks PASS — `data/logs/19-ups-events-closed.png` (card paints one 21 px `Power events` summary line; `::details-content` is `content-visibility: hidden`) and `-open.png` (expand draws the bounded 320 px list, 50 rows, date/time rows intact).
- **Docs**: `AGENTS.md` UI-conventions event-feed paragraph gains the `/ups` disclosure sentence (collapsed by default, `#events` stays inside the disclosure, pinned on served bytes); `README.md` `/ups` feed paragraph says the card is a closed `Power events` summary until opened.
- **Decisions / notes**:
- Markup only, per the task file — no JS/CSS touched, so `/ups` still fetches the feed on every poll while closed (that is task 02's lazy gate) and the open body currently has `.body` padding 0 because `.card > .body` no longer matches (task 03's `details.events-box` CSS closes it). Both intermediate states render correctly and every test is green.
- Pinned the summary as the plain word `Power events` (no count chip — an unfetched count would be rule 2's invented number) and no `data-k` attribute, as the task's ASSUMPTIONs state.
- `uv pip list --outdated` advisory (fastapi 0.153→0.156) left untouched: no dependency moved in this task, same handling as phases 15/17/18.
Next pending task: `.agents/phases/todo/19_ups_events_expandable/02_events_lazy_loading.md` (lazy fetch / poll-while-open in `page-ups.js`).
@@ -0,0 +1,65 @@
== uv run pytest --cov (hermetic: no network, live tests skip) ==
........................................................................ [ 4%]
........................................................................ [ 9%]
........................................................................ [ 14%]
........................................................................ [ 19%]
........................................................................ [ 24%]
...............................s........................................ [ 29%]
........................................................................ [ 34%]
........................................................................ [ 39%]
........................................................................ [ 44%]
........................................................................ [ 48%]
........................................................................ [ 53%]
........................................................................ [ 58%]
........................................................................ [ 63%]
....................................................................ssss [ 68%]
sssssss................................................................. [ 73%]
........................................................................ [ 78%]
........................................................................ [ 83%]
........................................................................ [ 88%]
........................................................................ [ 93%]
............................................sss......................... [ 97%]
.............................. [100%]
=============================== warnings summary ===============================
.venv/lib64/python3.14/site-packages/fastapi/testclient.py:1
/var/home/ducoterra/Projects/Personal/dashboards/.venv/lib64/python3.14/site-packages/fastapi/testclient.py:1: StarletteDeprecationWarning: Using `httpx` with `starlette.testclient` is deprecated; install `httpx2` instead.
from starlette.testclient import TestClient as TestClient # noqa
-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
================================ tests coverage ================================
_______________ coverage: platform linux, python 3.14.4-final-0 ________________
Name Stmts Miss Cover Missing
------------------------------------------------------------------
src/dashboards/__init__.py 3 0 100%
src/dashboards/__main__.py 14 1 93% 32
src/dashboards/alerts.py 176 0 100%
src/dashboards/api.py 436 0 100%
src/dashboards/app.py 185 0 100%
src/dashboards/auth.py 265 0 100%
src/dashboards/certs.py 292 0 100%
src/dashboards/collectors.py 205 5 98% 284-285, 289-290, 307
src/dashboards/config.py 120 0 100%
src/dashboards/debug.py 14 0 100%
src/dashboards/poller.py 165 3 98% 192-194, 279
src/dashboards/prom.py 112 5 96% 126, 163, 177, 180, 183
src/dashboards/rates.py 73 0 100%
src/dashboards/severity.py 49 0 100%
src/dashboards/sources/__init__.py 0 0 100%
src/dashboards/sources/disks.py 134 0 100%
src/dashboards/sources/host.py 209 0 100%
src/dashboards/sources/llm.py 178 1 99% 285
src/dashboards/sources/nut.py 413 20 95% 153, 172, 186, 198, 204, 217, 225, 241, 319-320, 322, 579, 591, 626, 657, 661, 718-719, 724, 736
src/dashboards/sources/nut_sim.py 134 0 100%
src/dashboards/store.py 313 2 99% 553-554
------------------------------------------------------------------
TOTAL 3490 37 99%
Required test coverage of 94% reached. Total coverage: 98.94%
1455 passed, 15 skipped, 1 warning in 154.08s (0:02:34)
== uv run ruff check src tests agent ==
All checks passed!
== uv run pyright ==
0 errors, 0 warnings, 0 informations
== node --check src/dashboards/web/static/*.js ==
advisory: dependencies are out of date (AGENTS.md rule 8 — update on purpose):
fastapi 0.153.0 0.156.2 wheel
@@ -0,0 +1,19 @@
Task 02 complete.
**Implemented**
- `src/dashboards/web/static/page-ups.js`: `state.events {open,painted,fetchedAt}`; `refresh()` now does `if (state.events.open) await refreshEvents();` (poll loop feeds the list only while open); `refreshEvents()` stamps `fetchedAt`/`painted` **after** `Dash.eventsPager()` on the success path only (a failure keeps the rows and re-fetches on next open); `toggle` listener bound before `poll()` — first expand fetches, re-expand inside one `state.pollMs` paints the cached rows, older than that re-fetches; header comment documents the lazy rule.
- `tests/test_api.py`: `test_ups_events_are_fetched_only_while_the_disclosure_is_open` — served-script pins for the state shape, the `refresh()` gate (incl. no surviving ungated `await refreshEvents()`), the success-path-only stamp ordering, and the toggle's stale rule bound before the poll starts.
- `data/logs/19-task02-lazy.cjs` (+ `.out`): live browser proof, 21 checks — 0 `/api/events` requests across 2 closed polls, exactly 1 on first expand, loop refreshes while open, stops when closed, stale re-open re-fetches, fresh re-open makes no request (rows + pager footer + scroll identical), failed fetch keeps 50 rows and never moves the stamp. Screenshots: `19-ups-events-card-closed.png` (one summary line), `19-ups-events-card-open.png`, `19-ups-events-{closed,open,fetch-failed}.png`.
**Tests / lint / coverage** (all green)
- `uv run pytest` → 1456 passed, 15 skipped, exit 0
- `uv run pytest -q --cov --cov-fail-under=94` → Total coverage: 98.94% (unchanged)
- `uv run ruff check src tests agent` → All checks passed; `uv run pyright` → 0 errors, 0 warnings
- `bash .agents/validate.sh` → exit 0 (incl. `node --check` of `page-ups.js`); only the pre-existing advisory `fastapi 0.153.0 → 0.156.2` (no dependency touched by this task)
- Dev server http://127.0.0.1:8090 still `status: ok` (all 9 sources)
**Docs**: AGENTS.md UI conventions (event-feed paragraph) and README's `/ups` expandable bullet now state the lazy rule — closed = not fetched, first expand fetches, re-open inside one poll interval redraws what is there.
**Decisions**: stale bound = one `state.pollMs` (task's ASSUMPTION); stamp placed after the pager call so a pager throw can't mark an unpainted list as cached. Live infrastructure untouched (repo-side + local browser only).
**Next pending task**: `19_ups_events_expandable/03_disclosure_css.md`.
@@ -0,0 +1,65 @@
== uv run pytest --cov (hermetic: no network, live tests skip) ==
........................................................................ [ 4%]
........................................................................ [ 9%]
........................................................................ [ 14%]
........................................................................ [ 19%]
........................................................................ [ 24%]
................................s....................................... [ 29%]
........................................................................ [ 34%]
........................................................................ [ 39%]
........................................................................ [ 44%]
........................................................................ [ 48%]
........................................................................ [ 53%]
........................................................................ [ 58%]
........................................................................ [ 63%]
.....................................................................sss [ 68%]
ssssssss................................................................ [ 73%]
........................................................................ [ 78%]
........................................................................ [ 83%]
........................................................................ [ 88%]
........................................................................ [ 92%]
.............................................sss........................ [ 97%]
............................... [100%]
=============================== warnings summary ===============================
.venv/lib64/python3.14/site-packages/fastapi/testclient.py:1
/var/home/ducoterra/Projects/Personal/dashboards/.venv/lib64/python3.14/site-packages/fastapi/testclient.py:1: StarletteDeprecationWarning: Using `httpx` with `starlette.testclient` is deprecated; install `httpx2` instead.
from starlette.testclient import TestClient as TestClient # noqa
-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
================================ tests coverage ================================
_______________ coverage: platform linux, python 3.14.4-final-0 ________________
Name Stmts Miss Cover Missing
------------------------------------------------------------------
src/dashboards/__init__.py 3 0 100%
src/dashboards/__main__.py 14 1 93% 32
src/dashboards/alerts.py 176 0 100%
src/dashboards/api.py 436 0 100%
src/dashboards/app.py 185 0 100%
src/dashboards/auth.py 265 0 100%
src/dashboards/certs.py 292 0 100%
src/dashboards/collectors.py 205 5 98% 284-285, 289-290, 307
src/dashboards/config.py 120 0 100%
src/dashboards/debug.py 14 0 100%
src/dashboards/poller.py 165 3 98% 192-194, 279
src/dashboards/prom.py 112 5 96% 126, 163, 177, 180, 183
src/dashboards/rates.py 73 0 100%
src/dashboards/severity.py 49 0 100%
src/dashboards/sources/__init__.py 0 0 100%
src/dashboards/sources/disks.py 134 0 100%
src/dashboards/sources/host.py 209 0 100%
src/dashboards/sources/llm.py 178 1 99% 285
src/dashboards/sources/nut.py 413 20 95% 153, 172, 186, 198, 204, 217, 225, 241, 319-320, 322, 579, 591, 626, 657, 661, 718-719, 724, 736
src/dashboards/sources/nut_sim.py 134 0 100%
src/dashboards/store.py 313 2 99% 553-554
------------------------------------------------------------------
TOTAL 3490 37 99%
Required test coverage of 94% reached. Total coverage: 98.94%
1456 passed, 15 skipped, 1 warning in 164.27s (0:02:44)
== uv run ruff check src tests agent ==
All checks passed!
== uv run pyright ==
0 errors, 0 warnings, 0 informations
== node --check src/dashboards/web/static/*.js ==
advisory: dependencies are out of date (AGENTS.md rule 8 — update on purpose):
fastapi 0.153.0 0.156.2 wheel
@@ -0,0 +1,16 @@
Everything is green. Report:
**Implemented**
- `src/dashboards/web/static/dash.css` — new `details.events-box` family beside `details.charts-box`: `min-width:0`, `> summary` (cursor, `list-style:none`, webkit marker hidden, `::before` `▸` / `[open] ::before` `▾` in `--text-faint`, hover, open-state de-emphasis), all tokens declared in `:root`.
- Shape deviation (deliberate, documented in the block's comment): this `<summary>` *replaces* the card's `header`, so it takes `.card > header`'s exact rhythm — `padding:.6rem .8rem`, the h3's `12.5px/650` uppercase type, `--text-muted`, `var(--border)` bottom rule, `color-mix` header ground — and the caret is a flex item (as in `charts-box`), not the snippet's `position:absolute` (which sits off the text's centre line). No `margin-bottom` when open: `.body` supplies the gap.
- **Defect fixed from task 01**: `.card > .body` is a *child* selector, so once the body moved inside `<details>` it lost its padding. Added `details.events-box > .body { padding: .8rem; }` (restatement, measured equal to another card's).
- No new rules for `ul.events` / `.events-pager` — verified they resolve inside the disclosure (320px bounded scroll, `overscroll-behavior:contain`, flex pager with its 1px top rule, `1–50 of 200`, both arrows).
- `AGENTS.md` UI conventions: the disclosure's styling sentence + the `.card > .body` child-selector gotcha.
**Tests / lint** (`bash .agents/validate.sh`, rc=0): `uv run pytest --cov --cov-fail-under=94` → **1456 passed, 15 skipped, coverage 98.94%**; `ruff check src tests agent` → All checks passed; `pyright` → 0 errors; `node --check` all JS clean. `uv run pytest -m live --live` → **11 passed**. Browser proof: `data/logs/19-task03-css.cjs` → **24/24 PASS** (`19-task03-css.out`), screenshots `19-task03-{closed,open}.png` + `19-task03-dark-{closed,open}.png` against the live dev server (both palettes; other cards and battery charts unchanged).
**Docs**: AGENTS.md updated; README no change needed (task 02's paragraph already covers the user-visible behaviour).
**Notes**: the served-stylesheet pin is deferred to task 04 by the task file, so I did not pre-empt it. Advisory only: `fastapi 0.153.0 → 0.156.3` available; dependencies untouched by this task, so not pulled.
**Next pending task**: `.agents/phases/todo/19_ups_events_expandable/04_tests_and_verification.md`.
@@ -0,0 +1,65 @@
== uv run pytest --cov (hermetic: no network, live tests skip) ==
........................................................................ [ 4%]
........................................................................ [ 9%]
........................................................................ [ 14%]
........................................................................ [ 19%]
........................................................................ [ 24%]
................................s....................................... [ 29%]
........................................................................ [ 34%]
........................................................................ [ 39%]
........................................................................ [ 44%]
........................................................................ [ 48%]
........................................................................ [ 53%]
........................................................................ [ 58%]
........................................................................ [ 63%]
.....................................................................sss [ 68%]
ssssssss................................................................ [ 73%]
........................................................................ [ 78%]
........................................................................ [ 83%]
........................................................................ [ 88%]
........................................................................ [ 92%]
.............................................sss........................ [ 97%]
............................... [100%]
=============================== warnings summary ===============================
.venv/lib64/python3.14/site-packages/fastapi/testclient.py:1
/var/home/ducoterra/Projects/Personal/dashboards/.venv/lib64/python3.14/site-packages/fastapi/testclient.py:1: StarletteDeprecationWarning: Using `httpx` with `starlette.testclient` is deprecated; install `httpx2` instead.
from starlette.testclient import TestClient as TestClient # noqa
-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
================================ tests coverage ================================
_______________ coverage: platform linux, python 3.14.4-final-0 ________________
Name Stmts Miss Cover Missing
------------------------------------------------------------------
src/dashboards/__init__.py 3 0 100%
src/dashboards/__main__.py 14 1 93% 32
src/dashboards/alerts.py 176 0 100%
src/dashboards/api.py 436 0 100%
src/dashboards/app.py 185 0 100%
src/dashboards/auth.py 265 0 100%
src/dashboards/certs.py 292 0 100%
src/dashboards/collectors.py 205 5 98% 284-285, 289-290, 307
src/dashboards/config.py 120 0 100%
src/dashboards/debug.py 14 0 100%
src/dashboards/poller.py 165 3 98% 192-194, 279
src/dashboards/prom.py 112 5 96% 126, 163, 177, 180, 183
src/dashboards/rates.py 73 0 100%
src/dashboards/severity.py 49 0 100%
src/dashboards/sources/__init__.py 0 0 100%
src/dashboards/sources/disks.py 134 0 100%
src/dashboards/sources/host.py 209 0 100%
src/dashboards/sources/llm.py 178 1 99% 285
src/dashboards/sources/nut.py 413 20 95% 153, 172, 186, 198, 204, 217, 225, 241, 319-320, 322, 579, 591, 626, 657, 661, 718-719, 724, 736
src/dashboards/sources/nut_sim.py 134 0 100%
src/dashboards/store.py 313 2 99% 553-554
------------------------------------------------------------------
TOTAL 3490 37 99%
Required test coverage of 94% reached. Total coverage: 98.94%
1456 passed, 15 skipped, 1 warning in 158.48s (0:02:38)
== uv run ruff check src tests agent ==
All checks passed!
== uv run pyright ==
0 errors, 0 warnings, 0 informations
== node --check src/dashboards/web/static/*.js ==
advisory: dependencies are out of date (AGENTS.md rule 8 — update on purpose):
fastapi 0.153.0 0.156.3 wheel
@@ -0,0 +1,19 @@
Everything is green. Final state verified:
**Implemented (task 04 — pins + browser verification)**
- `tests/test_api.py::test_the_ups_event_rows_keep_the_date_time_and_severity_shape` — served-script pin of the row template: `<time>` leading with `month/day + 24h` `toLocaleString` options, `sev ${event.severity}` chip carrying the escaped host, message via `escapeHtml(eventText(event))`, conditional `runtime_seconds` footnote, pager + `label: 'Power events'` + empty sentence.
- `tests/test_api.py::test_the_ups_events_disclosure_is_a_quiet_disclosure_not_a_new_component` — the served-stylesheet pin task 03 deferred: `▸`/`▾` carets, hidden webkit marker, the header ground/bottom rule (this `<summary>` replaces the card header), open-state de-emphasis with no `margin-bottom`, the `details.events-box > .body` padding restatement (`.card > .body` is a child selector), no verdict/accent token, and `ul.events`/`.events-pager` each styled exactly once.
- Both pins mutation-checked: dropping `<time>`, the severity class, `hour12: false`, raw-message printing, the caret, the `.body` padding, an accent token or a duplicated `ul.events {` each fails exactly one test; sources restored byte-identical.
- Browser proof against the running dev server with **real upsd** (2 UPSes, no simulator): `data/logs/19-task04-verify.cjs` → **28/28 PASS**; `data/logs/ups-events-closed.png` (collapsed, one 39px `Power events` summary, **0** `/api/events` requests across 2×5s polls while 4 snapshot polls ran) and `data/logs/ups-events-open.png` (bounded 320px list, 50 rows with `Oct 20, 07:11` timestamps + severity chips, `1–50 of 200` strip); counts recorded in `data/logs/ups-events-verify.out`. Also proven: 1 request on first expand, loop refreshes while open, close asks for nothing, fresh re-open makes no request (identical rows/pager), stale cache re-fetches, failed fetch keeps rows and doesn't stamp the cache.
- Docs: AGENTS.md UI conventions already carried the disclosure/lazy/stale sentence (tasks 01–03); corrected gap 12 (the two ex-NOTES items are now phases 20 and 24) and the `uv run pytest` tally 1454→1458. README's `/ups` "power events" row plus task 02's expandable paragraph stay accurate — no further README change.
**Tests / lint / coverage**
- `bash .agents/validate.sh` → **exit 0**: `pytest --cov --cov-fail-under=94` = **1458 passed, 15 skipped**, **98.94%** coverage (floor 94, unchanged); `ruff check src tests agent` clean; `pyright` 0 errors; `node --check` all JS clean; repo-hygiene gates clean. Advisory only: `fastapi 0.153.0 → 0.156.3` (no dependency moved this task).
- `uv run pytest -m live --live` → **11 passed** (read-only, real fleet/engines/upsd). Dev server still `status: ok`, nine sources ok.
**Decisions / deviations**
- Task-04's first two named pins already exist from tasks 01/02 (`test_ups_power_events_feed_sits_behind_a_collapsed_disclosure`, `test_ups_events_are_fetched_only_while_the_disclosure_is_open`) with the exact coverage the task describes; I left them untouched (criterion 2 asks for the existing feed pins unmodified) and added only the genuinely missing pins.
- "List renders nothing while shut" is measured with `checkVisibility({contentVisibilityAuto:true})`, not a rect: a closed `<details>` hides via `content-visibility`, which keeps the child's box measurable.
- No live-infrastructure changes; repo-side plus the local dev server only.
**Next pending task:** `.agents/phases/todo/20_masthead_nav_polish/01_two_row_masthead.md` (phase 19 has no remaining task files — this was its last).
@@ -0,0 +1,65 @@
== uv run pytest --cov (hermetic: no network, live tests skip) ==
........................................................................ [ 4%]
........................................................................ [ 9%]
........................................................................ [ 14%]
........................................................................ [ 19%]
........................................................................ [ 24%]
..................................s..................................... [ 29%]
........................................................................ [ 34%]
........................................................................ [ 39%]
........................................................................ [ 43%]
........................................................................ [ 48%]
........................................................................ [ 53%]
........................................................................ [ 58%]
........................................................................ [ 63%]
.......................................................................s [ 68%]
ssssssssss.............................................................. [ 73%]
........................................................................ [ 78%]
........................................................................ [ 83%]
........................................................................ [ 87%]
........................................................................ [ 92%]
...............................................sss...................... [ 97%]
................................. [100%]
=============================== warnings summary ===============================
.venv/lib64/python3.14/site-packages/fastapi/testclient.py:1
/var/home/ducoterra/Projects/Personal/dashboards/.venv/lib64/python3.14/site-packages/fastapi/testclient.py:1: StarletteDeprecationWarning: Using `httpx` with `starlette.testclient` is deprecated; install `httpx2` instead.
from starlette.testclient import TestClient as TestClient # noqa
-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
================================ tests coverage ================================
_______________ coverage: platform linux, python 3.14.4-final-0 ________________
Name Stmts Miss Cover Missing
------------------------------------------------------------------
src/dashboards/__init__.py 3 0 100%
src/dashboards/__main__.py 14 1 93% 32
src/dashboards/alerts.py 176 0 100%
src/dashboards/api.py 436 0 100%
src/dashboards/app.py 185 0 100%
src/dashboards/auth.py 265 0 100%
src/dashboards/certs.py 292 0 100%
src/dashboards/collectors.py 205 5 98% 284-285, 289-290, 307
src/dashboards/config.py 120 0 100%
src/dashboards/debug.py 14 0 100%
src/dashboards/poller.py 165 3 98% 192-194, 279
src/dashboards/prom.py 112 5 96% 126, 163, 177, 180, 183
src/dashboards/rates.py 73 0 100%
src/dashboards/severity.py 49 0 100%
src/dashboards/sources/__init__.py 0 0 100%
src/dashboards/sources/disks.py 134 0 100%
src/dashboards/sources/host.py 209 0 100%
src/dashboards/sources/llm.py 178 1 99% 285
src/dashboards/sources/nut.py 413 20 95% 153, 172, 186, 198, 204, 217, 225, 241, 319-320, 322, 579, 591, 626, 657, 661, 718-719, 724, 736
src/dashboards/sources/nut_sim.py 134 0 100%
src/dashboards/store.py 313 2 99% 553-554
------------------------------------------------------------------
TOTAL 3490 37 99%
Required test coverage of 94% reached. Total coverage: 98.94%
1458 passed, 15 skipped, 1 warning in 153.15s (0:02:33)
== uv run ruff check src tests agent ==
All checks passed!
== uv run pyright ==
0 errors, 0 warnings, 0 informations
== node --check src/dashboards/web/static/*.js ==
advisory: dependencies are out of date (AGENTS.md rule 8 — update on purpose):
fastapi 0.153.0 0.156.3 wheel