Guardrails so this branch's fix classes can't regress silently:
- test_web_smoke.py (24 tests): boots the pages blueprint with the same
dual registration app.py uses and asserts every page/partial returns 200
with its load-bearing markers (nav wiring, getting-started card, advanced
section, rotation order card, per-mode duration inputs), the /v3 legacy
alias serves everything, all critical static assets (incl. vendored
fontawesome/codemirror, PWA manifest/icons) are served, durations group
per plugin with the leftover bucket, and the advanced-hardware section
really contains the tuning fields. Would have caught this session's
unreachable-durations-page and orphaned-tab bugs instantly.
- test_web_static_audit.py (3 tests): (1) every responsive utility class
referenced in templates is actually defined in app.css - the
silently-no-op class bug that left the header search box invisible at
every width; (2) every url_for('static', ...) reference points to a real
file; (3) any JS file calling the debugLog global declares it in a
/* global */ header.
All 40 web tests pass (24 + 3 new, 13 existing) under pytest + Flask.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KEZK1P1Q1fu5pcuVrkrCFZ