Files
dashboards/.agents/phases/complete/19_ups_events_expandable/00_phase.md
T
ducoterra 95df8b695b 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).
2026-10-20 19:17:22 -04:00

4.7 KiB
Raw Blame History

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 touch ups.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

  1. 01_events_disclosure.md — the ups.html markup: the card body becomes a collapsed-by-default <details class="events-box">
  2. 02_events_lazy_loading.md — page-ups.js: no fetch while closed, first-expand fetch, re-expand stale rule, poll-while-open
  3. 03_disclosure_css.md — dash.css: the details.events-box summary styling in the charts-box shape, pager and bounded list inside
  4. 04_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 untouched Dash.eventsPager behaviour (a re-paint with the same data keeps the reader's page — the WeakMap rule, 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.md and AGENTS.md in 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 conventions section already documents the event pager ("An event feed is a window, not a printout") and the /system Charts disclosure; it gains the /ups disclosure's sentence (collapsed by default, lazy-built, poll-while-open, the stale rule) in the section's voice. The README's /ups table row ("power events") stays true — the list is on the page, now behind a summary.

Completion Criteria

  • /ups loads with the Power events card collapsed (a summary line, no list visible, no /api/events request 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 pytest green, coverage not down, ruff + pyright clean
  • bash .agents/validate.sh green (including node --check of the modified page-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