phase: 31_gateway_route_readability

**Phase 31 verification complete — all 5 tasks already in `complete/`; this was the final verification pass.**

Verified / changed
- Re-ran every phase-31 artifact live and green: `verify-31-page-only.py` 6 PASS/0 FAIL; browser pass `31-route-readability.cjs` **64 PASS / 0 FAIL** (exit 0); `27-task02-mobile-audit.cjs` **371 PASS / 0 FAIL / 0 FINDING** (exit 0); rebuilt `data/logs/31-route-readability.out` as the 4-part transcript.
- Added the 4 degradation-arm screenshots the criterion asked for but the pass lacked (`31-gateway-{unconfigured,unreachable,no-nginx,sockets-quiet}.png`) — 12 frames now, one per arm.
- Grep criteria: no gateway connector rule uses `border-strong`; no verdict token in the gateway CSS section or `page-gateway.js` beyond the `alerts.py`-owned `.warn`; exactly 4 `addEventListener` calls (none for the switch); `routeInConfig()` returns `true`; **no `.py` under `src/dashboards/` changed**.

Tests / lint (all 100% green)
- `uv run pytest` → **1796 passed, 16 skipped**; `--cov` → **98.94%** (floor 94); `ruff check src tests agent` → All checks passed; `pyright` → 0 errors; `node --check` clean; `bash .agents/validate.sh` → **exit 0**.
- `uv run pytest -m live --live` → **12 passed / 0 failed**; `./scripts/dev-server.sh status` → all **10 sources ok**.

Completion criteria: colour (33/33 rows every segment `--gate-on`, dark `rgb(63,185,80)` + light `rgb(26,127,55)`, zero grey dashes) ✔ · inbound-first rows + filter (33/33 line 1 = `out.selectors`, line 2 = `→ host:port · kind`; `stackexpected` → **6** rows, not the overview's "five" — the capture declares 6 such routes; tallies byte-identical `10/33/16/0`, zero requests) ✔ · trunk motion truth table + `null` arm prints `n/a` never `0` + reduced-motion stills 43 paths and every knob ✔ · screenshots/transcripts ✔ · payload page-only ✔ (literal byte-diff of a live socket table can never pass; answered by the no-`.py`-diff + shape-equality proof, as recorded in the transcript) · no behaviour change elsewhere ✔.

Docs: fixed 6 inconsistencies — two identifiers split across line breaks (`test_the_pipe_animation_stills_in_both_states_under_reduced_motion`, `32_gateway_resolved_target_liveness`), stale "sixteen routes" count, stale "four machines/hosts" in README **and** AGENTS (fleet is three), "three fields"→"three field types", gap-14 headline wording; updated the pass's screenshot list.

Deviations: `uv pip list --outdated` shows one advisory (`starlette 1.9.0 → 1.10.0`) — not upgraded, this phase moved no dependency (rule 8 / DoD apply "if deps moved"). `TODO.md` carries two new operator disk requests, left untouched.

Next pending phase: `32_gateway_resolved_target_liveness`.
This commit is contained in:
2026-10-25 05:32:30 -04:00
parent e366f599f3
commit 25fd74b7c5
52 changed files with 3434 additions and 181 deletions
@@ -0,0 +1,18 @@
**Phase 31 verification complete — all 5 tasks already in `complete/`; this was the final verification pass.**
Verified / changed
- Re-ran every phase-31 artifact live and green: `verify-31-page-only.py` 6 PASS/0 FAIL; browser pass `31-route-readability.cjs` **64 PASS / 0 FAIL** (exit 0); `27-task02-mobile-audit.cjs` **371 PASS / 0 FAIL / 0 FINDING** (exit 0); rebuilt `data/logs/31-route-readability.out` as the 4-part transcript.
- Added the 4 degradation-arm screenshots the criterion asked for but the pass lacked (`31-gateway-{unconfigured,unreachable,no-nginx,sockets-quiet}.png`) — 12 frames now, one per arm.
- Grep criteria: no gateway connector rule uses `border-strong`; no verdict token in the gateway CSS section or `page-gateway.js` beyond the `alerts.py`-owned `.warn`; exactly 4 `addEventListener` calls (none for the switch); `routeInConfig()` returns `true`; **no `.py` under `src/dashboards/` changed**.
Tests / lint (all 100% green)
- `uv run pytest` → **1796 passed, 16 skipped**; `--cov` → **98.94%** (floor 94); `ruff check src tests agent` → All checks passed; `pyright` → 0 errors; `node --check` clean; `bash .agents/validate.sh` → **exit 0**.
- `uv run pytest -m live --live` → **12 passed / 0 failed**; `./scripts/dev-server.sh status` → all **10 sources ok**.
Completion criteria: colour (33/33 rows every segment `--gate-on`, dark `rgb(63,185,80)` + light `rgb(26,127,55)`, zero grey dashes) ✔ · inbound-first rows + filter (33/33 line 1 = `out.selectors`, line 2 = `→ host:port · kind`; `stackexpected` → **6** rows, not the overview's "five" — the capture declares 6 such routes; tallies byte-identical `10/33/16/0`, zero requests) ✔ · trunk motion truth table + `null` arm prints `n/a` never `0` + reduced-motion stills 43 paths and every knob ✔ · screenshots/transcripts ✔ · payload page-only ✔ (literal byte-diff of a live socket table can never pass; answered by the no-`.py`-diff + shape-equality proof, as recorded in the transcript) · no behaviour change elsewhere ✔.
Docs: fixed 6 inconsistencies — two identifiers split across line breaks (`test_the_pipe_animation_stills_in_both_states_under_reduced_motion`, `32_gateway_resolved_target_liveness`), stale "sixteen routes" count, stale "four machines/hosts" in README **and** AGENTS (fleet is three), "three fields"→"three field types", gap-14 headline wording; updated the pass's screenshot list.
Deviations: `uv pip list --outdated` shows one advisory (`starlette 1.9.0 → 1.10.0`) — not upgraded, this phase moved no dependency (rule 8 / DoD apply "if deps moved"). `TODO.md` carries two new operator disk requests, left untouched.
Next pending phase: `32_gateway_resolved_target_liveness`.
@@ -0,0 +1,71 @@
== uv run pytest --cov (hermetic: no network, live tests skip) ==
........................................................................ [ 3%]
........................................................................ [ 7%]
........................................................................ [ 11%]
........................................................................ [ 15%]
........................................................................ [ 19%]
........................................................................ [ 23%]
........................................................................ [ 27%]
........................................................................ [ 31%]
.......s................................................................ [ 35%]
........................................................................ [ 39%]
........................................................................ [ 43%]
........................................................................ [ 47%]
........................................................................ [ 51%]
........................................................................ [ 55%]
........................................................................ [ 59%]
........................................................................ [ 63%]
........................................................................ [ 67%]
........................................................................ [ 71%]
......................................ssssssssssss...................... [ 75%]
........................................................................ [ 79%]
........................................................................ [ 83%]
........................................................................ [ 87%]
........................................................................ [ 91%]
........................................................................ [ 95%]
..........................sss........................................... [ 99%]
............ [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.8-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 201 0 100%
src/dashboards/api.py 499 3 99% 220, 250, 253
src/dashboards/app.py 190 0 100%
src/dashboards/auth.py 265 0 100%
src/dashboards/certs.py 292 0 100%
src/dashboards/collectors.py 216 4 98% 324, 328-329, 346
src/dashboards/config.py 126 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/gateway.py 463 4 99% 518, 529, 637, 1129
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 4063 43 99%
Required test coverage of 94% reached. Total coverage: 98.94%
1796 passed, 16 skipped, 1 warning in 164.04s (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):
starlette 1.9.0 1.10.0 wheel
@@ -0,0 +1,16 @@
**Implemented (task 01 — whole route is green)**
- `dash.css` `gateway pipes`: base `.gate-link::before`/`::after`/`.gate-elbow` now `2px dashed transparent` (geometry, no colour); four new rules colour spine halves + tick from the **row's** `on`/`off` via `--gate-on`/`--gate-off`; zero `--border-strong` left in the section; header/mobile comments state decision 2 (shared spine may colour, never move).
- `page-gateway.js`: `renderLeaf` toggles `on`/`off` on the `<li>` beside the `<svg>`, same value, same pass; `leafRow()` ships no state class; `renderGroups` comment records why rows need no class pruning; header docstring restated.
- `tests/test_api.py`: restated `test_haproxy_draws_straight_across_and_nginx_branches` (transparent base borders), extended `test_no_gateway_count_is_coloured_by_a_verdict` (connectors read the pair; `--border-strong` absent; all six connector selectors present), **new** `test_the_whole_route_takes_one_existence_class` (row+svg from one value, `transparent` base, switch `[aria-checked]` re-pinned).
- Browser proof on the live ingress: `data/logs/31-task01-green.cjs` → `31-task01-green.out` **27 PASS / 0 FAIL** — 48 spine halves + 33 ticks + 33 runs + 33 switches all `rgb(63,185,80)` dark and `rgb(26,127,55)` light, 81 dashed segments 0 grey, `off` moves all four to red, stateless row computes `rgba(0,0,0,0)`/`stroke:none`, 360 px restored half green, 0 px sideways scroll; screenshots `31-task01-{dark,light,phone}.png`.
**Verification**
- `uv run pytest` → **1792 passed, 16 skipped**; `bash .agents/validate.sh` → exit 0, coverage **98.94%** (floor 94), ruff clean, pyright 0 errors, `node --check` clean.
- `uv run pytest -m live --live` → **12 passed**; `./scripts/dev-server.sh status` → all **10 sources ok**; mobile audit re-run **371 PASS / 0 FAIL / 0 FINDING** (`data/logs/31-task01-mobile-audit.out`).
- No Python changed — `git status` shows only `web/static/dash.css`, `web/static/page-gateway.js`, `tests/test_api.py`, `AGENTS.md`, `README.md` (so `/api/gateway/snapshot` is byte-identical); switch still unwired, `routeInConfig()` still `true`.
**Docs**: AGENTS.md `/gateway` conventions (colour covers the whole row, motion stays on the run, `transparent` base rules, no per-width colour) and README's `/gateway` paragraph.
**Decisions/deviations**: used the four explicit state rules rather than the `--gate-line` custom property (a new token would need a `:root` default for `test_no_stylesheet_rule_uses_an_undeclared_token` and would duplicate the `transparent` default); left the pre-existing `starlette 1.9.0→1.10.0` advisory untouched (phase decision 7 forbids dependency/Python changes).
**Next pending task**: `31_gateway_route_readability/02_the_inbound_word_leads_the_row.md`.
@@ -0,0 +1,71 @@
== uv run pytest --cov (hermetic: no network, live tests skip) ==
........................................................................ [ 3%]
........................................................................ [ 7%]
........................................................................ [ 11%]
........................................................................ [ 15%]
........................................................................ [ 19%]
........................................................................ [ 23%]
........................................................................ [ 27%]
........................................................................ [ 31%]
...s.................................................................... [ 35%]
........................................................................ [ 39%]
........................................................................ [ 43%]
........................................................................ [ 47%]
........................................................................ [ 51%]
........................................................................ [ 55%]
........................................................................ [ 59%]
........................................................................ [ 63%]
........................................................................ [ 67%]
........................................................................ [ 71%]
..................................ssssssssssss.......................... [ 75%]
........................................................................ [ 79%]
........................................................................ [ 83%]
........................................................................ [ 87%]
........................................................................ [ 91%]
........................................................................ [ 95%]
......................sss............................................... [ 99%]
........ [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.8-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 201 0 100%
src/dashboards/api.py 499 3 99% 220, 250, 253
src/dashboards/app.py 190 0 100%
src/dashboards/auth.py 265 0 100%
src/dashboards/certs.py 292 0 100%
src/dashboards/collectors.py 216 5 98% 323-324, 328-329, 346
src/dashboards/config.py 126 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/gateway.py 463 4 99% 518, 529, 637, 1129
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 4063 44 99%
Required test coverage of 94% reached. Total coverage: 98.92%
1792 passed, 16 skipped, 1 warning in 165.46s (0:02:45)
== 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):
starlette 1.9.0 1.10.0 wheel
@@ -0,0 +1,18 @@
**Implemented** (task 31/02 — page-only, no Python touched)
- `page-gateway.js`: replaced `selectorText()` with `inboundText()` (`out.selectors` joined → fallback `out.kind`, never `out.host`) and `counterpartText()` (`→ host:port · kind`); added `ARROW`/`SHARED_WHY` constants; `renderLeaf()` now writes the inbound word to `.pipe-name` (endpoint only as the null-inbound fallback, so the primary line is never empty) and the counterpart to a new `.pipe-end mono` span; `leafTitle()` carries **both ends** + kind + sharing sentence; `leafAria()` unchanged in facts, now sharing one `SHARED_WHY` string.
- `dash.css`: new `.pipe-end` rule (11px, mono class, same truncation trio) beside `.gate-selector`; `.gate-selector` comment now scopes it to the trunk; `.pipe-name` comment restated for the new rank/gap 13.
- `gateway.html`: footer paragraph 2 rewritten to the operator's dash sentence (still exactly 3 paragraphs); card comments above both `.gate-map`s record the reading order.
- `tests/test_api.py`: strengthened `test_a_clipped_pipe_label_keeps_its_full_text_on_the_element` (both ends whole in `title` *and* `aria-label`, no string-cutting in the helpers, longest-endpoint control over the live capture); new `test_the_leaf_row_leads_with_the_inbound_word` (helper sources, call sites, `GATEWAY_PAGE_FIELDS` both directions, `brain.reeseapps.com → …:443`, haproxy backend names, `default` verbatim, no empty primary line, `:80` refusal intact) + `rendered_leaf()` second-reading mirror. Negative controls confirmed both tests fail on reverted code.
**Verification**: `bash .agents/validate.sh` exit 0 — `uv run pytest --cov --cov-fail-under=94` **1793 passed, 16 skipped**, coverage **98.94%**; `ruff check src tests agent` clean; `pyright` 0 errors; `node --check` clean. Live browser pass `data/logs/31-task02-inbound-row.cjs` → `31-task02-inbound-row.out`: **21/21 PASS**, 33/33 rows read inbound→counterpart, no console errors/non-GET, 0 px sideways scroll at 360 px; screenshots `31-task02-gateway-{1440,360}.png`. `27-task02-mobile-audit.cjs` re-run → exit 0, 0 FAIL / 0 FINDING (`31-task02-mobile-audit.out`). `dev-server.sh status`: all ten sources `ok`.
**Docs**: README `/gateway` table row + UI-conventions paragraph and AGENTS.md table row + `/gateway` conventions paragraph now state line 1 = inbound word, line 2 = `→ host:port · kind`, both carried whole; AGENTS.md test count 1791→1793. Also corrected a stale claim in both files (haproxy's inbound word is the `backend` name, not the `server` name).
**Decisions/deviations**
- Arrow is rendered `→ host:port` (one space) rather than the task snippet's `→host:port`, matching the phase's completion-criterion rendering and the live transcript; `ARROW = '→'` stays as specified.
- `GATEWAY_PAGE_FIELDS` already named all four `connections.out.*` paths, so I pinned the two-directional resolution in the new test + documented the second use instead of duplicating entries.
- Task said the fleet's longest endpoint is `reeseapps-brain-of-code.reeselink.com:443`; the capture's longest is actually `reeseapps-brain-of-reese.reeselink.com:443` (42 chars) — pinned by length+family so either neighbour satisfies the control.
- gap 13 measurement for task 05: 1440 px → 0 clipped; 360 px → 19/33 inbound and 33/33 counterpart lines ellipsised, every one keeping full text in `title` + `aria-label`.
- Left phase 30's `data/logs/30-connection-map.cjs` untouched (its `sels` FINDING still reads `.gate-selector`, absent from leaf rows now — it still exits 0; gap 13's restatement is task 05's).
**Next pending task**: `.agents/phases/todo/31_gateway_route_readability/03_the_trunk_moves_on_a_real_number.md`.
@@ -0,0 +1,71 @@
== uv run pytest --cov (hermetic: no network, live tests skip) ==
........................................................................ [ 3%]
........................................................................ [ 7%]
........................................................................ [ 11%]
........................................................................ [ 15%]
........................................................................ [ 19%]
........................................................................ [ 23%]
........................................................................ [ 27%]
........................................................................ [ 31%]
....s................................................................... [ 35%]
........................................................................ [ 39%]
........................................................................ [ 43%]
........................................................................ [ 47%]
........................................................................ [ 51%]
........................................................................ [ 55%]
........................................................................ [ 59%]
........................................................................ [ 63%]
........................................................................ [ 67%]
........................................................................ [ 71%]
...................................ssssssssssss......................... [ 75%]
........................................................................ [ 79%]
........................................................................ [ 83%]
........................................................................ [ 87%]
........................................................................ [ 91%]
........................................................................ [ 95%]
.......................sss.............................................. [ 99%]
......... [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.8-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 201 0 100%
src/dashboards/api.py 499 3 99% 220, 250, 253
src/dashboards/app.py 190 0 100%
src/dashboards/auth.py 265 0 100%
src/dashboards/certs.py 292 0 100%
src/dashboards/collectors.py 216 5 98% 323-324, 328-329, 346
src/dashboards/config.py 126 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/gateway.py 463 4 99% 518, 529, 637, 1129
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 4063 44 99%
Required test coverage of 94% reached. Total coverage: 98.92%
1793 passed, 16 skipped, 1 warning in 179.17s (0:02:59)
== 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):
starlette 1.9.0 1.10.0 wheel
@@ -0,0 +1,16 @@
**Task 03 complete — all green.**
**Implemented**
- `page-gateway.js`: `buildPipe(trunk = false)` adds `pipe pipe-trunk` (one builder, one class shape for both lines); `groupElement()` appends the stub as `.gate-trunk`'s last child via DOM (`createElementNS`, commented); new `trunkAria(group)` — three pinned sentences, indexes only `listeners[].connections`; `renderTrunk()` patches `hidden`/`on`/`off`/`live`/`still` + `role="img"` + `aria-label`/`title`, with `measured` separating `0` from `null`; file-header bullet now says motion has two payload paths (leaf `connections[]`, trunk `listeners[]`) and never implies a leaf.
- `dash.css`: `.pipe-trunk { width: 26px; height: 10px; flex: none; margin-inline-start: auto; }` — no new token, no new keyframe; motion/colour still come from `.pipe.on`/`.pipe.live`.
- `tests/test_api.py`: new `test_the_trunk_pipe_is_an_accessible_image_of_its_port` (+ `rendered_trunk` mirror) covering both arms over the capture and over real `sockets_silent`; `test_a_pipe_moves_only_while_it_carries_a_connection` restated for the two payload paths with "trunk ≠ leaf" absences; reduced-motion pin now demands descendant selectors and forbids `.pipe-trunk` styling motion; the accessible-image pin now asserts two pipe builders, one `createElementNS`, `role`+`aria`+`title` on both.
- Browser pass `data/logs/31-task03-trunk.cjs` → `31-task03-trunk.out` (35 PASS / 0 FAIL, exit 0) with `31-task03-trunk-{1440,360,light,sockets-quiet,reduced}.png`: `:443`/`:4443` run, the 8 idle ports sit still, all green `--gate-on` in dark *and* light, same colour either way; every `aria-label` is one of the three sentences and names no endpoint; socket-quiet arm is green, motionless, `n/a`, **0** occurrences of `0 live`; stub flush at the trunk's right edge (= the spine x) even on wrapped trunks, no clipping, 0 px sideways scroll at 360 px; reduced motion stills every stub with no `.pipe-trunk` rule.
- `data/logs/27-task02-mobile-audit.cjs` re-run: **371 PASS / 0 FAIL / 0 FINDING**, exit 0 (`data/logs/31-task03-mobile-audit.out`).
**Commands / results:** `uv run pytest` → **1794 passed, 16 skipped**; `uv run pytest --cov --cov-fail-under=94` → **98.94%** (floor met, not down); `uv run ruff check src tests agent` → all checks passed; `uv run pyright` → 0 errors; `node --check src/dashboards/web/static/page-gateway.js` → clean; `bash .agents/validate.sh` → **exit 0**. `git diff` touches **no** Python under `src/dashboards/` except `web/`; `routeInConfig()` still returns `true`; no switch listener.
**Docs:** README `/gateway` conventions paragraph and AGENTS.md (the groups paragraph + the `live`/`still` bullet) now state the trunk's own line, its port-only claim and its three sentences.
**Deviations (2, both measured):** added `margin-inline-start: auto` to `.pipe-trunk` — without it a trunk whose text wraps (`:80` live) dropped the dash alone at the trunk's far-left, stranded from the spine; it costs ~6 px of slack on one-line trunks. `aria-label` is set with `setAttribute` (the sketch's `setTxt` writes `textContent`, which an `<svg>` has no use for). Left alone: `starlette 1.9.0 → 1.10.0` advisory (pre-existing, `validate.sh` treats it as advisory; this task moves no dependencies).
**Next pending task:** `.agents/phases/todo/31_gateway_route_readability/04_find_a_domain.md`
@@ -0,0 +1,71 @@
== uv run pytest --cov (hermetic: no network, live tests skip) ==
........................................................................ [ 3%]
........................................................................ [ 7%]
........................................................................ [ 11%]
........................................................................ [ 15%]
........................................................................ [ 19%]
........................................................................ [ 23%]
........................................................................ [ 27%]
........................................................................ [ 31%]
.....s.................................................................. [ 35%]
........................................................................ [ 39%]
........................................................................ [ 43%]
........................................................................ [ 47%]
........................................................................ [ 51%]
........................................................................ [ 55%]
........................................................................ [ 59%]
........................................................................ [ 63%]
........................................................................ [ 67%]
........................................................................ [ 71%]
....................................ssssssssssss........................ [ 75%]
........................................................................ [ 79%]
........................................................................ [ 83%]
........................................................................ [ 87%]
........................................................................ [ 91%]
........................................................................ [ 95%]
........................sss............................................. [ 99%]
.......... [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.8-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 201 0 100%
src/dashboards/api.py 499 3 99% 220, 250, 253
src/dashboards/app.py 190 0 100%
src/dashboards/auth.py 265 0 100%
src/dashboards/certs.py 292 0 100%
src/dashboards/collectors.py 216 5 98% 323-324, 328-329, 346
src/dashboards/config.py 126 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/gateway.py 463 4 99% 518, 529, 637, 1129
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 4063 44 99%
Required test coverage of 94% reached. Total coverage: 98.92%
1794 passed, 16 skipped, 1 warning in 165.70s (0:02:45)
== 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):
starlette 1.9.0 1.10.0 wheel
@@ -0,0 +1,26 @@
**Phase 31 task 04 (`04_find_a_domain.md`) is complete and green.**
## What shipped
| File | Change |
|---|---|
| `src/dashboards/web/pages/gateway.html` | `#gate-filter` (`type="search"`, `autocomplete="off"`, `spellcheck="false"`, `aria-label`) + `#gate-filter-count` chip in the *Connection map* head; `[data-k="no-match"]` sentence in each panel; the **fourth** footer paragraph |
| `src/dashboards/web/static/dash.css` | `input[type="search"]` joined the shared field rule; `.gate-filter { max-width: 18rem; min-width: 0 }` + `width: 100%` at ≤640px; no `:hover`/`cursor`/`transition`, no new token |
| `src/dashboards/web/static/page-gateway.js` | `state.filter`/`state.row`; `haystack()` / `trunkHaystack()` / `leafShown` / `trunkShown`; `renderPanels()` (the one repaint the poll, the keystroke and `Escape` all share); `li.hidden` by index in the patch pass; per-panel no-match; `N of M routes` chip |
| `tests/test_api.py` | `test_the_filter_narrows_the_map_without_asking_anything`, `test_the_filter_field_is_markup_the_sheet_and_nobody_else_uses`; the switch pin's `addEventListener` set became an allow-list that still forbids `click`; footer pin 3 → 4 |
| `README.md` / `AGENTS.md` | the filter named in both `/gateway` rows and paragraphs, the refusal to group/sort/colour by suffix, footer=4, the four real bindings, test count 1791 → 1796 |
## Verification
- `bash .agents/validate.sh` → **EXIT=0**: **1796 passed, 16 skipped**, coverage **98.94%** (floor 94), ruff clean, pyright 0 errors, `node --check` clean, hygiene clean. Only advisory: `starlette 1.9.0 → 1.10.0` (pre-existing; this task moves no dependency).
- `data/logs/31-task04-filter.cjs` → `31-task04-filter.out` **34 PASS / 0 FAIL** on the live ingress (33 routes): `stackexpected` → 6 of 33 with only `nginx:443` + `haproxy:2000` standing; case-insensitive; tallies `10 / 33 / 12 / 0` identical either side of every keystroke; **zero** requests across five keystroke states; `Escape` restores all 33; a client address keeps `:443`'s whole 18-leaf fan-out; `no-such-domain-anywhere` hides 10/10 groups and prints the sentence in both panels; the same filtered view after a real 30 s poll; 360 px → 320 px field, **0 px** sideways scroll. Screenshots `31-task04-filter-{stackexpected,no-match,cleared,360}.png`.
- `data/logs/27-task02-mobile-audit.cjs` → `31-task04-mobile-audit.out`: **371 PASS / 0 FAIL / 0 FINDING**, 10 skips, exit 0.
- Report written: `.agents/reports/31_gateway_route_readability/31_gateway_route_readability__04_find_a_domain.a1.md` (+ `.status` = `ok`, + `.a1.validate`).
- Dev server is up; `/api/gateway/snapshot` untouched — no Python changed (`git diff` under `src/dashboards/` is `web/` only).
## Two things I need your answer on
1. **Phase bookkeeping** (rule 4 — I won't edit `.agents/phases/todo/` unprompted): tick the Completion-Criteria boxes in `04_find_a_domain.md` and move it to `.agents/phases/complete/31_gateway_route_readability/` like tasks 01–03, tick only, or leave the phase tree alone?
2. **Commit?** HEAD is still `phase: 30_gateway_connection_map`, so phase 31 tasks 01–04 are all uncommitted. Options: leave it, one phase-31-so-far commit, or task 04 only (all with `--no-gpg-sign` + `LLM-Generated: true`).
Next in the phase is `05_docs_and_live_verify.md` (the project-wide README/AGENTS pass, gap 13's re-measurement with the new row + filter, and the read-only live check).
@@ -0,0 +1,71 @@
== uv run pytest --cov (hermetic: no network, live tests skip) ==
........................................................................ [ 3%]
........................................................................ [ 7%]
........................................................................ [ 11%]
........................................................................ [ 15%]
........................................................................ [ 19%]
........................................................................ [ 23%]
........................................................................ [ 27%]
........................................................................ [ 31%]
.......s................................................................ [ 35%]
........................................................................ [ 39%]
........................................................................ [ 43%]
........................................................................ [ 47%]
........................................................................ [ 51%]
........................................................................ [ 55%]
........................................................................ [ 59%]
........................................................................ [ 63%]
........................................................................ [ 67%]
........................................................................ [ 71%]
......................................ssssssssssss...................... [ 75%]
........................................................................ [ 79%]
........................................................................ [ 83%]
........................................................................ [ 87%]
........................................................................ [ 91%]
........................................................................ [ 95%]
..........................sss........................................... [ 99%]
............ [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.8-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 201 0 100%
src/dashboards/api.py 499 3 99% 220, 250, 253
src/dashboards/app.py 190 0 100%
src/dashboards/auth.py 265 0 100%
src/dashboards/certs.py 292 0 100%
src/dashboards/collectors.py 216 4 98% 324, 328-329, 346
src/dashboards/config.py 126 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/gateway.py 463 4 99% 518, 529, 637, 1129
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 4063 43 99%
Required test coverage of 94% reached. Total coverage: 98.94%
1796 passed, 16 skipped, 1 warning in 169.82s (0:02:49)
== 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):
starlette 1.9.0 1.10.0 wheel
@@ -0,0 +1,12 @@
Phase 31 task 05 is complete. Final state:
**Implemented**
- `data/logs/31-route-readability.cjs` — read-only live browser pass, **64 checks / 0 FAIL**: computed `--gate-on` on every segment of all 33 rows in **both** themes (`rgb(63,185,80)` / `rgb(26,127,55)`) with **no** dashed border computing `--border-strong`; gap‑13 clip counts at 1440 px (0/33 + 0/33, 361 px column) and 360 px (19/33 + 33/33, 139 px) with 33/33 rows carrying both ends whole; the trunk truth table plus a deterministic arm (7 sockets on `:443`, every leaf count removed → trunk moves, 0/18 leaves do); filter = **zero requests** with byte-identical tallies; reduced motion stills 43 paths + every knob; all degradation arms; 0 px sideways scroll at both widths; **zero non-GET requests**.
- `data/logs/verify-31-page-only.py` (6/0) — the payload half, explained at the head of the transcript: a literal before/after byte diff of a live socket snapshot can never pass, so it proves the guarded question instead (no `.py` differs from the phase‑30 commit; 571 leaf paths with 0 non-volatile differences across a real poll; key set equal to `30-connection-map.out`).
- Transcript `data/logs/31-route-readability.out` (4 parts, 222 lines) + 8 screenshots + `31-task05-mobile-audit.out` (audit **371 PASS / 0 FAIL / 0 FINDING**, exit 0).
- **Real defect found and fixed**: `svg.title = aria` assigned a property `SVGElement` does not reflect — no tooltip ever painted. Now `setAttribute('title', aria)` (leaf + trunk), pinned in `tests/test_api.py`.
- Docs: AGENTS `/gateway` paragraph (phase‑31 framing, measurement paragraph, dash→phase 32, `setAttribute`), gap 13 restated with the measured numbers, new gaps **14/15** (one line each for `32_gateway_resolved_target_liveness` and `33_gateway_route_switching`); README dash/what-runs-today paragraph (**agent 1.2.0**, no resolution, no switching); `gateway.html` footer comment three→four; stale "17-row fan-out" counts corrected to 18.
**Results**: `uv run pytest` **1796 passed, 16 skipped**, coverage **98.92%** (floor 94) · `ruff` **All checks passed** · `pyright` **0 errors** · `node --check web/static/*.js` clean · `bash .agents/validate.sh` **exit 0** · live suite **12 passed, 0 failed** · dev server restarted, all **10 sources ok**.
**Notes/deviations**: `validate.sh` prints one advisory (`starlette 1.9.0 → 1.10.0`) — not upgraded, this phase touched no dependency (rule 8 applies to dependency changes); `NOTES.md` left empty (this project's log is AGENTS.md + `data/logs/`); the phase dir stays in `todo/` for the harness and the task file is unmodified. Next pending: none in phase 31 — the harness may move it to `complete/`, after which phase 32 is the next work item.
@@ -0,0 +1,71 @@
== uv run pytest --cov (hermetic: no network, live tests skip) ==
........................................................................ [ 3%]
........................................................................ [ 7%]
........................................................................ [ 11%]
........................................................................ [ 15%]
........................................................................ [ 19%]
........................................................................ [ 23%]
........................................................................ [ 27%]
........................................................................ [ 31%]
.......s................................................................ [ 35%]
........................................................................ [ 39%]
........................................................................ [ 43%]
........................................................................ [ 47%]
........................................................................ [ 51%]
........................................................................ [ 55%]
........................................................................ [ 59%]
........................................................................ [ 63%]
........................................................................ [ 67%]
........................................................................ [ 71%]
......................................ssssssssssss...................... [ 75%]
........................................................................ [ 79%]
........................................................................ [ 83%]
........................................................................ [ 87%]
........................................................................ [ 91%]
........................................................................ [ 95%]
..........................sss........................................... [ 99%]
............ [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.8-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 201 0 100%
src/dashboards/api.py 499 3 99% 220, 250, 253
src/dashboards/app.py 190 0 100%
src/dashboards/auth.py 265 0 100%
src/dashboards/certs.py 292 0 100%
src/dashboards/collectors.py 216 4 98% 324, 328-329, 346
src/dashboards/config.py 126 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/gateway.py 463 4 99% 518, 529, 637, 1129
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 4063 43 99%
Required test coverage of 94% reached. Total coverage: 98.94%
1796 passed, 16 skipped, 1 warning in 167.52s (0:02:47)
== 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):
starlette 1.9.0 1.10.0 wheel