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

2.8 KiB

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