phase: 30_gateway_connection_map

**Phase 30 final verification pass — all 6 tasks already in `complete/`, so this was the phase-completion pass.**

- **Verified (re-run, not trusted):** `/gateway` serves exactly two panels (nginx→haproxy, `GATEWAY_CARD_KEYS`); haproxy rows `.solo` straight across, nginx `:443` branched with one tick per leaf (18 live, fixture 17); every pipe computes the theme `--ok` in dark+light, animates only at count ≥1, fully static under `prefers-reduced-motion`; switch = `role="switch"`+`aria-checked`+`aria-disabled`, no listener, footer+title name the missing write path; zero non-GET requests anywhere.
- **Live read-only re-run** (`data/logs/30-final-live-verify.out`): 17 PASS — `warnings: []`, 6 haproxy frontends→own `server`, `:443` 18 map leaves incl. `default→127.0.0.1:4443`, `:4443` 6 / `:8443` 3, `:80` bare listener, no `unmatched`/`outbound`/`by_port`/`partial`, no `9120`; inbound reconciliation **page 16 = kernel 16**; `gateway` block = the 12 declared keys; no `gateway:config` alert on a clean config. `git diff --stat agent/ deploy/` empty; agents 1.2.0 (gateway) / 1.1.0 ×2, all `tls`, `bundle_rev 2`.
- **Tests/lint (exact):** `uv run pytest` → **1791 passed, 16 skipped**, exit 0 · `bash .agents/validate.sh` → **exit 0**, `--cov` **98.94%** (floor 94), `ruff check src tests agent` clean, `pyright` 0 errors, `node --check` all page scripts, hygiene clean · `uv run pytest -m live --live` → **12 passed** · `uv pip list --outdated` empty · `./scripts/dev-server.sh status` → **10 sources ok**, `gateway` included · browser pass `30-connection-map.out` 63 PASS/0 FAIL (5 screenshots at 1440/360) · `30-final-mobile-audit.out` 371 PASS/0 FAIL/0 FINDING, exit 0, nine pages.
- **Docs fixed:** README `alerts.py` bullet omitted phase 30's keyword-only `gateway_payload` and its one `warn` config-integrity row (+suppression) — added; overview ingress line labelled a dated reading. AGENTS gap 13 corrected 391 px → **361 px** (task 05's pill took 30 px), "task 06 measured" → "phase 29 measured", coverage figure un-pinned to 98.9 %, live-suite count noted (12 today), "all five pages" severity sentence clarified for `/gateway`'s existence-only palette. Everything else reviewed in full and **verified consistent**.
- **Defect fixed:** duplicated footer-assertion block at the end of `tests/test_api.py::test_the_switch_is_built_and_explicitly_unwired` (one copy removed).
- **Notable:** `NOTES.md` is empty in the working tree — both items in it (non-mTLS agents, disappearing admin links) landed in phases 24 and 20; left cleared. Fixture/live `map`-count drift (17 vs 18) is recorded in AGENTS.md, not silently re-captured.
- **Next pending phase:** none — `30_gateway_connection_map` was the only entry in `.agents/phases/todo/`.
This commit is contained in:
2026-10-23 22:56:21 -04:00
parent fa4f38c694
commit e366f599f3
51 changed files with 5419 additions and 1519 deletions
+23 -5
View File
@@ -15,7 +15,7 @@ import asyncio
import logging
import sqlite3
import time
from collections.abc import Iterator
from collections.abc import Callable, Iterator
from typing import Any
import pytest
@@ -208,6 +208,24 @@ async def test_states_can_be_filtered_to_one_dashboard(store) -> None:
assert [s["key"] for s in hub.states("ups")] == ["nut"]
async def _wait_for(predicate: Callable[[], bool], deadline: float = 10.0) -> bool:
"""Poll `predicate` on the loop until it holds or the deadline passes.
The two maintenance tests below cross a thread boundary (`anyio.to_thread.run_sync`, because a
SQLite write must not stall the event loop), so *when* the first pass lands is a property of the
machine and of whether coverage is tracing every line — a fixed `asyncio.sleep()` fitted to a
quiet laptop is a flake on a loaded one. Waiting for the thing itself is the assertion; the
deadline only stops a broken loop hanging the suite.
"""
loop = asyncio.get_running_loop()
end = loop.time() + deadline
while loop.time() < end:
if predicate():
return True
await asyncio.sleep(0.02)
return predicate()
async def test_maintenance_failure_is_logged_not_swallowed(store, monkeypatch,
caplog: pytest.LogCaptureFixture) -> None:
"""A broken rollup means every chart past the raw window is empty. Retrying quietly
@@ -220,12 +238,12 @@ async def test_maintenance_failure_is_logged_not_swallowed(store, monkeypatch,
task = asyncio.create_task(hub._maintain_loop(interval=0.02))
try:
with caplog.at_level(logging.ERROR, logger="dashboards.poller"):
await asyncio.sleep(0.15)
logged = await _wait_for(lambda: "store maintenance failed" in caplog.text)
finally:
task.cancel()
with pytest.raises(asyncio.CancelledError):
await task
assert "store maintenance failed" in caplog.text
assert logged, "a failed rollup must be logged, never retried in silence"
assert "no such table" in caplog.text, "the log line must carry the real reason"
@@ -236,12 +254,12 @@ async def test_maintenance_runs_from_the_hub_and_records_its_pass(store) -> None
"pruned": 0}
task = asyncio.create_task(hub._maintain_loop(interval=0.02))
try:
await asyncio.sleep(0.1)
ran = await _wait_for(lambda: bool(calls))
finally:
task.cancel()
with pytest.raises(asyncio.CancelledError):
await task
assert calls, "the maintenance loop must actually call maintain"
assert ran, "the maintenance loop must actually call maintain"
async def test_events_written_by_a_job_land_in_the_store(store) -> None: