phase: 29_gateway_dashboard_page
Phase 29 completion pass is done. Final state:
**Verification (all re-run this pass, no task files touched)**
- `uv run pytest` → **1746 passed, 16 skipped**; `uv run pytest --cov` → **99.05 %** (gate 94 %); `bash .agents/validate.sh` → **rc=0** (ruff clean, pyright `0 errors, 0 warnings`, `node --check` clean); `uv run pytest -m live --live` → **12 passed**.
- Live, read-only: dev server **10/10 sources `ok`** (incl. `gateway`); `data/logs/29-task06-live.cjs` → **49 PASS / 0 FAIL** with per-port inbound counts reconciled against a direct `/proc/net/tcp{,6}` read (divergence 0); `data/logs/verify-29-task06-predates.py` → 0 failed (1.1.0 arm renders `WHY_PREDATES`, no pipes, every tally `n/a`, job still `ok`); phase-27 mobile audit re-run over all nine served pages → **371 PASS / 0 FAIL / 0 FINDING** (`data/logs/29-final-mobile-audit.out`).
- Criteria: (1) unconfigured `GATEWAY_HOST` → `404 {configured:false}` with dial spies + `#gate-empty` sentence ✅; (2) 1.1.0/`--no-proxy` → `supported:false`, no `0`s, never red ✅; (3) live gateway 1.2.0 → pipes animate, totals reconcile ✅; (4) no `severity`/verdict colour anywhere, validate green ✅; (5) docs updated + gap 13 logged ✅; (6) the five existing dashboards, jobs and `/api/summary` unchanged (live control leg) ✅.
**Docs (consistency pass, incl. pre-existing drift)**
- `README.md`: architecture diagram now lists `sources/gateway.py`, the 30 s gateway job and the third `/snapshot` reader; `GATEWAY_HOST` row says *a name, never a URL*; the `POLL_MS_*` row states `/gateway`'s fixed 15 s browser poll; "nine pollers" → per-source cadences; phase-28 "until phase 29" wording → current state; new Testing paragraph for `tests/test_gateway_source.py` (join pinned from the captures, reconciliation identity, `getaddrinfo` spy).
- `AGENTS.md`: phase-28 stop-point wording replaced with the shipped phase-29 state; suite/coverage figures → 1746 / 16 skipped / 99.08 %→99.05 % band and `~99 %`; window-chips paragraph records that `/gateway` has **no** chips (fixed 24 h); new testing-policy bullet for the join; stale page counts fixed (seven → eight tabbed pages / `MARKUP_PAGES`, clock-order and badge-count transcripts annotated); gap 12 now records the phase-29 nine-page audit re-run.
- `tests/test_api.py`: comment/docstring-only fixes for the "seven pages" wording that phase 29 made eight.
**Deviations / notes**
- Nothing implemented: all six task files were already in `complete/`, so this run was the final verification + docs pass; no git index or phase file was touched.
- Coverage wobbles 99.05–99.08 % between runs on a pre-existing timing-dependent line (`collectors.py` `/slots`-alias `except`/`break`), not a phase-29 path (`sources/gateway.py` is 100 %).
- `uv pip list --outdated` still lists upgrades; dependencies did not move in this phase, so they were left alone rather than upgraded mid-verification.
- Historical browser-transcript counts (phase 18/20/25 script runs) were left as recorded history and annotated instead of rewritten.
**Next phase:** none — `.agents/phases/todo/` is empty after 29; further work needs a new phase authored first.
This commit is contained in:
+13
-4
@@ -56,7 +56,12 @@ CURL_HEADERS = {"accept": "*/*", "user-agent": "curl/8.5.0"}
|
||||
PROTECTED_API = (
|
||||
"/api/config", "/api/events", "/api/metrics", "/api/history", "/api/summary",
|
||||
"/api/system/snapshot", "/api/system/history", "/api/disks/snapshot",
|
||||
"/api/disks/history", "/api/llm/snapshot", "/api/llm/history", "/api/ups/snapshot",
|
||||
"/api/disks/history",
|
||||
# Phase 29: the ingress's pipes. `…/snapshot` carries the gateway's whole routing map — every
|
||||
# domain it serves and every address it dials — so it is listed here for the same reason the
|
||||
# bundle download is, whatever the payload happens to hold today.
|
||||
"/api/gateway/snapshot", "/api/gateway/history",
|
||||
"/api/llm/snapshot", "/api/llm/history", "/api/ups/snapshot",
|
||||
"/api/ups/history", "/api/ups/commands", "/api/auth/status",
|
||||
# Phase 15: the certificate authority's own surface. `/api/certs/<name>/bundle` is every
|
||||
# agent's private key, so "it needs a session" is the whole point of listing it here.
|
||||
@@ -68,7 +73,11 @@ PROTECTED_API = (
|
||||
#: thing that must never regress is a new route answering anonymously. `/certs` is the sharper
|
||||
#: case of the two — an anonymous caller reaching it would see the CA's serials and every host
|
||||
#: the dashboard dials.
|
||||
PROTECTED_PAGES = ("/", "/system", "/disks", "/llm", "/ups", "/settings", "/certs")
|
||||
#: `/gateway` (phase 29 task 04) is the sixth dashboard: it prints this fleet's ingress map —
|
||||
#: every listening port, every `server_name`, every internal target — so it is gated by being a
|
||||
#: page and nothing else.
|
||||
PROTECTED_PAGES = ("/", "/system", "/disks", "/llm", "/ups", "/gateway", "/settings",
|
||||
"/certs")
|
||||
|
||||
|
||||
#: The one write this app has, with the fields the route needs (the UPS is unconfigured, so a
|
||||
@@ -1598,8 +1607,8 @@ def test_the_served_route_table_is_the_surface_the_gate_describes(anon: TestClie
|
||||
pages = sorted(str(route.path) for route in served_routes(anon)
|
||||
if isinstance(getattr(route, "path", None), str)
|
||||
and not str(route.path).startswith(("/api", "/static")))
|
||||
assert pages == ["/", "/certs", "/disks", "/llm", "/login", "/settings", "/system",
|
||||
"/ups"]
|
||||
assert pages == ["/", "/certs", "/disks", "/gateway", "/llm", "/login", "/settings",
|
||||
"/system", "/ups"]
|
||||
for path in pages:
|
||||
response = anon.get(path, headers=BROWSER_HEADERS)
|
||||
if path == "/login":
|
||||
|
||||
Reference in New Issue
Block a user