phase: 07_canvas_client_and_hud
**Final report — phase 07 final verification pass** - **Verified:** all 5 tasks present in `complete/`; app boots (`/healthz` 200, `/` 200, static + atlas + `/api/levels` served); `docs/CLIENT.md` module map matches the on-disk `app/static/` tree; no code defects found. - **Tests/lint/coverage:** `bash .agents/validate.sh` → ruff clean, pyright 0 errors, `undercooked-levels validate` pass, **full pytest 1838 passed, 97.21% coverage (floor 90%)**, slow sim stage 2 passed, repo hygiene clean. `uv run pytest tests/e2e -q` → **53 passed** (all 8 suites added this phase). - **Criteria:** (1) two tabs create→join→ready→start→serve one dish, keyboard only — green via `test_us01/02/03/04` ✔; (2) e2e green ✔; (3) canvas non-blank, pixels change after a move — `test_us04_render_smoke` ✔; (4) HUD score within 200 ms of the `served` event — asserted, green ✔; (5) zero console errors + zero off-origin requests — `test_atlas_loads` / `test_us01_landing_surface` ✔; (6) `docs/CLIENT.md` matches layout ✔; (7) 100% suite + 97.21% ✔; (8) docs pass below ✔; (9) no regression in completed phases (all prior suites green) ✔. - **README.md fixed:** status still said "next pending phase is 07" → now 07 complete, 08 pending; the phase-07 client text buried in the phase-05 bullet → its own "canvas client, art, HUD (phase 07)" bullet; stale "What does not exist yet" (claimed movement/controls missing) → rewritten to phases 08–14; "once the room flow ships" → "live in the browser today"; Layout `app/static/` "renderer arrives with the rest of phase 07" → full shipped-client map; Layout `docs/` "CLIENT lands with phase 07" → `CLIENT.md` listed; added `gen_art_index.py` Layout entry. - **AGENTS.md fixed:** "the eight screens" (7 listed) → "the seven screens" (+ `dom.js` is the shared builder, not a screen); e2e naming line → notes the non-story guard `test_atlas_loads.py`. - **Decisions:** no code changes were needed; only the two doc fixes above. Docs re-verified by `test_docs_commands`/`test_docs_index` (44 passed) plus the final full gate. - **Next pending phase:** `08_client_prediction_and_touch`.
This commit is contained in:
@@ -257,7 +257,9 @@ def test_shipped_index_html_loads_only_same_origin_assets() -> None:
|
||||
html = (STATIC_DIR / "index.html").read_text(encoding="utf-8")
|
||||
tags = re.findall(r"<(script|link|img)\b", html, re.I)
|
||||
assert len(tags) >= 3, "index.html no longer declares the assets this test guards"
|
||||
assert 'src="/static/js/app.js"' in html
|
||||
# The page's single entry module (the composition root, phase 07 task 03;
|
||||
# `main.js` imports `app.js` and the render/decode modules).
|
||||
assert 'src="/static/js/main.js"' in html
|
||||
assert scan_element_urls(STATIC_DIR / "index.html", html) == []
|
||||
|
||||
|
||||
@@ -321,6 +323,18 @@ def test_a_planted_html_reference_fails_the_scan(
|
||||
'<image href="https://x.example.com/a.png"/></svg>',
|
||||
"absolute URL",
|
||||
),
|
||||
(
|
||||
".svg",
|
||||
'<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">'
|
||||
'<use xlink:href="https://evil.example/x.svg"/></svg>',
|
||||
"absolute URL",
|
||||
),
|
||||
(
|
||||
".svg",
|
||||
'<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">'
|
||||
'<image xlink:href="https://evil.example/x.svg"/></svg>',
|
||||
"absolute URL",
|
||||
),
|
||||
(".html", '<a href="https://external.example.com">out</a>', "absolute URL"),
|
||||
],
|
||||
)
|
||||
@@ -364,6 +378,13 @@ def test_a_planted_hosted_library_token_fails_the_scan(
|
||||
'<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">'
|
||||
'<use xlink:href="#tile"/></svg>',
|
||||
),
|
||||
(
|
||||
".svg",
|
||||
'<svg xmlns="http://www.w3.org/2000/svg"'
|
||||
' xmlns:xml="http://www.w3.org/XML/1998/namespace"'
|
||||
' xmlns:xhtml="http://www.w3.org/1999/xhtml">'
|
||||
'<rect width="16" height="16"/></svg>',
|
||||
),
|
||||
(".js", "// a comment about docs/PROTOCOL.md and a/b/c paths\nexport const ok = 1;\n"),
|
||||
(".js", 'const ws = new WebSocket(`${location.origin}/ws/${code}`);\n'),
|
||||
(".js", 'fetch("/static/api/levels");\n'),
|
||||
|
||||
Reference in New Issue
Block a user