feat(ui): expose Global Tuning from Chat + Sources headers (admin-only Tuning nav link)

This commit is contained in:
2026-08-25 14:51:16 -04:00
parent 3d044f33a1
commit 3841bd5a30
7 changed files with 366 additions and 2 deletions
@@ -0,0 +1,40 @@
# Phase 29 — Global Tuning nav link
**Source:** `.agent/phases/complete/27_global_tuning` — the `/tuning.html` page and its create/edit/delete machinery shipped, but the "Tuning" header link that was supposed to *expose* the page (`27` task 04: *"Expose the tuning page via an admin-only header link"`) only exists on `tuning.html` itself. It is not present on the Chat or Sources pages, so there is no way to reach `/tuning.html` from anywhere except typing the URL.
**Story:** `.agent/user_stories/global-tuning.md`
**Context:** The shared header (phase 19) renders `[Chat, Sources — admin only]` in `<nav class="app-nav">` on the Chat and Sources pages. `frontend/assets/header.js` already contains logic to reveal an admin-only `#nav-tuning` link for admins (lines 69–70) — but that element is never in `index.html`/`sources.html` markup, so the reveal is a no-op. This phase adds the missing markup so the link the JS already handles actually exists.
## Objective
Make the Global Tuning page reachable by adding the admin-only **"Tuning"** nav link to the shared headers of the **Chat** (`index.html`) and **Sources** (`sources.html`) pages — hidden by default, revealed for admins by the existing `header.js` whoami gate (the exact same pattern as the Sources link). No backend, schema, or API change.
## Dependencies
- `19_shared_header` (complete) — the `<nav class="app-nav">` contract, the shared header markup/ids, and the 64px header-height rule on the Chat and Sources pages.
- `16_admin_auth` (complete) — the `/api/whoami` gate + `header.js` `fetchIsAdmin()` that reveals admin-only nav links.
- `27_global_tuning` (complete) — the `/tuning.html` page and `PUT/POST/DELETE /api/steering` endpoints that this link points to.
## Tasks
1. `01_tuning_links_markup.md` — add the admin-only `#nav-tuning` link to the Chat (`index.html`) and Sources (`sources.html`) headers, hidden by default.
2. `02_tuning_nav_e2e_and_regression.md` — add the story E2E suite (`test_tuning_nav_link.py`) + verify no regressions in the header/shared-header/no-CDN suites and commit.
## Testing & Quality
- Unit/integration: no new backend logic (frontend-only markup). The existing no-CDN integration test (`tests/integration/test_api.py::test_index_html_served_locally`) must still pass — the new link is same-origin `<a>`, no new tags.
- Coverage: frontend-only; the `app/` >90% gate is unaffected (unchanged).
- E2E (mandatory, A16): `tests/e2e/test_tuning_nav_link.py` — the story gate, run in isolation.
## Completion Criteria
- [ ] On the **Chat** page (`/`), a signed-in admin sees a **"Tuning"** link (next to Chat/Sources) that navigates to `/tuning.html`; an anonymous visitor does **not** see it (ships `hidden`, `header.js` reveals only for admin).
- [ ] Same on the **Sources** page (`/sources.html`).
- [ ] The link is absent from the anonymous DOM-reveal (verified via `test_anonymous` behavior); header markup stays valid and semantic.
- [ ] `uv run pytest` green; `uv run pytest --cov=app --cov-report=term-missing` TOTAL unchanged (no backend change).
- [ ] Existing header suites stay green in isolation: `test_header_consistency.py` (64px height + nav structure unchanged), `test_shared_header.py`, `test_smoke.py`, `test_global_tuning.py`.
- [ ] `uv run pytest tests/e2e/test_tuning_nav_link.py -v --no-cov` green in isolation.
- [ ] `uv run ruff check . && uv run pyright` clean (no Python change, but run the gate).
- [ ] UI Structure Check (AGENTS.md rule 5): the link is a labeled, focus-visible `<a>`; header landmarks/contrast unchanged; no CDN.
- [ ] One `--no-gpg-sign` commit staging only this phase's files; `.agent/phases/todo/29_tuning_nav_link/` moved to `.agent/phases/complete/`.
## Locked decisions
- **A11 untouched** — vanilla HTML/CSS/JS, no CDN, no new packages; the link is a same-origin `<a href="/tuning.html">`.
- **A10 untouched** — no new endpoint or auth surface; the link reuses the existing `#nav-sources` reveal path (`header.js` already gates `#nav-tuning` on `fetchIsAdmin()`).
- **No schema / migration** — purely a markup addition.
- **A16 / A17 honoured** — one new story E2E suite + one atomic `--no-gpg-sign` commit.
- **Scope boundary** — only the Chat and Sources pages (the two that share the standard `app-nav` with the Sources link). The viewer page (`document.html`) uses a different `.doc-header-actions` header variant and the login page is the auth gate; both stay out of scope for this fix.