Standardize on the .agents/ directory (shared with project skills): phases/, user_stories/, reports/, screenshots/, validate.sh, and phase-sessions/ + pipeline.log all move to .agents/ (git mv preserves history; runtime artifacts move alongside). Updates every reference in AGENTS.md, README.md, .gitignore, app docstrings, and test story headers. Historical KB content in data/ and the runtime pipeline.log transcript are left untouched.
52 lines
2.8 KiB
Markdown
52 lines
2.8 KiB
Markdown
# 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).
|