feat(demo): dogfood dj-navigate + dj-hook with a playwright regression guard (#1742)#1744
Conversation
…ght guard (#1742) Adds an in-house dogfood of the v1.0.2 navigation arc so a future regression red-bars CI internally instead of surfacing downstream: - Two plain djust.LiveView pages (NavDemoPageAView / NavDemoPageBView) linked by dj-navigate. The route map auto-derives from the URLconf (#1733) and auto-emits via {% djust_client_config %} — NO live_session(), get_route_map_script(), or context processor needed. - A DemoWidget dj-hook (canvas, dj-update="ignore") registered once in the persistent shell, following the verified 19-hooks.js contract (DjustHooks registry, mounted()/destroyed(), this.el). Mounts on Page A and re-mounts on the SPA patch-inserted Page B widget (#1738). - tests/playwright/test_nav_hooks.py asserts: SPA nav (window sentinel survives + pathname changes, no full reload — #1733); no first-hydration wholesale [dj-view] child replacement via MutationObserver (#1737); hook mounted() fires on initial load AND survives SPA nav (#1738). - Wires the new test into the playwright-tests job in test.yml. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Stage 11 Adversarial Review — PR #1744 (feat #1742: dogfood dj-navigate + dj-hook, playwright guard)Verdict: 🟢 APPROVE. The guard's three assertions are non-tautological — empirically gate-off-proven for #1733 and #1738, and code-traced + functionally-probed for #1737. No framework runtime touched. demo-checks clean, all CI green. Two 🟡 hardening notes (neither blocking). Reviewed at branch HEAD Pre-review gates
Per-assertion non-tautology verdict (the load-bearing deliverable)I ran the demo on :8002 (chromium available locally) and did real gate-off tests. #1733 SPA-nav sentinel — 🟢 NON-TAUTOLOGICAL (empirically proven). Assertion FAILED exactly as designed ( #1738 hook-survives-nav — 🟢 NON-TAUTOLOGICAL (empirically proven; count check is load-bearing). #1737 no-hydration-flash MutationObserver — 🟢 NON-TAUTOLOGICAL for the real symptom (timing analyzed + functionally probed). (This was the subtle one.)
Flakiness — 🟡 low risk, non-blockingRan 3 consecutive clean passes locally (3/3 PASS). Navigation uses a proper Other findings
Empirical results summary
🤖 Generated with Claude Code |
Retrospective — PR #1744 (pipeline-drain v1.0.2-3)Task: v1.0.2-3 — #1742 (Action Tracker #290): dogfood dj-navigate + a client-hook in the demo with a playwright regression guard. Quality: 5/5The guard is empirically non-tautological across all 3 invariants, and the dogfood proved #1733's zero-wiring claim in-house. What went well
What didn't / follow-up
Process findingA regression guard's worth = its non-tautology. The reviewer didn't just read the assertions; it gate-off-tested each (href-swap, forced-flash) to prove each would FAIL if the regression returned. For the subtle #1737 timing assertion, it verified the observer is installed early enough to see the real (async WS-mount) flash window. That empirical-canary discipline (#252) is what separates a guard from green-theater. Verified
|
Summary
The entire v1.0.2 navigation arc (#1733 zero-wiring route map, #1737 SSR→hydration flash parity, #1738
DjustHooks/dj-hook) was driven by a downstream consumer becauseexamples/demo_projectnever exercised these paths end-to-end. This PR adds an in-house dogfood flow + a playwright regression guard so a future regression in nav/hydration/hooks red-bars CI internally.Closes #1742.
What was added
New views/templates (reused nothing — 2 new minimal LiveViews):
NavDemoPageAView/NavDemoPageBView(plaindjust.LiveViewsubclasses) at/demos/nav-a/and/demos/nav-b/, linked bydj-navigate.demos/nav_demo_base.htmlregisters theDemoWidgetdj-hook once in the persistent shell;nav_demo_a.html/nav_demo_b.htmlcarry a<canvas dj-hook="DemoWidget" dj-update="ignore">.The #1733 dogfood (verified locally): the SPA cross-view flow needed NO
live_session(), NOget_route_map_script(), and NO context processor. The route map auto-derives from the URLconf and auto-emits via{% djust_client_config %}(already in the demo base<head>). Confirmedwindow.djust._routeMapcontained both/demos/nav-a/→NavDemoPageAViewand/demos/nav-b/→NavDemoPageBViewon a live server.The #1738 dj-hook API (verified against
python/djust/static/djust/src/19-hooks.js):window.DjustHooks.DemoWidget = { mounted(), destroyed() },this.elis the DOM element, registry mergeswindow.DjustHooks.dj-update="ignore"keeps VDOM patches off the canvas.The 3 playwright assertions (
tests/playwright/test_nav_hooks.py)window.__nav_sentinelset on Page A survives thedj-navigateclick to B ANDlocation.pathnamechanges to/demos/nav-b/. (A full reload would wipe the sentinel.)MutationObserveron the[dj-view]root records zero direct-child remove/re-add during first-load hydration.mounted()marker is set on initial load AND a freshmounted()fires on the SPA patch-inserted Page B widget (mount count increments).Local dogfood-pass evidence (#1060)
Ran the demo server exactly as the CI playwright job does (uvicorn
demo_project.asgi:applicationon :8002) and ran the new test against it:Gate-off self-test (#1468): forcing a full reload instead of the SPA nav makes the test FAIL with exit 1 (
sentinel after: None) — the #1733 assertion is non-tautological.GET /demos/nav-a/andGET /demos/nav-b/both return 200.manage.py checkon the demo is clean for the new files (no new T012/T015/T016/V0xx; T016 stays silent because the URLconf-derived route map is non-empty).CI wiring
Added
.venv/bin/python tests/playwright/test_nav_hooks.pyto theplaywright-testsjob in.github/workflows/test.yml, after thetest_draft_mode.pyline.Two-commit shape
feat(demo): ...— demo views/templates/urls + hook JS + playwright test + test.yml wiring (no CHANGELOG).docs(changelog): ...— CHANGELOG only.🤖 Generated with Claude Code