feat(ui): rename nav items — "Sources" becomes "RAG", "Git sources" becomes "Sources"

Owner request (2026-08-28): the two admin-only nav items read like the
same thing, so they are relabeled — the document-catalog link
(#nav-sources, /sources.html) becomes "RAG" and the source-manager link
(#nav-git-sources, /git-sources.html) becomes "Sources".

Phase 48 (48_nav_rename_sources), label-only per the locked decision:
- all six pages (index, sources, git-sources, tuning, document, login):
  the two <a> texts swap; ids, hrefs, hidden defaults, is-active /
  aria-current placement, and nav order (Chat, RAG, Sources, Tuning)
  are byte-unchanged otherwise.
- header.js: comment/docstring label mentions only — the reveal-by-id
  logic is untouched (ship-hidden/reveal contract intact).
- test_git_sources_admin.py / test_mobile_hamburger_nav.py: the two
  suites that asserted the old label text are updated; comment-only
  label fixes in test_shared_header.py / test_nav_consistency.py.
- tests/e2e/test_nav_rename_sources.py: the story E2E (green in
  isolation) — renamed labels + unchanged hrefs/order/markers on all
  six pages, click navigation with the active marker, the anonymous
  ship-hidden contract, and regression guards for the untouched
  controls (#sync-label "Sync sources", viewer #doc-back "Sources").
- All eight surrounding header/nav suites stay green in isolation;
  unit+integration green, app/ coverage 99% (frontend-only change),
  ruff + pyright clean.

Note: per this phase file-level staging, the six page files and
header.js also carry the same-day in-flight owner rework that was
already in the working tree when phase 48 ran (mobile sign-in dropdown
copy, sync button ship-hidden on the Sources page); the label rename
itself is the two-text swap on each page.
This commit is contained in:
2026-08-28 12:33:18 -04:00
parent 03bead092c
commit 872a07cee7
17 changed files with 785 additions and 118 deletions
@@ -0,0 +1,40 @@
# Phase 48 — Nav rename: "Sources" → "RAG", "Git sources" → "Sources"
**Source:** owner request (chat, 2026-08-28) — "The 'Git sources' navbar item should be renamed to 'Sources' and the 'Sources' navbar item should be renamed to 'RAG'."
**Story:** `.agent/user_stories/nav-sources-rag-rename.md`
**Context:** the shared header (phase 19, `frontend/assets/header.js`) ships ONE nav on all six pages — `[Chat, #nav-sources "Sources" → /sources.html, #nav-git-sources "Git sources" → /git-sources.html, #nav-tuning "Tuning"]` — with the two admin-only links ship-hidden and revealed by id once `/api/whoami` says admin (phase 16 contract, phase 34 same-header-everywhere, phase 46 mobile hamburger). Both labels are plain `<a>` text in the six page files; `header.js` toggles only the `hidden` attribute and never reads the label, so the rename is markup-only.
## Objective
Relabel the two admin-only nav items across all six pages — the document-catalog link (`#nav-sources`) becomes **"RAG"** and the source-manager link (`#nav-git-sources`) becomes **"Sources"** — with ids, hrefs, order, the ship-hidden/reveal contract, and every other page control (viewer back button, Sync button, page titles/h1s) unchanged.
## Dependencies
- `34_consistent_navbar` / `19_shared_header` (complete) — the same full header block on all six pages and the `header.js` reveal-by-id contract this phase preserves.
- `35_git_sources_admin` (complete) — the `#nav-git-sources` link (and the `/git-sources.html` page) it labels.
- `46_mobile_hamburger_nav` (complete) — the `LINK_TEXTS` assertion in `test_mobile_hamburger_nav.py` that this phase updates.
## Tasks
1. `01_nav_label_swap.md` — swap the two label texts in all six page files + the label mentions in `header.js`'s docstring/comments (no logic).
2. `02_existing_test_labels.md` — update the two test suites that assert the old label text; leave every other assertion (viewer back button, sync label, h1/title markers) untouched.
3. `03_story_e2e_regression_commit.md` — the story E2E (`test_nav_rename_sources.py`), the regression suites in isolation, ruff + pyright, the one `--no-gpg-sign` commit, and the phase-dir move.
## Testing & Quality
- Unit/integration: no new backend logic (frontend markup only). The existing no-CDN integration test (`tests/integration/test_api.py::test_html_pages_served_locally_no_cdn`) must still pass — the swap touches no tags or origins (the `/git-sources.html` "Git sources" marker keeps matching the page's unchanged `<h1>`).
- Coverage: frontend-only; the `app/` >90% gate is unaffected (unchanged).
- E2E (mandatory, A16): `tests/e2e/test_nav_rename_sources.py` — the story gate, run in isolation.
## Completion Criteria
- [ ] All six pages show **"RAG"** on `#nav-sources` (href `/sources.html`) and **"Sources"** on `#nav-git-sources` (href `/git-sources.html`); nav order and `is-active`/`aria-current` placement unchanged; no id/href/`hidden` change.
- [ ] Anonymous still sees neither link; the admin sees both (contract preserved, proven by the story E2E + `test_shared_header.py`).
- [ ] `uv run pytest` green (the two updated suites included); `uv run pytest --cov=app --cov-report=term-missing` TOTAL unchanged (no backend change).
- [ ] Regression E2E suites green in isolation: `test_nav_consistency.py`, `test_shared_header.py`, `test_mobile_hamburger_nav.py`, `test_git_sources_admin.py`, `test_header_consistency.py`, `test_sources_midstream_bug.py`, `test_smoke.py`.
- [ ] `uv run pytest tests/e2e/test_nav_rename_sources.py -v --no-cov` green in isolation.
- [ ] `uv run ruff check . && uv run pyright` clean (no Python behavior change, but run the gate).
- [ ] UI Structure Check (AGENTS.md rule 5): labeled links, landmarks/contrast/focus unchanged; no CDN (rule 6).
- [ ] One `--no-gpg-sign` commit staging only this phase's files; `.agent/phases/todo/48_nav_rename_sources/` moved to `.agent/phases/complete/`.
## Locked decisions
- **Label-only rename (owner request 2026-08-28)** — exactly the two nav item texts change. Element ids (`#nav-sources`, `#nav-git-sources`), hrefs, nav order, the ship-hidden/reveal contract, `header.js` behavior, and every other label (the document viewer's "Sources" back button, the "Sync sources" button, page `<title>`/`<h1>` — incl. "Knowledge base" and "Git sources") are OUT of scope. If the owner later wants the page titles/h1s to follow the nav labels, that is a follow-up phase, not this one.
- **A11 untouched** — vanilla HTML only, no CDN, no new packages, no new tags.
- **A10 untouched** — no endpoint, auth, or `header.js` logic change; the rename rides the existing reveal-by-id path.
- **No schema / migration** — purely a markup + test-label change.
- **A16 / A17 honoured** — one new story E2E suite + one atomic `--no-gpg-sign` commit.
@@ -0,0 +1,46 @@
# Task 01 — Swap the two nav label texts on all six pages
**Phase:** `48_nav_rename_sources` · **Story:** `.agent/user_stories/nav-sources-rag-rename.md`
## Objective
Rename the nav items themselves: `#nav-sources` ("Sources" → **"RAG"**) and `#nav-git-sources` ("Git sources" → **"Sources"") in the shared header markup of all six pages, plus the label mentions in `header.js`'s doc comments. Markup only — no ids, hrefs, classes, logic, or other text changes.
## Work
On **each** of the six page files — `frontend/index.html`, `frontend/sources.html`, `frontend/git-sources.html`, `frontend/tuning.html`, `frontend/document.html`, `frontend/login.html` — make exactly two text swaps inside `<nav class="app-nav" aria-label="Primary">`:
1. The catalog link — change the visible text only:
```html
<a href="/sources.html" class="nav-link" id="nav-sources" hidden>Sources</a>
```
becomes
```html
<a href="/sources.html" class="nav-link" id="nav-sources" hidden>RAG</a>
```
(On `sources.html` the link carries `class="nav-link is-active" aria-current="page"` — keep those attributes exactly as they are; swap only `Sources` → `RAG`.)
2. The manager link — change the visible text only:
```html
<a href="/git-sources.html" class="nav-link" id="nav-git-sources" hidden>Git sources</a>
```
becomes
```html
<a href="/git-sources.html" class="nav-link" id="nav-git-sources" hidden>Sources</a>
```
(On `git-sources.html` the link carries `class="nav-link is-active" aria-current="page"` — keep them; swap only `Git sources` → `Sources`.)
3. `frontend/assets/header.js` — **comments only** (the reveal logic is id-based and must stay byte-identical): in the module docstring (the nav-link inventory: `"Sources" (#nav-sources, phase 19), "Git sources" (#nav-git-sources, phase 35)`) and in the inline comment above the `navGitSources` reveal, update the quoted labels to the new ones (`"RAG" (#nav-sources)`, `"Sources" (#nav-git-sources)`). No executable line of `header.js` changes.
Rules for all edits:
- Do **not** touch element ids, `href`s, `hidden` defaults, `class` attributes, `aria-current`, indentation, or any other text on the page (page `<title>`, `<h1>`, the viewer's "Sources" back button, the "Sync sources" button label — all stay).
- Do **not** add `is-active` anywhere or move the links — the physical nav order stays Chat, `#nav-sources`, `#nav-git-sources`, `#nav-tuning` (the labels just swap, so it now *reads* Chat, RAG, Sources, Tuning).
- The phase-16/19/35 ship-hidden contract is untouched: the links still ship `hidden` and `header.js` still reveals them by id for admins.
## Testing & Quality
- No new unit/integration logic (frontend markup only).
- Coverage: frontend-only; `app/` coverage unaffected.
- The no-CDN integration test (`tests/integration/test_api.py::test_html_pages_served_locally_no_cdn`) still passes — no tags or origins change; its `("/git-sources.html", "Git sources")` marker still matches that page's unchanged `<h1>Git sources</h1>`.
## Completion Criteria
- [ ] All six pages: `#nav-sources` renders text **RAG** and `#nav-git-sources` renders text **Sources**; ids/hrefs/`hidden`/`is-active` unchanged (a quick `grep -c 'id="nav-sources" hidden>RAG<'` per page returns 1, and `grep -c 'id="nav-git-sources" .*>Sources<'` returns 1).
- [ ] `git diff --stat` shows only the six HTML files + `frontend/assets/header.js` (comment-only) changed.
- [ ] `uv run pytest tests/unit tests/integration -q` green; `uv run ruff check . && uv run pyright` clean.
@@ -0,0 +1,34 @@
# Task 02 — Update the tests that assert the old nav label text
**Phase:** `48_nav_rename_sources` · **Story:** `.agent/user_stories/nav-sources-rag-rename.md`
## Objective
Keep the existing suites honest after the rename: update the (few) assertions and label-describing comments that reference the OLD label texts, without touching any assertion that is about a different control.
## Work
Only these tests assert the old label text (verified by grepping `tests/` for the strings) — update exactly these:
1. `tests/e2e/test_git_sources_admin.py` — `test_admin_nav_link_on_all_five_pages_and_click_navigates`:
- the comment `pointing at the git sources page, labeled "Git sources" — and it` → `labeled "Sources"`;
- `expect(link).to_have_text("Git sources")` → `expect(link).to_have_text("Sources")`.
- This suite has no assertion on `#nav-sources`'s TEXT (it is id-based everywhere) — do not add one here; the story E2E (task 03) owns the label checks.
2. `tests/e2e/test_mobile_hamburger_nav.py` —
- `LINK_TEXTS = ("Chat", "Sources", "Git sources", "Tuning")` → `LINK_TEXTS = ("Chat", "RAG", "Sources", "Tuning")` (DOM order unchanged — only the labels swapped);
- the comment near `test_…` that reads `Chat / Sources / Git sources / Tuning — i.e. the whoami reveal` → `Chat / RAG / Sources / Tuning …`.
3. **Comments-only drift fixes** (no assertion changes) where a suite's docstring/comment quotes the old labels as the link's identity: `tests/e2e/test_shared_header.py` (module docstring line `"the 'Sources' nav link (#nav-sources)"` → `'RAG'`), `tests/e2e/test_nav_consistency.py` (the comment `the Git sources link joined in phase 35` → mention the relabeled link), `tests/e2e/test_header_consistency.py` if it quotes the labels. Where a comment also explains the phase-35 origin, keep that history and only fix the quoted label.
**Explicitly DO NOT touch** (different controls / different text — they stay green and must stay):
- `tests/e2e/test_nav_consistency.py` lines asserting the document viewer's **back button** `span` text "Sources" / "Chat" — that is the viewer's back link (`document.js`), not the nav.
- `tests/e2e/test_document_back_navigation.py` — same back button.
- `tests/unit/test_document_viewer.py` — the `'Sources' in js` check on the viewer's back-link label.
- `tests/e2e/test_nav_consistency.py` `expect(page.locator("#sync-label")).to_have_text("Sync sources")` — the Sync button label is unchanged.
- `tests/integration/test_api.py::test_html_pages_served_locally_no_cdn` — the `("/git-sources.html", "Git sources")` marker matches the page's unchanged `<h1>`; leave it.
## Testing & Quality
- No new logic — assertion-text updates only.
- Coverage: frontend/test-only; `app/` coverage unaffected.
## Completion Criteria
- [ ] `grep -rn '"Git sources"\|Git sources' tests/e2e/*.py` shows no remaining assertion expecting the old nav label (only phase-history comments, if any, are acceptable — and none should quote it as the current label).
- [ ] `uv run pytest tests/unit tests/integration -q` green; `uv run ruff check . && uv run pyright` clean.
- [ ] The two updated E2E suites pass in isolation: `uv run pytest tests/e2e/test_git_sources_admin.py -v --no-cov` and `uv run pytest tests/e2e/test_mobile_hamburger_nav.py -v --no-cov` (DB up).
@@ -0,0 +1,33 @@
# Task 03 — Story E2E, regression suites, commit, phase move
**Phase:** `48_nav_rename_sources` · **Story:** `.agent/user_stories/nav-sources-rag-rename.md`
## Objective
Prove the renamed nav end to end with the story's dedicated Playwright suite (run in isolation per A16), confirm the surrounding header suites stay green, and land the one atomic `--no-gpg-sign` commit.
## Work
1. **Create `tests/e2e/test_nav_rename_sources.py`** (one story, one file — follow the conventions of `test_git_sources_admin.py` / `test_nav_consistency.py`: the shared `app_url` / `db_ready` fixtures, the `login(page, app_url, next=…)` helper, desktop viewport 1280×800, `expect` sync-API assertions, a module docstring stating the story + the rename). The six pages under test are `/` (chat), `/sources.html`, `/git-sources.html`, `/tuning.html`, `/login.html`, and `/document.html` (viewer — seed one document row first, reusing the seeding pattern `test_nav_consistency.py` uses for its viewer pass, and wait for `#doc-title` to settle as that file does).
Test cases:
- **admin_labels_on_all_six_pages** — signed-in admin visits each of the six pages; on each: `#nav-sources` is visible, has text exactly `RAG` and href `/sources.html`; `#nav-git-sources` is visible, has text exactly `Sources` and href `/git-sources.html`; the nav DOM order is Chat, RAG, Sources, Tuning (assert the `.app-nav a` text sequence); the current page's link carries `is-active`/`aria-current="page"` (and only it does).
- **click_navigates_with_marker** — from the chat page: click `#nav-sources` (label "RAG") → lands on `/sources.html` where `#nav-sources` is the active link; from the chat page: click `#nav-git-sources` (label "Sources") → lands on `/git-sources.html` where `#nav-git-sources` is the active link.
- **anonymous_sees_neither** — anonymous (no login): on `/` and `/login.html` both `#nav-sources` and `#nav-git-sources` are present in the DOM but hidden (ship-hidden contract unchanged), `#sign-in-link` visible.
- **untouched_controls_stay** — the rename did not leak: on `/sources.html` the Sync button still reads "Sync sources" (`#sync-label`), and on the settled viewer page the back button's span still reads "Sources" (the viewer back link is a different control — regression guard for task 02's do-not-touch list).
2. **Run the regression suites in isolation** (DB up, `--no-cov`), fixing nothing unless a test genuinely asserted a renamed nav label (if one does, update it as in task 02 and note it in the commit message):
`test_nav_consistency.py`, `test_shared_header.py`, `test_mobile_hamburger_nav.py`, `test_git_sources_admin.py`, `test_header_consistency.py`, `test_sources_midstream_bug.py`, `test_smoke.py`, `test_tuning_nav_link.py`.
3. **Full gates**: `uv run pytest` (unit + integration) green; `uv run pytest --cov=app --cov-report=term-missing` with TOTAL unchanged from the pre-phase baseline (frontend-only change); `uv run ruff check . && uv run pyright` clean.
4. **UI Structure Check (AGENTS.md rule 5)** — the relabeled links are still labeled `<a>`s inside the semantic `<nav aria-label="Primary">`; landmarks/contrast/focus-visible untouched (label text only); no CDN (rule 6 — the no-CDN integration test covers it).
5. **Commit** — one atomic commit staging exactly this phase's files (the six HTML files, `frontend/assets/header.js`, `tests/e2e/test_git_sources_admin.py`, `tests/e2e/test_mobile_hamburger_nav.py`, the comment-only test files from task 02, and the new `tests/e2e/test_nav_rename_sources.py`):
`feat(ui): rename nav items — "Sources" becomes "RAG", "Git sources" becomes "Sources"` with a body citing the owner request (2026-08-28) + phase 48. `git commit --no-gpg-sign` (repo also has `commit.gpgsign=false`).
6. **Move the phase directory**: `mv .agent/phases/todo/48_nav_rename_sources .agent/phases/complete/` and `git add -f .agent/phases/complete/48_nav_rename_sources` into the SAME commit as step 5 (`.agent/` is gitignored by design — AGENTS.md rule 8), plus the story file `git add -f .agent/user_stories/nav-sources-rag-rename.md`.
## Testing & Quality
- Story E2E: `tests/e2e/test_nav_rename_sources.py` green **in isolation**.
- Coverage: frontend-only; `app/` >90% gate unaffected (TOTAL unchanged).
## Completion Criteria
- [ ] `uv run pytest tests/e2e/test_nav_rename_sources.py -v --no-cov` green in isolation (DB up).
- [ ] All eight regression suites green in isolation (commands above).
- [ ] `uv run pytest` green; `uv run pytest --cov=app --cov-report=term-missing` TOTAL unchanged; `uv run ruff check . && uv run pyright` clean.
- [ ] One `--no-gpg-sign` commit containing the code/test files, the story file, and the moved phase directory; `.agent/phases/todo/` no longer lists 48.
- [ ] No behavior change in completed phases (the suites above are the proof).
@@ -0,0 +1,49 @@
# Story: Nav rename — "Sources" becomes "RAG", "Git sources" becomes "Sources"
**Phase:** `48_nav_rename_sources` · **E2E:** `tests/e2e/test_nav_rename_sources.py`
## Narrative
As **the admin (owner)**, the two admin-only nav items are confusing:
"Sources" (the document catalog) and "Git sources" (the source manager)
read like the same thing. I want the navbar to say **"RAG"** for the
document catalog page and **"Sources"** for the source manager page, so
the labels say what each page *is*: RAG = the indexed knowledge base you
chat against; Sources = where that knowledge comes from.
- **Given** I am signed in as admin, on any of the six pages (chat,
sources, git-sources, tuning, document viewer, login)
- **When** I look at the primary nav
- **Then** the catalog link (`#nav-sources`, → `/sources.html`) is
labeled **"RAG"** and the manager link (`#nav-git-sources`, →
`/git-sources.html`) is labeled **"Sources"** — same ids, same hrefs,
same order, same admin-only ship-hidden/reveal contract as before.
## Acceptance criteria
1. All six pages (`frontend/index.html`, `sources.html`,
`git-sources.html`, `tuning.html`, `document.html`, `login.html`)
carry the swapped label text on `#nav-sources` ("RAG") and
`#nav-git-sources` ("Sources"). Element ids, `href`s, `hidden`
defaults, `is-active`/`aria-current` markers, and the nav order
(Chat, RAG, Sources, Tuning) are unchanged — this is a label-only
rename, and `header.js` logic (reveal by id) is untouched.
2. Anonymous visitors still see neither link (ship-hidden contract
unchanged); the admin sees both, as before.
3. Existing test assertions on the old label text are updated
(`test_git_sources_admin.py` nav-link text,
`test_mobile_hamburger_nav.py` `LINK_TEXTS`); assertions that are
NOT about these two nav items stay untouched (the document viewer's
"Sources" back button, the "Sync sources" button label, the
`/git-sources.html` page-`<h1>`/`<title>` marker).
4. Story E2E `tests/e2e/test_nav_rename_sources.py` proves: admin sees
"RAG" (→ `/sources.html`) and "Sources" (→ `/git-sources.html`) on
all six pages; clicking each lands on the right page with
`is-active`; anonymous sees both hidden.
5. Frontend-only: unit + integration green, `app/` coverage
unaffected, ruff + pyright clean, no CDN, one `--no-gpg-sign` commit.
## Playwright Mapping Rule
`tests/e2e/test_nav_rename_sources.py` — one story, one file, run in
isolation: the two relabeled nav items' full lifecycle (labels, hrefs,
admin reveal, anonymous hide, click-navigation with the current-page
marker) on the shared header, desktop viewport.