fix(header): ship the tuning toggle hidden — no anonymous flash

#steering-toggle (the header 'Tuning' button) shipped visible in all
six pages and was only removed after /api/whoami resolved, so
anonymous visitors saw it flash for the whole round-trip (TODO.md L3).
It now ships hidden on every page and initSharedHeader unhides it only
for admin — the same ship-hidden / reveal-for-admin contract as the
admin-only nav links; the anonymous end-state (removed from the DOM,
phase-16 'absent, not hidden') is unchanged.

Adds the story E2E suite (MutationObserver proves zero visible frames
for anonymous on every page, admin reveal + panel + count badge,
nav-contract regression) and the source-level unit pins. Also fixes
test_steering.py's BASE_SCRIPT_COUNT (2 → 3: brand.js + markdown.js +
app.js, since phase 39).
This commit is contained in:
2026-08-27 22:38:58 -04:00
parent 02c76ad328
commit 6f9e033117
11 changed files with 429 additions and 17 deletions
+17 -9
View File
@@ -26,10 +26,15 @@
* list (newest-first, textContent-rendered, per-note delete, count
* badge, the #steering-announcer live region) — so the toggle can
* sit in every page's header with zero page-script duplication.
* refreshSteering() / announceSteering() are exported for the chat
* page's per-bubble Tune form (which stays in app.js); anonymous
* visitors get the phase-16 "absent, not hidden" treatment (toggle
* + panel removed from the DOM, /api/steering never fetched);
* The toggle SHIPS hidden in every page (phase 40, 2026-08-27,
* TODO.md L3 — the exact ship-hidden / reveal-for-admin contract
* the admin-only nav links use: anonymous never sees it for a
* single frame) and initSharedHeader unhides it only when whoami
* says admin. refreshSteering() / announceSteering() are exported
* for the chat page's per-bubble Tune form (which stays in app.js);
* anonymous visitors get the phase-16 "absent, not hidden"
* treatment (toggle + panel removed from the DOM, /api/steering
* never fetched);
* • the Sync sources state machine (phase 32, moved here from
* sources.js in phase 34 task 02) — the §7.4 never-stale lifecycle
* for #sync-btn (idle → running → success | failed): admin-only
@@ -124,12 +129,15 @@ export async function initSharedHeader() {
// same cached whoami (anonymous users never see it).
if (syncBtn) syncBtn.hidden = !admin;
// Phase 34: the steering controls (phase 15) are module-owned. Admin:
// refresh the list so the count badge is right before the panel is
// ever opened (fire-and-forget, as the chat page did before the move).
// Anonymous: the toggle + panel are REMOVED from the DOM entirely —
// the phase-16 contract says "absent", not just hidden — and
// /api/steering is never fetched.
// unhide the toggle — it SHIPS hidden (phase 40, 2026-08-27, TODO.md
// L3, the same ship-hidden / reveal-for-admin contract as the
// admin-only nav links) — then refresh the list so the count badge is
// right before the panel is ever opened (fire-and-forget, as the chat
// page did before the move). Anonymous: the toggle + panel are REMOVED
// from the DOM entirely — the phase-16 contract says "absent", not
// just hidden — and /api/steering is never fetched.
if (admin) {
if (steeringToggle) steeringToggle.hidden = false;
if (steeringPanel) refreshSteering();
} else {
steeringToggle?.remove();