**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).
4.7 KiB
Phase 19 — Expandable UPS power-events list
Source: TODO.md L4 — "The UPS tab should have an expandable, clear list of last incidents with dates/times"
Story: n/a
Context: src/dashboards/web/pages/ups.html lines 72–76 (the Power events card: <header><h3> + a ul.events#events holding a loading… placeholder), src/dashboards/web/static/page-ups.js (refreshEvents() at line 430 — fetches /api/events?limit=200&source=ups and hands the rows to Dash.eventsPager with the date/time + severity-chip + host row shape; called unconditionally from refresh() at line 427, which the page's poll(refresh, state.pollMs) loop drives, state.pollMs from config.ups.poll_ms || 5000), src/dashboards/web/static/app.js (Dash.eventsPager — 50 rows a page, paging state in a WeakMap keyed by the <ul>, rows re-written only when their markup changed), src/dashboards/web/static/dash.css (details.charts-box at lines 714–727 — the summary marker ▸/▾, hidden webkit marker, hover; the .events bounded 320px scroll and .events-pager strip), and the pattern this phase mirrors — /system's per-host Charts disclosure (system.html line 81, page-system.js line 315: "a <details> renders nothing, so … nothing is built or drawn while the stack is shut") and its pin test_system_history_charts_are_collapsed_behind_a_toggle
Objective
The /ups power-events list becomes what the TODO asks for — expandable: the card is collapsed by default behind a <details> disclosure in the repo's own charts-box style, nothing is fetched or drawn while it is shut (the /system rule), the first expand fetches and builds, a re-expand paints from the data in hand unless it is stale, and the page's poll loop refreshes the list only while the disclosure is open. The "clear" half already ships and stays: every row carries its date/time, the severity chip, and the host, and the 50-per-page pager bounds the list. No new API surface — the existing /api/events?source=ups is the whole backend.
Dependencies
18_nav_hamburger(todo) — both phases touchups.html(the masthead, the events card); landing 18 first keeps this phase's diff confined to the events section. No shared code between them.
Tasks
01_events_disclosure.md— theups.htmlmarkup: the card body becomes a collapsed-by-default<details class="events-box">02_events_lazy_loading.md—page-ups.js: no fetch while closed, first-expand fetch, re-expand stale rule, poll-while-open03_disclosure_css.md—dash.css: thedetails.events-boxsummary styling in thecharts-boxshape, pager and bounded list inside04_tests_and_verification.md— the served-page pins (disclosure present and unopened, the lazy rule, the unchanged row shape) and the closed/open browser verification
Testing & Quality
- Unit/integration:
tests/test_api.py— the served-page pins (see task 04), alongside the untouchedDash.eventsPagerbehaviour (a re-paint with the same data keeps the reader's page — theWeakMaprule, pinned by the existing event-feed tests) - Coverage: >90% on new/modified code (the phase is markup + JS + CSS; the gate applies to the changed Python test files)
- The phase is "Complete" only when the entire project test suite passes 100% (every test in the project, not just this phase's)
Documentation & Consistency
- The final pass reviews
README.mdandAGENTS.mdin full against the project as it stands after this phase — fixing every inconsistency found, including pre-existing ones not caused by this phase. - AGENTS.md's
UI conventionssection already documents the event pager ("An event feed is a window, not a printout") and the/systemCharts disclosure; it gains the/upsdisclosure's sentence (collapsed by default, lazy-built, poll-while-open, the stale rule) in the section's voice. The README's/upstable row ("power events") stays true — the list is on the page, now behind a summary.
Completion Criteria
/upsloads with the Power events card collapsed (a summary line, no list visible, no/api/eventsrequest in the page's first poll) and the expand shows the bounded, paged list with date/time rows- closing and re-opening within one poll interval paints the cached rows without a refetch; a stale cache refetches on open; while open, the poll loop keeps the list current
uv run pytestgreen, coverage not down,ruff+pyrightcleanbash .agents/validate.shgreen (includingnode --checkof the modifiedpage-ups.js)- closed- and open-state screenshots in
data/logs/against the running dev server (invariant 10) - no behavior change in completed work: every other element on
/ups(cards, charts, dialog, pager behaviour) is unchanged