Converts the 9 TODO items into an executable phase roadmap (Protocol B, appended after phase 39): - 40 tuning toggle anonymous flash (TODO L3) - 41 sync fail-fast + modal when a model is down (TODO L4) - 42 no reply autoscroll (TODO L5) - 43 thinking scroll back — user scroll + gated autoscroll (TODO L7) - 44 markdown tables (TODO L6) - 45 agent unlimited tool calls behind BOR_AGENT_MAX_ROUNDS (TODO L8) - 46 mobile hamburger nav (TODO L9) - 47 quadlet + jinja import formats, A9 revision (TODO L10–L11) Each phase carries a user story, a dedicated Playwright E2E suite plan, and owner-locked decisions (R1 A9 format extension, R2 phase-37 budget revision, A1–A5 scope decisions) confirmed 2026-08-27. Also records the completed phases 30–39 todo/ -> complete/ moves that were pending in the working tree. TODO.md is cleared (items now live in .agent/phases/todo/).
88 lines
4.3 KiB
Markdown
88 lines
4.3 KiB
Markdown
# Story: Tuning toggle anonymous flash
|
|
|
|
**Phase:** `40_tuning_toggle_flash` · **Source:** `TODO.md` L3 ·
|
|
**E2E:** `tests/e2e/test_tuning_toggle_flash.py`
|
|
|
|
## Bug report (verbatim, `TODO.md` L3)
|
|
|
|
> "Loading the page briefly shows the 'Tuning' button in the header even
|
|
> when the user isn't authenticated. Only show that if the user is
|
|
> authenticated."
|
|
|
|
## Narrative
|
|
|
|
As **an anonymous visitor**, the header must never show admin-only
|
|
controls — not even for a frame. Today the tuning-notes toggle
|
|
(`#steering-toggle`, the header button labeled **"Tuning"**) ships
|
|
*visible* in all six pages' markup and is only removed from the DOM after
|
|
`/api/whoami` resolves — so every anonymous page load flashes the button
|
|
for the length of the whoami round-trip. The admin-only *nav links*
|
|
(`#nav-sources`, `#nav-git-sources`, `#nav-tuning`) already ship `hidden`
|
|
(phase-19 "absent, not hidden" contract) and are not the issue.
|
|
|
|
- **Given** an anonymous visitor loads any page
|
|
- **When** the page renders (before `/api/whoami` resolves)
|
|
- **Then** no "Tuning" control is ever visible — not for a single frame.
|
|
|
|
- **Given** a signed-in admin loads any page
|
|
- **When** whoami resolves
|
|
- **Then** the toggle is revealed (and the note list refreshes, as today).
|
|
|
|
## Acceptance criteria
|
|
1. Anonymous load of **every** page: `#steering-toggle` is never
|
|
attached-visible — a MutationObserver installed via `addInitScript`
|
|
records zero visible frames of the toggle from first paint to settled
|
|
state; after load the toggle is absent from the DOM (the existing
|
|
remove-from-DOM behavior).
|
|
2. Admin load: the toggle is visible after whoami, `aria-expanded`
|
|
works, the count badge refreshes — identical to today's admin
|
|
behavior (phase 15/34 contract).
|
|
3. No-JS visitors: the toggle is hidden (the control is JS-gated by
|
|
design — whoami decides).
|
|
4. No regression to the shared-header contract (phase 19/34): nav links
|
|
ship hidden, sign-in/sign-out pair, sync button, new-chat binding
|
|
unchanged.
|
|
|
|
## Owner-confirmed (2026-08-27, roadmap A — confirmed with the
|
|
conversion interview)
|
|
1. **The flashing control is the steering toggle**, not the admin
|
|
"Tuning" nav link (which already ships `hidden`) — confirmed by code
|
|
inspection: `#steering-toggle` ships visible in all six pages
|
|
(`index.html`, `sources.html`, `document.html`, `git-sources.html`,
|
|
`login.html`, `tuning.html`) and is removed post-whoami.
|
|
2. **Fix = ship `hidden`, reveal for admin, keep anonymous removal** —
|
|
the same ship-hidden / reveal-for-admin contract the admin-only nav
|
|
links already use; anonymous still gets "absent, not hidden".
|
|
|
|
## UI Visualization & Structure
|
|
- **The whole functional change is one attribute + one JS line:**
|
|
- `#steering-toggle` gains `hidden` in all six pages' header markup
|
|
(the button element only — the `#steering-panel` region already ships
|
|
`hidden`).
|
|
- `frontend/assets/header.js` `initSharedHeader()`: in the admin branch,
|
|
unhide the toggle (`steeringToggle.hidden = false`) before
|
|
`refreshSteering()`; the anonymous branch (`steeringToggle?.remove()`)
|
|
is unchanged.
|
|
- **Non-goals:** no change to the nav links, the panel, the steering
|
|
API, or any other shared-header control.
|
|
|
|
## Playwright Mapping Rule
|
|
**Test Scenario → `tests/e2e/test_tuning_toggle_flash.py`** (mock LLM;
|
|
DB up):
|
|
1. `test_anonymous_never_sees_toggle` — `addInitScript` a MutationObserver
|
|
that records every frame in which `#steering-toggle` exists in the
|
|
DOM and is not `[hidden]`; load `/` anonymously; after load assert the
|
|
observer recorded **zero** such frames, and the toggle is absent from
|
|
the DOM (removed, per the phase-16 contract).
|
|
2. `test_anonymous_other_pages_never_flash` — same observer assertion on
|
|
`/sources.html`, `/tuning.html`, `/login.html` (the page set the
|
|
contract must hold on).
|
|
3. `test_admin_toggle_revealed_and_working` — login via
|
|
`e2e.auth_helpers.login`; reload `/`; the toggle is visible
|
|
(`hidden` removed), clicking opens `#steering-panel`
|
|
(`aria-expanded="true"`), and the count badge matches the panel.
|
|
4. `test_nav_contract_regression` (phase 19/34) — anonymous: nav links
|
|
`#nav-sources` / `#nav-git-sources` / `#nav-tuning` stay hidden and
|
|
absent from the visible header; admin: they are revealed — the
|
|
ship-hidden contract this phase relies on is intact.
|