# Story: Configurable app name **Phase:** `39_configurable_brand` · **E2E:** `tests/e2e/test_configurable_brand.py` ## Narrative As **the owner**, the app is "Brain of Reese" because that's what I named it — but the name is hard-coded across every template and JS string. I want to be able to **rename the app with one env var** (`BOR_APP_NAME`) so every page title, the header brand, the status labels, and the aria text carry the new name without touching code. - **Given** the app is running with `BOR_APP_NAME="Brain of Testy"` - **When** I open any page (chat, sources, tuning, login, document viewer) - **Then** the title, the header brand, the status labels ("Brain of Testy is thinking"), the empty-state greeting, and the document-viewer titles all say "Brain of Testy" — and with the variable unset the app is byte-identical to today ("Brain of Reese"). ## Acceptance criteria 1. `GET /api/config` (public, stateless) → `{app_name, version}` from settings — exactly those two keys (no secrets, no other settings); unit + integration tested. 2. `frontend/assets/brand.js` (classic script, loaded first on all five pages): sets `window.BOR_BRAND` synchronously (default "Brain of Reese"), fetches `/api/config` (`cache: "no-store"`), and applies the name — `document.title` (global replace of the literal), every `.brand-text` (bold split: a name starting "Brain of " bolds the remainder, else plain; the name is HTML-escaped), every text node and the `aria-label` / `placeholder` / meta-`content` attributes containing the literal; a fetch failure falls back to the default (console.warn, the page never breaks). 3. The JS hard-codes move off the literal: `app.js` (status labels, the typing label, the elapsed-hint aria) + `document.js` (page titles) read `window.BOR_BRAND` (default "Brain of Reese" if unset). 4. `Containerfile`: an esbuild line for brand.js next to the others. 5. The story E2E boots its **own** app instance with `BOR_APP_NAME` overridden (distinct port; the shared conftest server keeps the default name so the existing title assertions stay green) and asserts title/brand/greeting/aria/label. 6. `.env.example` + README document `BOR_APP_NAME` (what it affects, the default, the bold-split rule). 7. Unit + integration green, `app/` coverage >90%, story E2E green in isolation, ruff + pyright clean, one `--no-gpg-sign` commit. ## Playwright Mapping Rule `tests/e2e/test_configurable_brand.py` — one story, one file, run in isolation: the overridden-name instance shows the new brand in the title, the header `.brand-text` (with the bold split), the empty-state greeting, the messages aria-label, and the chat status label; the default-name shared server still renders "Brain of Reese" (the no-op regression).