diff --git a/.agents/phases/todo/25_masthead_control_placement/00_phase.md b/.agents/phases/complete/25_masthead_control_placement/00_phase.md similarity index 100% rename from .agents/phases/todo/25_masthead_control_placement/00_phase.md rename to .agents/phases/complete/25_masthead_control_placement/00_phase.md diff --git a/.agents/phases/todo/25_masthead_control_placement/01_status_dots_on_admin_pages.md b/.agents/phases/complete/25_masthead_control_placement/01_status_dots_on_admin_pages.md similarity index 100% rename from .agents/phases/todo/25_masthead_control_placement/01_status_dots_on_admin_pages.md rename to .agents/phases/complete/25_masthead_control_placement/01_status_dots_on_admin_pages.md diff --git a/.agents/phases/todo/25_masthead_control_placement/02_right_justify_masthead_rows.md b/.agents/phases/complete/25_masthead_control_placement/02_right_justify_masthead_rows.md similarity index 100% rename from .agents/phases/todo/25_masthead_control_placement/02_right_justify_masthead_rows.md rename to .agents/phases/complete/25_masthead_control_placement/02_right_justify_masthead_rows.md diff --git a/.agents/phases/todo/25_masthead_control_placement/03_clock_rightmost.md b/.agents/phases/complete/25_masthead_control_placement/03_clock_rightmost.md similarity index 100% rename from .agents/phases/todo/25_masthead_control_placement/03_clock_rightmost.md rename to .agents/phases/complete/25_masthead_control_placement/03_clock_rightmost.md diff --git a/.agents/reports/25_masthead_control_placement/25_masthead_control_placement__00_phase.a1.err b/.agents/reports/25_masthead_control_placement/25_masthead_control_placement__00_phase.a1.err new file mode 100644 index 0000000..e69de29 diff --git a/.agents/reports/25_masthead_control_placement/25_masthead_control_placement__00_phase.a1.md b/.agents/reports/25_masthead_control_placement/25_masthead_control_placement__00_phase.a1.md new file mode 100644 index 0000000..a9e7a12 --- /dev/null +++ b/.agents/reports/25_masthead_control_placement/25_masthead_control_placement__00_phase.a1.md @@ -0,0 +1,25 @@ +**Phase 25 final verification pass** (all 3 tasks were already in `complete/`, so this was the phase-level verify + docs pass) + +**Verified** +- `#nav-status` first child of `.masthead-right` on all 7 signed-in templates (incl. `/certs`, `/settings`); `app.js`/`charts.js` untouched +- `#nav-status` + `.masthead-right` both `justify-content: flex-end`, `margin-inline-start` still absent, row-break (`flex-wrap` + two `flex-basis: 100%`), non-sticky bar, dropdown slide + 4 close paths, `setInterval(refreshStatus, 15000)` all intact (dash.css diff = comments + the two alignment values) +- Clock is the last control on all 7 templates; no `order:` anywhere in the bar + +**Fixed (defects found in this pass)** +- `tests/test_api.py::test_every_page_links_to_both_admin_pages` — duplicated `tabs = html[...]` statement (task 03 edit artifact) +- `certs.html` / `settings.html` comments said "all **eight** signed-in pages"; the signed-in set is seven (`/login` is the eighth, anonymous, and has no links/clock) +- `AGENTS.md` — suite tally `1542 → 1543 passed`; "the three phase-20 tests" re-worded, since task 02 renamed one to `test_the_masthead_is_three_rows_right_justified_at_every_width` + +**Results** +- `uv run pytest` → **1543 passed, 15 skipped**, 0 failed +- `bash .agents/validate.sh` → **exit 0**: coverage **98.94 %** (floor 94 %), `ruff` All checks passed, `pyright` 0 errors, `node --check` clean, hygiene clean +- `uv run pytest -m live --live` (`.env.dev` sourced) → **11 passed**; `./scripts/dev-server.sh status` → 9/9 sources `ok` +- Browser: `node data/logs/25-task03-verify.cjs` → **235 checks, 0 FAIL**, exit 0 (`data/logs/25-task03-verify.out`, 16 `masthead-25-*.png`) + +**Completion criteria**: badges pinned+measured (green `system/disks/llm/ups` on both admin pages, 1280 px and 360 px) ✅ · both rows flush right, three rows + dropdown unchanged (pinned + measured) ✅ · clock farthest-right (pinned markup; measured right edge 1260.0 at 1280 px, ≤900 px hidden by the long-standing readout rule with source order asserted) ✅ · pytest/coverage/ruff/pyright ✅ · validate.sh ✅ · no behaviour change in completed work ✅ + +**Docs**: README reviewed end-to-end against the code (served-route audit vs documented routes, env table vs `config.py`, commands vs `scripts/`, public-path table, `web/` masthead bullet) — consistent, no changes needed beyond the phase's own edits. AGENTS.md — two fixes above; spot-checked every doc-named test, function, script, threshold, `install-agent.sh` sha256 (`ffbcd91f…` matches) and agent version (1.1.0). + +**Deviations**: left the pre-existing `fastapi 0.153.0 → 0.159.0` advisory (validate prints it as advisory; no dependency work in scope, AGENTS rule 8 triggers on touching deps). No live-infra changes. Overview's "all eight signed-in pages" for the clock is verified against seven templates + `/login` (which has no clock, pinned). + +**Next pending phase**: `.agents/phases/todo/26_ups_event_split/` (3 tasks). diff --git a/.agents/reports/25_masthead_control_placement/25_masthead_control_placement__00_phase.a1.md.status b/.agents/reports/25_masthead_control_placement/25_masthead_control_placement__00_phase.a1.md.status new file mode 100644 index 0000000..9766475 --- /dev/null +++ b/.agents/reports/25_masthead_control_placement/25_masthead_control_placement__00_phase.a1.md.status @@ -0,0 +1 @@ +ok diff --git a/.agents/reports/25_masthead_control_placement/25_masthead_control_placement__00_phase.a1.validate b/.agents/reports/25_masthead_control_placement/25_masthead_control_placement__00_phase.a1.validate new file mode 100644 index 0000000..523270a --- /dev/null +++ b/.agents/reports/25_masthead_control_placement/25_masthead_control_placement__00_phase.a1.validate @@ -0,0 +1,66 @@ +== uv run pytest --cov (hermetic: no network, live tests skip) == +........................................................................ [ 4%] +........................................................................ [ 9%] +........................................................................ [ 13%] +........................................................................ [ 18%] +........................................................................ [ 23%] +..........................................................s............. [ 27%] +........................................................................ [ 32%] +........................................................................ [ 36%] +........................................................................ [ 41%] +........................................................................ [ 46%] +........................................................................ [ 50%] +........................................................................ [ 55%] +........................................................................ [ 60%] +........................................................................ [ 64%] +........................................................................ [ 69%] +.sssssssssss............................................................ [ 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 440 0 100% +src/dashboards/app.py 186 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 3495 37 99% +Required test coverage of 94% reached. Total coverage: 98.94% +1543 passed, 15 skipped, 1 warning in 163.67s (0:02:43) +== 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.159.0 wheel diff --git a/.agents/reports/25_masthead_control_placement/25_masthead_control_placement__01_status_dots_on_admin_pages.a1.err b/.agents/reports/25_masthead_control_placement/25_masthead_control_placement__01_status_dots_on_admin_pages.a1.err new file mode 100644 index 0000000..e69de29 diff --git a/.agents/reports/25_masthead_control_placement/25_masthead_control_placement__01_status_dots_on_admin_pages.a1.md b/.agents/reports/25_masthead_control_placement/25_masthead_control_placement__01_status_dots_on_admin_pages.a1.md new file mode 100644 index 0000000..a521363 --- /dev/null +++ b/.agents/reports/25_masthead_control_placement/25_masthead_control_placement__01_status_dots_on_admin_pages.a1.md @@ -0,0 +1,25 @@ +All green. Report: + +**Implemented (phase 25 task 01)** +- `src/dashboards/web/pages/certs.html` + `settings.html`: added `
` as the first child of `.masthead-right` (same position as the five dashboards); deleted settings.html's "No `#nav-status`…" comment whose premise the operator overruled. +- `app.js`: **unchanged** — `initChrome()`'s `if (nav)` block builds the four badges from `/api/health` whenever the container exists. +- `tests/test_api.py`: flipped the two no-dots pins (`/settings`, `/certs`) to dots-present + re-worded docstrings/messages; added `test_every_signed_in_page_carries_the_fleet_badges` (one `MARKUP_PAGES` loop pinning the exact element as first child of `.masthead-right`, plus the `app.js` container guard, the four badge labels and the untouched 15 s refresh). +- Browser proof: `data/logs/25-task01-badges.cjs` (headless, `20-task01-rows.cjs` convention) → **ALL PASS (90 checks)**, transcript `data/logs/25-task01-badges.out`, screenshots `data/logs/25-task01-{overview,settings,certs}-badges.png` — badges populate `system=ok disks=ok llm=ok ups=ok` on `/certs` and `/settings`, identical to `/`, at 1280 px and 360 px; `/login` (unsigned) still has none. +- Dev server restarted; `./scripts/dev-server.sh status` = 9 sources `ok`. + +**Tests / lint / coverage** +- `uv run pytest` → `1543 passed, 15 skipped` (was 1542+15; +1 new test), exit 0 +- `uv run pytest --cov` → `TOTAL 3495 37 99%`, "Required test coverage of 94% reached. Total coverage: 98.94%" (no Python source lines added) +- `uv run ruff check src tests agent` → All checks passed · `uv run pyright` → 0 errors +- `bash .agents/validate.sh` → exit 0 (pytest+cov, ruff, pyright, `node --check`); only advisory: `fastapi 0.153.0 → 0.158.1` outdated — dependencies untouched by this task, so not bumped + +**Docs** +- `AGENTS.md`: `/settings` "no status dots" → the four badges (with the TODO/`initChrome` reason); `/certs` "no `#nav-status`" → badges like every masthead; the three-rows paragraph now says every masthead page renders three rows and `/login` remains the only two-row page. +- `README.md`: status dots are on **every** signed-in page; the "a page with no indicators — `/login`, `/settings`, `/certs`" clause narrowed to `/login` only. Left-alignment wording deliberately left for task 02. + +**Notes / deviations** +- Kept task 01 strictly placement-of-container: no CSS, no `app.js`, no order change (tasks 02/03 own right-justification and clock-rightmost). New pin asserts "first child", which survives task 03's clock move. +- Pre-existing working-tree change I did not make and left alone: `NOTES.md` is emptied (the two items consumed by phases 20/24). +- The interactive-browser hook fired but no visible session was needed — this phase's mandate is the repo's own headless `data/logs/*.cjs` verification, which ran. + +**Next pending task:** `25_masthead_control_placement/02_right_justify_masthead_rows.md` diff --git a/.agents/reports/25_masthead_control_placement/25_masthead_control_placement__01_status_dots_on_admin_pages.a1.md.status b/.agents/reports/25_masthead_control_placement/25_masthead_control_placement__01_status_dots_on_admin_pages.a1.md.status new file mode 100644 index 0000000..9766475 --- /dev/null +++ b/.agents/reports/25_masthead_control_placement/25_masthead_control_placement__01_status_dots_on_admin_pages.a1.md.status @@ -0,0 +1 @@ +ok diff --git a/.agents/reports/25_masthead_control_placement/25_masthead_control_placement__01_status_dots_on_admin_pages.a1.validate b/.agents/reports/25_masthead_control_placement/25_masthead_control_placement__01_status_dots_on_admin_pages.a1.validate new file mode 100644 index 0000000..684f4a5 --- /dev/null +++ b/.agents/reports/25_masthead_control_placement/25_masthead_control_placement__01_status_dots_on_admin_pages.a1.validate @@ -0,0 +1,66 @@ +== uv run pytest --cov (hermetic: no network, live tests skip) == +........................................................................ [ 4%] +........................................................................ [ 9%] +........................................................................ [ 13%] +........................................................................ [ 18%] +........................................................................ [ 23%] +..........................................................s............. [ 27%] +........................................................................ [ 32%] +........................................................................ [ 36%] +........................................................................ [ 41%] +........................................................................ [ 46%] +........................................................................ [ 50%] +........................................................................ [ 55%] +........................................................................ [ 60%] +........................................................................ [ 64%] +........................................................................ [ 69%] +.sssssssssss............................................................ [ 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 440 0 100% +src/dashboards/app.py 186 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 3495 37 99% +Required test coverage of 94% reached. Total coverage: 98.94% +1543 passed, 15 skipped, 1 warning in 161.44s (0:02:41) +== 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.158.1 wheel diff --git a/.agents/reports/25_masthead_control_placement/25_masthead_control_placement__02_right_justify_masthead_rows.a1.err b/.agents/reports/25_masthead_control_placement/25_masthead_control_placement__02_right_justify_masthead_rows.a1.err new file mode 100644 index 0000000..e69de29 diff --git a/.agents/reports/25_masthead_control_placement/25_masthead_control_placement__02_right_justify_masthead_rows.a1.md b/.agents/reports/25_masthead_control_placement/25_masthead_control_placement__02_right_justify_masthead_rows.a1.md new file mode 100644 index 0000000..6493504 --- /dev/null +++ b/.agents/reports/25_masthead_control_placement/25_masthead_control_placement__02_right_justify_masthead_rows.a1.md @@ -0,0 +1,15 @@ +**Implemented (phase 25 task 02)** +- `src/dashboards/web/static/dash.css`: `#nav-status` and `.masthead-right` `justify-content: flex-start` → `flex-end`; `margin-inline-start` still absent from the cluster; row-break (`flex-wrap` + both `flex-basis: 100%`), `min-height`, gaps and the whole ≤640px block (incl. `#nav-toggle { margin-inline-start: auto }`) untouched. +- Re-worded every comment that stated the old alignment: the header block, `Row 3:`, `Row 2:`, the mobile block intro, and the mobile "row alignment is the base rule's …" note. +- `tests/test_api.py`: renamed the pin to `test_the_masthead_is_three_rows_right_justified_at_every_width`, flipped only the two alignment assertions to `flex-end`, re-worded docstring/messages; all row-break/height/gap/no-`margin-inline-start` guards kept. The mobile-height and unsticky pins are unchanged and green. +- Measured half: `data/logs/25-task02-justify.cjs` (7 signed-in pages × 1280/360 px + unsigned `/login`) — both rows compute `flex-end` and sit flush with the bar's right padding edge (0.00 px gap), 4 badges per row, still three rows, dropdown still under the whole bar, no sideways scroll: **ALL CHECKS PASSED**; screenshots `data/logs/25-task02-{index,certs,settings}-{1280,360}.png`, `25-task02-login-1280.png`, transcript `25-task02-justify.out`. + +**Tests / lint / coverage** +- `bash .agents/validate.sh` → EXIT=0: `1543 passed, 15 skipped`, **coverage 98.94 %** (floor 94 %), `ruff check src tests agent` All checks passed, `pyright` 0 errors, `node --check web/static/*.js` clean. +- `uv run pytest -m live --live` (with `.env.dev`) → **11 passed**; `./scripts/dev-server.sh status` → 9/9 sources `ok`, served `/static/dash.css` carries the two `flex-end` declarations. + +**Docs**: `AGENTS.md` masthead paragraph now reads "three rows … the two control rows flush right", gained a phase-25-task-02 paragraph naming the renamed pin + the browser script, and gap 12's "three left-justified rows" wording fixed; `README.md`'s masthead sentence likewise. Clock-order sentences left for task 03. + +**Notes/deviations**: no scope change (clock move not started). Pre-existing, untouched: `validate.sh` advisory `fastapi 0.153.0 → 0.158.1` (no dependency work in this task) and `NOTES.md` already emptied by earlier tasks in the working tree. + +**Next pending task**: `.agents/phases/todo/25_masthead_control_placement/03_clock_rightmost.md`. diff --git a/.agents/reports/25_masthead_control_placement/25_masthead_control_placement__02_right_justify_masthead_rows.a1.md.status b/.agents/reports/25_masthead_control_placement/25_masthead_control_placement__02_right_justify_masthead_rows.a1.md.status new file mode 100644 index 0000000..9766475 --- /dev/null +++ b/.agents/reports/25_masthead_control_placement/25_masthead_control_placement__02_right_justify_masthead_rows.a1.md.status @@ -0,0 +1 @@ +ok diff --git a/.agents/reports/25_masthead_control_placement/25_masthead_control_placement__02_right_justify_masthead_rows.a1.validate b/.agents/reports/25_masthead_control_placement/25_masthead_control_placement__02_right_justify_masthead_rows.a1.validate new file mode 100644 index 0000000..bd01086 --- /dev/null +++ b/.agents/reports/25_masthead_control_placement/25_masthead_control_placement__02_right_justify_masthead_rows.a1.validate @@ -0,0 +1,66 @@ +== uv run pytest --cov (hermetic: no network, live tests skip) == +........................................................................ [ 4%] +........................................................................ [ 9%] +........................................................................ [ 13%] +........................................................................ [ 18%] +........................................................................ [ 23%] +..........................................................s............. [ 27%] +........................................................................ [ 32%] +........................................................................ [ 36%] +........................................................................ [ 41%] +........................................................................ [ 46%] +........................................................................ [ 50%] +........................................................................ [ 55%] +........................................................................ [ 60%] +........................................................................ [ 64%] +........................................................................ [ 69%] +.sssssssssss............................................................ [ 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 440 0 100% +src/dashboards/app.py 186 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 3495 37 99% +Required test coverage of 94% reached. Total coverage: 98.94% +1543 passed, 15 skipped, 1 warning in 162.55s (0:02:42) +== 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.158.1 wheel diff --git a/.agents/reports/25_masthead_control_placement/25_masthead_control_placement__03_clock_rightmost.a1.err b/.agents/reports/25_masthead_control_placement/25_masthead_control_placement__03_clock_rightmost.a1.err new file mode 100644 index 0000000..e69de29 diff --git a/.agents/reports/25_masthead_control_placement/25_masthead_control_placement__03_clock_rightmost.a1.md b/.agents/reports/25_masthead_control_placement/25_masthead_control_placement__03_clock_rightmost.a1.md new file mode 100644 index 0000000..cd45e13 --- /dev/null +++ b/.agents/reports/25_masthead_control_placement/25_masthead_control_placement__03_clock_rightmost.a1.md @@ -0,0 +1,16 @@ +**Implemented (phase 25 task 03)** +- All 7 templates (`index/system/disks/llm/ups/settings/certs.html`): `` moved from first control to **last** child of `.masthead-right`, so the row reads `⚷ Certs` → `⚙ Account` → `☾ Dark` → clock; the seven control-row blocks are byte-identical (md5-verified), `#nav-status` stays first child. +- `dash.css`: header block + `Row 3:` comment re-worded to the new order and to say the clock's place is markup, not a CSS `order:`. No declaration changed. +- `tests/test_api.py::test_every_page_links_to_both_admin_pages`: docstring order sentence updated, both `before_theme` assertions kept, added `html.index('id="clock"') > html.index('id="theme-toggle"')` per page, plus a guard that `.masthead`/`.masthead-right`/`#nav-status`/`.clock` carry **no** `order:` declaration (the task's ASSUMPTION, made enforceable). `/login` half untouched. +- `data/logs/25-task03-verify.cjs` + `.out` + 16 screenshots `data/logs/masthead-25-*.png`: 7 pages × 1280/360 px + `/login` unsigned at both — three rows, both right-hand rows flush with the bar's right padding edge (measured on the chips' content, not the full-basis row box, and explicitly *not* flush left), clock the rightmost painted thing on the bar, cluster source order, four populated badges on `/certs`/`/settings`, 360 px dropdown still under the whole bar, no sideways scroll. + +**Tests / lint / coverage** +- `bash .agents/validate.sh` → **1543 passed, 15 skipped**, coverage **98.94 %** (floor 94 %), `ruff check src tests agent` All checks passed, `pyright` 0 errors, `node --check web/static/*.js` clean, hygiene clean (only the pre-existing advisory `fastapi 0.153.0 → 0.158.2`; no dependency work in this task). +- `uv run pytest -m live --live` (`.env.dev` sourced) → **11 passed** (first attempt had one transient `deskwork … /snapshot timed out`, clean on re-run); `./scripts/dev-server.sh status` → 9/9 sources `ok`. +- Browser pass → **ALL CHECKS PASSED** (235 checks, 0 failures). + +**Docs**: `AGENTS.md` — control-order sentence in the `/certs` paragraph, the row-3 description, a new phase-25-task-03 paragraph (markup-vs-`order:` rationale, the pin, the ≤900 px clock note, the script/screenshots); `README.md` — the `web/` masthead bullet now reads `⚷ Certs` / `⚙ Account` / `☾ Dark` / clock with "the time is the farthest-right thing on the bar". + +**Decisions/deviations**: the long-standing `@media (max-width: 900px){.clock{display:none}}` stays (task said no declaration changes; it's a readout, not a control), so the clock's pixel claim is measured at 1280 px and the 360 px leg asserts the clock is hidden *by that rule* and the source order still ends with it — if the operator wants the time on the phone too, that hide is the one line to drop. No live-infra changes; `TODO.md`/`NOTES.md` working-tree edits are pre-existing, not mine. + +**Next pending task**: `.agents/phases/todo/26_ups_event_split/01_page_side_event_split.md` (this task file is the last of phase 25). diff --git a/.agents/reports/25_masthead_control_placement/25_masthead_control_placement__03_clock_rightmost.a1.md.status b/.agents/reports/25_masthead_control_placement/25_masthead_control_placement__03_clock_rightmost.a1.md.status new file mode 100644 index 0000000..9766475 --- /dev/null +++ b/.agents/reports/25_masthead_control_placement/25_masthead_control_placement__03_clock_rightmost.a1.md.status @@ -0,0 +1 @@ +ok diff --git a/.agents/reports/25_masthead_control_placement/25_masthead_control_placement__03_clock_rightmost.a1.validate b/.agents/reports/25_masthead_control_placement/25_masthead_control_placement__03_clock_rightmost.a1.validate new file mode 100644 index 0000000..8025c53 --- /dev/null +++ b/.agents/reports/25_masthead_control_placement/25_masthead_control_placement__03_clock_rightmost.a1.validate @@ -0,0 +1,66 @@ +== uv run pytest --cov (hermetic: no network, live tests skip) == +........................................................................ [ 4%] +........................................................................ [ 9%] +........................................................................ [ 13%] +........................................................................ [ 18%] +........................................................................ [ 23%] +..........................................................s............. [ 27%] +........................................................................ [ 32%] +........................................................................ [ 36%] +........................................................................ [ 41%] +........................................................................ [ 46%] +........................................................................ [ 50%] +........................................................................ [ 55%] +........................................................................ [ 60%] +........................................................................ [ 64%] +........................................................................ [ 69%] +.sssssssssss............................................................ [ 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 440 0 100% +src/dashboards/app.py 186 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 3495 37 99% +Required test coverage of 94% reached. Total coverage: 98.94% +1543 passed, 15 skipped, 1 warning in 163.74s (0:02:43) +== 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.159.0 wheel diff --git a/AGENTS.md b/AGENTS.md index 1c4b471..6efbd4e 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -750,7 +750,10 @@ Responsibilities, one line each: unsigned because a signed-in `GET /login` is a `303` and would audit `/`'s markup under the wrong name. `/settings` (`pages/settings.html` + `static/page-settings.js`) is a **page and not a sixth dashboard**: same `` and masthead, the five tabs with **no** `aria-current` (it is - not one of them), no status dots, and it is reached from the `⚙ Account` link in + not one of them), the same four `#nav-status` badges as every dashboard (phase 25 task 01 — + `TODO.md:3`'s "hosts disks engines UPS disappear when in the certs/account page" was the + missing container, and `Dash.initChrome()` fills it from `/api/health` with no JS change), and + it is reached from the `⚙ Account` link in `.masthead-right` of every dashboard **including itself** — outside `.tabs`, so the pinned tab structure is untouched (`tests/test_api.py::test_every_page_links_to_both_admin_pages`). It prints `/api/auth/status` (name, expiry as a local timestamp *and* as time left), signs out through @@ -766,15 +769,17 @@ Responsibilities, one line each: `csrf` is now invalid — every later write, the UPS buttons included, 403s until a reload) and on any `401` bounce. `/certs` (`pages/certs.html` + `static/page-certs.js`) is the second of those admin pages, and it - is the same shape for the same reasons: five tabs with **no** `aria-current`, no `#nav-status`, - and both masthead links — `⚷ Certs` beside `⚙ Account` — exactly as the five dashboards render - them. **Both links are on both admin pages** (phase 20 task 04, `TODO.md:4`), which retires phase - 15's "no self-link" precedent: chrome that changes shape depending on where you already are reads + is the same shape for the same reasons: five tabs with **no** `aria-current`, the four fleet + badges in `#nav-status` like every other masthead (phase 25 task 01, same reasoning as + `/settings` above), and both masthead links — `⚷ Certs` beside `⚙ Account` — exactly as the five + dashboards render them. **Both links are on both admin pages** (phase 20 task 04, `TODO.md:4`), + which retires phase 15's "no self-link" precedent: chrome that changes shape depending on where you already are reads as a broken button, while a link to the page you are on is a no-op that answers with the page you were looking at (`data/logs/20-task04-admin-links.cjs` measures exactly that — one document response, no redirect, the same cluster afterwards). The two links therefore sit on all seven signed-in pages (the five dashboards plus `/settings` and `/certs`) in one order - (clock → `⚷ Certs` → `⚙ Account` → theme toggle), which is what lets + (`⚷ Certs` → `⚙ Account` → theme toggle → clock — phase 25 task 03 moved the clock to the end so + it is the farthest-right item on a `flex-end` row), which is what lets the pinned audit be one loop over `MARKUP_PAGES` instead of two over different tuples; `/login` is the eighth page and the one with neither. Everything on it is `GET /api/certs`: the Root CA block, the display-only Dashboard client line (that certificate is @@ -814,17 +819,21 @@ Responsibilities, one line each: `severity` verdict the API sent (`tests/test_api.py` fails if a page script names it again). A value the API should rate gets a row in `severity.py`, never a bound in JS. - **The masthead is three left-justified rows at every width** (phase 20 task 01, `TODO.md:5-6`): - row 1 the brand, `#nav-toggle` and the five tabs; row 2 the `#nav-status` per-source dots; row 3 - the `.masthead-right` controls (clock, `⚷ Certs`, `⚙ Account`, theme — phase 21 task 01 deleted - the `⏸ Pause` that used to sit between the clock and the two links, button, `Space` shortcut and + **The masthead is three rows at every width, the two control rows flush right** (phase 20 task + 01 made the rows, `TODO.md:5-6`; phase 25 task 02 re-justified them, `TODO.md:4` — "I made a + mistake earlier, I did want hosts disks engines UPS badges and certs/account/dark right + justified"): row 1 the brand, `#nav-toggle` and the five tabs; row 2 the `#nav-status` + per-source dots; row 3 + the `.masthead-right` controls (`⚷ Certs`, `⚙ Account`, theme, then the clock — phase 25 task 03 + moved the clock last for `TODO.md:5`'s "the time should show up as the farthest right item"; + phase 21 task 01 deleted the `⏸ Pause` that used to sit beside the links, button, `Space` shortcut and `setPaused`/`_paused` plumbing alike: it froze the numbers on screen without growing a `stale` label, and a backgrounded tab already idles its polls, which is the only stop this app needs). - That is CSS and + That was CSS and no markup change, which decides the mechanism: `#nav-status` is a **child** of `.masthead-right`, so there is no wrapper to promote, and the rows are line breaks — `.masthead` is `flex-wrap: wrap`, and both `#nav-status` and `.masthead-right` carry `flex-basis: 100%` with - `justify-content: flex-start` (line breaking uses a flex item's **base** size, before shrink, so + `justify-content: flex-end` (line breaking uses a flex item's **base** size, before shrink, so a 100 % item is alone on its line: the div's break starts row 2 of the bar and the dots' starts row 2 inside it, leaving the controls to wrap to row 3). `.masthead-right` lost the `margin-inline-start: auto` that pinned the cluster to the right edge of the one-row bar. @@ -832,12 +841,46 @@ Responsibilities, one line each: brand and the tabs on separate rows. `height: var(--header-h)` became **`min-height`** (three rows do not fit in 58 px and a fixed height clips the controls) and is declared **once** — the ≤640px block no longer restates `height`/`min-height`/`flex-wrap`, and that pair drifting is exactly what - would clip the bar on a phone only. A page with no dots (`/certs`, `/settings`) renders two rows, - `/login` two (brand, then the theme toggle). Pinned on the served bytes by the three phase-20 - tests in `tests/test_api.py` (the three rules' declarations, the mobile block's silence about - the bar's height, and the bar's `position`), and measured in a browser at 1280 px and 360 px by + would clip the bar on a phone only. Every page that renders the bar now renders three rows — + `/certs` and `/settings` grew the dots row in phase 25 task 01, where `#nav-status` is pinned as + the **first child** of `.masthead-right` on all seven signed-in pages (`app.js` is untouched: + the container's existence is what makes `initChrome` build the four badges, which is why the fix + was markup and not JS) — and `/login` remains the one page with no dots, rendering two rows + (brand, then the theme toggle). Pinned on the served bytes by phase 20's three tests in + `tests/test_api.py` — the row declarations (task 02 renamed that one to + `test_the_masthead_is_three_rows_right_justified_at_every_width`), the mobile block's silence + about the bar's height, and the bar's `position` — and measured in a browser at 1280 px and 360 px by `data/logs/20-task01-rows.cjs` (seven pages × two widths plus the dropdown; screenshots - `data/logs/masthead-rows*.png`). + `data/logs/masthead-rows*.png`). **Phase 25 task 02 moved the alignment of those two rows to + the other edge** (`TODO.md:4` — "I made a mistake earlier, I did want hosts disks engines UPS + badges and certs/account/dark right justified"): the two `justify-content` declarations are + `flex-end`, `margin-inline-start` is still absent from the cluster (a full-basis row aligned by + `flex-end` is the mechanism, not the old single-row right-pinning), and the ≤640px block still + restates neither the alignment nor the bar's height, so the phone's wrapped control row follows + the base rule. The pin is `tests/test_api.py::test_the_masthead_is_three_rows_right_justified_at_every_width` + (the row-break assertions are the same ones phase 20 wrote, deliberately: the re-justify must + not cost the rows), and the measured half is `data/logs/25-task02-justify.cjs` — seven pages × + two widths, each demanding both rows compute `flex-end` and sit flush with the bar's right + padding edge, the four badges present, three rows intact, the dropdown still under the whole + bar and no sideways scroll, plus `/login`'s two-row bar with no badges + (`data/logs/25-task02-justify.out`, screenshots `data/logs/25-task02-*.png`). **Phase 25 task 03 + then put the clock at the end of the control row** (`TODO.md:5` — "The time should show up as the + farthest right item on the navbar"): the `` moved from the first + control to the last one on all seven templates, so the row reads `⚷ Certs` → `⚙ Account` → theme + toggle → clock. It is a **markup** move and deliberately not a CSS `order:` declaration, because + on a `flex-end` row the last child *is* the farthest-right item and an `order:` override would be + something the served-byte pin cannot see — `test_every_page_links_to_both_admin_pages` therefore + asserts `html.index('id="clock"') > html.index('id="theme-toggle"')` on every page (the two + Certs/Account-before-theme assertions stay, and the `/login` half of the test is untouched). + The clock keeps its long-standing `@media (max-width: 900px) { .clock { display: none } }` hide: + it is a readout and not a control, so this is not the "no control is hidden to make the width + work" rule the masthead keeps. Measured by `data/logs/25-task03-verify.cjs` — seven pages × two + widths, each demanding three rows, both right-hand rows flush with the bar's right padding edge + and the chips' content (not the full-basis row box) measured to prove it is not flush left, the + clock the rightmost painted thing on the bar at 1280 px, the cluster's source order unchanged at + 360 px where the clock is hidden, and the four badges populated on `/certs` and `/settings` as on + the dashboards; plus the 360 px dropdown and `/login` unsigned at both widths + (`data/logs/25-task03-verify.out`, screenshots `data/logs/masthead-25-*.png`). **The bar is not sticky** (phase 20 task 02, `TODO.md:10` — "It shouldn't be sticky at all. We want to maximize the amount of screen space for data viewing"): `position: sticky; top: 0` became **`position: relative`** with no offset, so the whole masthead scrolls away with the page and the @@ -1186,7 +1229,7 @@ uv run python scripts/dev-certs-export.py --url http://127.0.0.1:8090 \ # GET /api/certs/