Files
ducoterra dbf2af26c6 refactor(agents): migrate .agent/ planning tree to .agents/
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.
2026-09-05 10:57:07 -04:00

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).